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

Compare commits

..

72 Commits

Author SHA1 Message Date
d2c299301a Update CHANGELOG.md 2023-12-24 21:36:37 +06:00
25fb6b2b46 Update gradle-wrapper.properties 2023-12-24 21:30:15 +06:00
5b5de5253c Update libs.versions.toml 2023-12-24 21:29:08 +06:00
9a7dd6ec9a upgrades 2023-12-24 19:25:41 +06:00
4085f721be update dependencies 2023-12-24 19:12:02 +06:00
ffc915cae1 start 9.4.3 2023-12-24 18:56:27 +06:00
a6c90b3df5 Merge pull request #810 from InsanusMokrassar/9.4.2
9.4.2
2023-12-10 14:10:59 +06:00
a6d9fa6ce3 update uuid 2023-12-10 13:43:43 +06:00
7fd2442f8b add inmonexus user/password 2023-12-10 13:39:59 +06:00
8b37ecea9e remove publishing to gitea 2023-12-10 13:39:10 +06:00
35fc9f60df replace gitea packages with nexus 2023-12-10 13:37:35 +06:00
68e89dc1ad Update build.gradle 2023-12-10 02:18:25 +06:00
129fb31b74 update dependencies 2023-12-08 16:13:02 +06:00
a2c353ca41 start 9.4.2 2023-12-08 16:10:48 +06:00
cb74abfce5 Merge pull request #806 from InsanusMokrassar/9.4.1
9.4.1
2023-11-26 13:43:38 +06:00
ca7314923e replace warning about two bots from LongPolling to DefaultKtorRequestsExecutor 2023-11-26 13:37:49 +06:00
74f625a53a start 9.4.1 2023-11-26 13:32:56 +06:00
3a5fed3dd9 Merge pull request #803 from InsanusMokrassar/9.4.0
9.4.0
2023-11-26 04:30:56 +06:00
6158143220 Update greetings.yml 2023-11-26 04:30:18 +06:00
f8182ddb85 Revert "pinned message in ExtendedOtherPartiesChat"
This reverts commit b7c3f9f607.
2023-11-26 03:20:05 +06:00
b7c3f9f607 pinned message in ExtendedOtherPartiesChat 2023-11-26 02:06:30 +06:00
8763ea23fa cratch fix of build fails 2023-11-25 17:54:40 +06:00
b412e7b3b7 improvement of DefaultKTgBotAPIKSLog 2023-11-25 12:56:00 +06:00
98e5d182bb update dependencies 2023-11-25 00:28:51 +06:00
ffc0f5abb7 add improvements in logging functionality 2023-11-23 20:00:52 +06:00
816cf00dac basically add logging 2023-11-23 12:47:58 +06:00
e34bc7453e update dependencies 2023-11-23 12:06:46 +06:00
3b2ccbf33b start 9.4.0 2023-11-23 12:00:32 +06:00
6ecfbdf56d improvements in publish.gradle 2023-11-05 13:22:32 +06:00
b49e1c50f5 remove temporal publish.gradle file 2023-11-05 13:09:54 +06:00
d7389dfcfe potential fix of publish.gradle 2023-11-05 13:09:29 +06:00
cf5cee3e53 Merge pull request #798 from InsanusMokrassar/9.3.0
9.3.0
2023-11-05 12:46:40 +06:00
8593263506 fill changelog 2023-11-05 12:36:36 +06:00
4422a4d09b update workflows jdk 2023-11-05 00:48:09 +06:00
9ecb50e377 update dependencies and gradle wrapper 2023-11-04 21:13:14 +06:00
8a4b40c6ec Update libs.versions.toml 2023-11-01 12:45:24 +06:00
bba667db30 Merge branch 'master' into 9.3.0 2023-10-31 18:45:52 +06:00
ca0d256bbb update dependencies 2023-10-31 18:43:51 +06:00
dc2fd07632 Merge pull request #797 from InsanusMokrassar/9.2.4
9.2.4
2023-10-25 19:59:05 +06:00
32fe008eef update docs remote url 2023-10-25 14:58:05 +06:00
2b938903b6 update kdocs 2023-10-25 14:55:46 +06:00
080db09d2c improvements of commands expectations and triggers 2023-10-25 14:36:32 +06:00
0efa52fe00 start 9.2.4 2023-10-25 13:34:41 +06:00
c4214798e3 Merge branch 'master' into 9.3.0-RC 2023-10-24 16:15:49 +06:00
fdf510153d Merge pull request #796 from InsanusMokrassar/9.2.3
9.2.3
2023-10-24 16:12:17 +06:00
edb16d7107 update gradle wrapper 2023-10-24 15:55:37 +06:00
c49f400201 fix in VoiceContent#createResend 2023-10-24 15:52:06 +06:00
db7de6edf8 start 9.2.3 2023-10-24 15:51:08 +06:00
a0b14233e0 update microutils 2023-10-20 22:35:03 +06:00
1a479706e2 update version of microutils and add arm platform target 2023-10-20 22:07:14 +06:00
2719e166a8 update workgflows 2023-10-17 23:46:28 +06:00
051684db23 update changelog 2023-10-17 23:43:13 +06:00
805cec76ce update kdokka 2023-10-17 23:39:30 +06:00
899c195fd5 update gradle wrapper 2023-10-17 23:38:31 +06:00
f5937fc4d6 upgrade up to 9.3.0-RC 2023-10-17 23:37:51 +06:00
8cf7b349df Merge branch 'master' into 9.3.0 2023-10-15 18:04:44 +06:00
bf8f8b9e6f update microutils 2023-10-15 17:49:13 +06:00
edc0b1c492 update ksp and ktor 2023-10-12 13:27:54 +06:00
a85d58aac1 update up to 1.9.20-RC🎉 2023-10-12 13:21:06 +06:00
10860e1bb2 Merge pull request #794 from InsanusMokrassar/9.2.2
9.2.2
2023-10-11 13:55:41 +06:00
46e6eeca9d improve serializers 2023-10-11 13:38:09 +06:00
80be86454d fix of #793 2023-10-11 13:07:07 +06:00
d5f5a0e30b start 9.2.2 2023-10-11 12:10:36 +06:00
826c27874d update microutils 2023-10-09 20:52:33 +06:00
4e917e8cf8 update libs and changelog with thanks to Anton Lakotka 2023-10-09 13:07:56 +06:00
051210caf5 Merge pull request #791 from InsanusMokrassar/9.2.1
9.2.1
2023-09-29 22:39:04 +06:00
96ffae2062 Update libs.versions.toml 2023-09-27 23:26:57 +06:00
4180721aed update dependencies 2023-09-27 18:18:30 +06:00
4ab0845333 migrate onto 9.3.0 2023-09-27 18:02:14 +06:00
3f9a4e95a3 update up to kotlin 1.9.20-Beta2 2023-09-27 18:02:14 +06:00
834d60ff16 update dependencies 2023-09-27 18:02:14 +06:00
650d96974f update dependencies 2023-09-27 18:02:14 +06:00
96 changed files with 1251 additions and 354 deletions

View File

@@ -5,6 +5,9 @@ on: [pull_request, issues]
jobs: jobs:
greeting: greeting:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps: steps:
- uses: actions/first-interaction@v1 - uses: actions/first-interaction@v1
with: with:

View File

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

View File

@@ -7,9 +7,9 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-java@v1 - uses: actions/setup-java@v1
with: with:
java-version: 11 java-version: 17
- name: Setup LibCurl - name: Setup LibCurl
run: sudo apt install -y libcurl4-openssl-dev run: sudo apt update && sudo apt install -y libcurl4-openssl-dev
- name: Rewrite version - name: Rewrite version
run: | run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`" branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -22,9 +22,10 @@ jobs:
run: ./gradlew build run: ./gradlew build
- name: Publish to Gitea - name: Publish to Gitea
continue-on-error: true continue-on-error: true
run: ./gradlew publishAllPublicationsToGiteaRepository run: ./gradlew publishAllPublicationsToInmoNexusRepository
env: env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} INMONEXUS_USER: ${{ secrets.INMONEXUS_USER }}
INMONEXUS_PASSWORD: ${{ secrets.INMONEXUS_PASSWORD }}
- name: Publish to GithubPackages - name: Publish to GithubPackages
continue-on-error: true continue-on-error: true
run: ./gradlew publishAllPublicationsToGithubPackagesRepository --no-parallel run: ./gradlew publishAllPublicationsToGithubPackagesRepository --no-parallel

View File

@@ -1,5 +1,69 @@
# TelegramBotAPI changelog # TelegramBotAPI changelog
## 9.4.3
**IetfLanguageCode has been renamed to IetfLang in MicroUtils**
* `Version`:
* `Kotlin`: `1.9.21` -> `1.9.22`
* `MicroUtils`: `0.20.19` -> `0.20.23`
## 9.4.2
* `Version`:
* `Serialization`: `1.6.1` -> `1.6.2`
* `Ktor`: `2.3.6` -> `2.3.7`
* `MicroUtils`: `0.20.15` -> `0.20.19`
* `UUID`: `0.8.1` -> `0.8.2`
## 9.4.1
* Replace warning about two bots from `LongPolling` to `DefaultKtorRequestsExecutor`
## 9.4.0
* `Version`:
* `Kotlin`: `1.9.20` -> `1.9.21`
* `Serialization`: `1.6.0` -> `1.6.1`
* `Ktor`: `2.3.5` -> `2.3.6`
* `MicroUtils`: `0.20.12` -> `0.20.15`
## 9.3.0
This release become possible thanks to [Anton Lakotka](https://youtrack.jetbrains.com/users/anton.lakotka)
**THIS RELEASE CONTAINS UPDATES UP TO RELEASE CANDIDATES VERSIONS**
**UPDATE MAY HAVE BREAKING CHANGES**
**SINCE THIS UPDATE IT WILL BE REQUIRED TO USE JDK 17+ FOR DEVELOPMENT**
* `Version`:
* `Kotlin`: `1.8.22` -> `1.9.20`
* `Serialization`: `1.5.1` -> `1.6.0`
* `KorLibs`: `4.0.3` -> `4.0.10`
* `UUID`: `0.7.1` -> `0.8.1`
* `Ktor`: `2.3.4` -> `2.3.5`
* `MicroUtils`: `0.19.9` -> `0.20.12`
## 9.2.4
* `Utils`:
* New extensions `*.parseCommandsWithNamedArgs`
* `BehaviourBuilder`:
* In expectaters and triggers of `commands` add `*WithNamedArgs` variants
* In expectaters and triggers of `commands` add opportunity to use custom separator
## 9.2.3
* `Core`:
* Fix in `VoiceContent#createResend`
## 9.2.2
* `Core`:
* Fix of [#793](https://github.com/InsanusMokrassar/ktgbotapi/issues/793): Add `PreviewChat`
## 9.2.1 ## 9.2.1
* `Version`: * `Version`:

View File

@@ -16,6 +16,7 @@ buildscript {
plugins { plugins {
alias(libs.plugins.kotlin.dokka) alias(libs.plugins.kotlin.dokka)
alias(libs.plugins.versions)
} }
// temporal crutch until legacy tests will be stabled or legacy target will be removed // temporal crutch until legacy tests will be stabled or legacy target will be removed
@@ -24,7 +25,7 @@ allprojects {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
google() google()
maven { url "https://git.inmo.dev/api/packages/InsanusMokrassar/maven" } maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
} }
if (it != rootProject.findProject("docs")) { if (it != rootProject.findProject("docs")) {
tasks.whenTaskAdded { task -> tasks.whenTaskAdded { task ->

View File

@@ -55,7 +55,7 @@ Object callback = {
sourceLink { sourceLink {
localDirectory.set(file("../")) localDirectory.set(file("../"))
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/")) remoteUrl.set(new URL("https://github.com/InsanusMokrassar/ktgbotapi/tree/master"))
remoteLineSuffix.set("#L") remoteLineSuffix.set("#L")
} }
} }

View File

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

View File

@@ -1,22 +1,25 @@
[versions] [versions]
kotlin = "1.8.22" kotlin = "1.9.22"
kotlin-serialization = "1.5.1" kotlin-serialization = "1.6.2"
kotlin-coroutines = "1.7.3" kotlin-coroutines = "1.7.3"
javax-activation = "1.1.1" javax-activation = "1.1.1"
korlibs = "4.0.3" korlibs = "4.0.10"
uuid = "0.7.1" uuid = "0.8.2"
ktor = "2.3.4" ktor = "2.3.7"
ksp = "1.8.22-1.0.11" ksp = "1.9.21-1.0.16"
kotlin-poet = "1.14.2" kotlin-poet = "1.15.3"
microutils = "0.19.9" microutils = "0.20.23"
kslog = "1.3.1"
versions = "0.50.0"
github-release-plugin = "2.4.1" github-release-plugin = "2.4.1"
dokka = "1.8.20" dokka = "1.9.10"
[libraries] [libraries]
@@ -52,6 +55,8 @@ microutils-languageCodes = { module = "dev.inmo:micro_utils.language_codes", ver
microutils-ktor-common = { module = "dev.inmo:micro_utils.ktor.common", version.ref = "microutils" } microutils-ktor-common = { module = "dev.inmo:micro_utils.ktor.common", version.ref = "microutils" }
microutils-fsm-common = { module = "dev.inmo:micro_utils.fsm.common", version.ref = "microutils" } microutils-fsm-common = { module = "dev.inmo:micro_utils.fsm.common", version.ref = "microutils" }
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
# ksp dependencies # ksp dependencies
kotlin-poet = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin-poet" } kotlin-poet = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin-poet" }
@@ -71,3 +76,4 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlin-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kotlin-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
versions = { id = "com.github.ben-manes.versions", version.ref = "versions" }

View File

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

View File

@@ -5,7 +5,7 @@ kotlin {
jvm { jvm {
compilations.main { compilations.main {
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "17"
} }
} }
} }
@@ -15,6 +15,7 @@ kotlin {
} }
linuxX64() linuxX64()
mingwX64() mingwX64()
linuxArm64()
sourceSets { sourceSets {
commonMain { commonMain {
@@ -40,10 +41,18 @@ kotlin {
implementation libs.kotlin.test.junit implementation libs.kotlin.test.junit
} }
} }
all {
languageSettings {
optIn('dev.inmo.tgbotapi.utils.RiskFeature')
optIn('dev.inmo.tgbotapi.utils.PreviewFeature')
optIn('dev.inmo.micro_utils.common.Warning')
optIn('dev.inmo.micro_utils.common.PreviewFeature')
}
}
} }
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_17
} }

View File

@@ -1,8 +1,7 @@
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
task javadocsJar(type: Jar) { task javadocsJar(type: Jar) {
archiveClassifier.convention("javadoc") archiveClassifier = 'javadoc'
archiveClassifier.set("javadoc")
} }
publishing { publishing {
@@ -20,22 +19,22 @@ publishing {
} }
developers { developers {
developer { developer {
id = "InsanusMokrassar" id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei" name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com" email = "ovsyannikov.alexey95@gmail.com"
} }
} }
licenses { licenses {
license { license {
name = "Apache Software License 2.0" name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE" url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
} }
} }
} }
repositories { repositories {
@@ -43,58 +42,77 @@ publishing {
maven { maven {
name = "GithubPackages" name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI") url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI")
credentials { credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER') username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
} }
} }
} }
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) { if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
maven { maven {
name = "Gitea" name = "InmoNexus"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven") url = uri("https://nexus.inmo.dev/repository/maven-releases/")
credentials(HttpHeaderCredentials) { credentials {
name = "Authorization" username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER')
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN') password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD')
} }
authentication {
header(HttpHeaderAuthentication)
}
} }
} }
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven { maven {
name = "sonatype" name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials { credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
} }
} }
} }
} }
} }
} }
if (project.hasProperty("signing.gnupg.keyName")) { if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing' apply plugin: 'signing'
signing { signing {
useGpgCmd() useGpgCmd()
sign publishing.publications sign publishing.publications
} }
task signAll { task signAll {
tasks.withType(Sign).forEach { tasks.withType(Sign).forEach {
dependsOn(it) dependsOn(it)
} }
} }
// Workaround to make android sign operations depend on signing tasks
project.getTasks().withType(AbstractPublishToMaven.class).configureEach {
def signingTasks = project.getTasks().withType(Sign.class)
mustRunAfter(signingTasks)
}
// Workaround to make test tasks use sign
project.getTasks().withType(Sign.class).configureEach { signTask ->
def withoutSign = (signTask.name.startsWith("sign") ? signTask.name.minus("sign") : signTask.name)
def pubName = withoutSign.endsWith("Publication") ? withoutSign.substring(0, withoutSign.length() - "Publication".length()) : withoutSign
// These tasks only exist for native targets, hence findByName() to avoid trying to find them for other targets
// Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def debugTestTask = tasks.findByName("linkDebugTest$pubName")
if (debugTestTask != null) {
signTask.mustRunAfter(debugTestTask)
}
// Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def testTask = tasks.findByName("compileTestKotlin$pubName")
if (testTask != null) {
signTask.mustRunAfter(testTask)
}
}
} }

View File

@@ -1 +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":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}} {"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":"InmoNexus","url":"https://nexus.inmo.dev/repository/maven-releases/"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.extensions.api.bot package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.DeleteMyCommands import dev.inmo.tgbotapi.requests.bot.DeleteMyCommands
import dev.inmo.tgbotapi.types.commands.BotCommandScope import dev.inmo.tgbotapi.types.commands.BotCommandScope
@@ -8,10 +8,10 @@ import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.deleteMyCommands( suspend fun TelegramBot.deleteMyCommands(
scope: BotCommandScope = BotCommandScopeDefault, scope: BotCommandScope = BotCommandScopeDefault,
languageCode: IetfLanguageCode? languageCode: IetfLang?
) = execute(DeleteMyCommands(scope, languageCode)) ) = execute(DeleteMyCommands(scope, languageCode))
suspend fun TelegramBot.deleteMyCommands( suspend fun TelegramBot.deleteMyCommands(
scope: BotCommandScope = BotCommandScopeDefault, scope: BotCommandScope = BotCommandScopeDefault,
languageCode: String? = null languageCode: String? = null
) = deleteMyCommands(scope, languageCode ?.let(::IetfLanguageCode)) ) = deleteMyCommands(scope, languageCode ?.let(::IetfLang))

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.extensions.api.bot package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.GetMyCommands import dev.inmo.tgbotapi.requests.bot.GetMyCommands
import dev.inmo.tgbotapi.types.commands.BotCommandScope import dev.inmo.tgbotapi.types.commands.BotCommandScope
@@ -8,10 +8,10 @@ import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.getMyCommands( suspend fun TelegramBot.getMyCommands(
scope: BotCommandScope = BotCommandScopeDefault, scope: BotCommandScope = BotCommandScopeDefault,
languageCode: IetfLanguageCode? = null languageCode: IetfLang? = null
) = execute(GetMyCommands(scope, languageCode)) ) = execute(GetMyCommands(scope, languageCode))
suspend fun TelegramBot.getMyCommands( suspend fun TelegramBot.getMyCommands(
scope: BotCommandScope = BotCommandScopeDefault, scope: BotCommandScope = BotCommandScopeDefault,
languageCode: String? languageCode: String?
) = getMyCommands(scope, languageCode ?.let(::IetfLanguageCode)) ) = getMyCommands(scope, languageCode ?.let(::IetfLang))

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.extensions.api.bot package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.SetMyCommands import dev.inmo.tgbotapi.requests.bot.SetMyCommands
import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.BotCommand
@@ -10,20 +10,20 @@ import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.setMyCommands( suspend fun TelegramBot.setMyCommands(
commands: List<BotCommand>, commands: List<BotCommand>,
scope: BotCommandScope = BotCommandScopeDefault, scope: BotCommandScope = BotCommandScopeDefault,
languageCode: IetfLanguageCode? languageCode: IetfLang?
) = execute(SetMyCommands(commands, scope, languageCode)) ) = execute(SetMyCommands(commands, scope, languageCode))
suspend fun TelegramBot.setMyCommands( suspend fun TelegramBot.setMyCommands(
vararg commands: BotCommand, vararg commands: BotCommand,
scope: BotCommandScope = BotCommandScopeDefault, scope: BotCommandScope = BotCommandScopeDefault,
languageCode: IetfLanguageCode? languageCode: IetfLang?
) = setMyCommands(commands.toList(), scope, languageCode) ) = setMyCommands(commands.toList(), scope, languageCode)
suspend fun TelegramBot.setMyCommands( suspend fun TelegramBot.setMyCommands(
commands: List<BotCommand>, commands: List<BotCommand>,
scope: BotCommandScope = BotCommandScopeDefault, scope: BotCommandScope = BotCommandScopeDefault,
languageCode: String? = null languageCode: String? = null
) = setMyCommands(commands, scope, languageCode ?.let(::IetfLanguageCode)) ) = setMyCommands(commands, scope, languageCode ?.let(::IetfLang))
suspend fun TelegramBot.setMyCommands( suspend fun TelegramBot.setMyCommands(
vararg commands: BotCommand, vararg commands: BotCommand,

View File

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

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.extensions.api.bot package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.GetMyCommands import dev.inmo.tgbotapi.requests.bot.GetMyCommands
import dev.inmo.tgbotapi.requests.bot.GetMyName import dev.inmo.tgbotapi.requests.bot.GetMyName
@@ -10,10 +10,10 @@ import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.setMyName( suspend fun TelegramBot.setMyName(
name: String? = null, name: String? = null,
languageCode: IetfLanguageCode? = null languageCode: IetfLang? = null
) = execute(SetMyName(name, languageCode)) ) = execute(SetMyName(name, languageCode))
suspend fun TelegramBot.setMyName( suspend fun TelegramBot.setMyName(
name: String?, name: String?,
languageCode: String? languageCode: String?
) = setMyName(name, languageCode ?.let(::IetfLanguageCode)) ) = setMyName(name, languageCode ?.let(::IetfLang))

View File

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

View File

@@ -13,6 +13,8 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api project(":tgbotapi.core")
api project(":tgbotapi.utils")
api project(":tgbotapi.behaviour_builder") api project(":tgbotapi.behaviour_builder")
api libs.microutils.fsm.common api libs.microutils.fsm.common
} }

View File

@@ -13,7 +13,8 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api project(":tgbotapi.utils") api project(path: ':tgbotapi.core')
api project(path: ':tgbotapi.utils')
} }
} }
} }

View File

@@ -3,6 +3,10 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.doWithRegistration import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.doWithRegistration
import dev.inmo.tgbotapi.extensions.utils.* import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.TelegramBotCommandsDefaults
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgsSources
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithNamedArgs
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
@@ -79,35 +83,54 @@ fun Flow<CommonMessage<TextContent>>.requireCommandsWithoutParams() = filter {
} }
/** /**
* Map the commands with their arguments and source messages * Uses [parseCommandsWithArgsSources] on incoming text sources and map them with [CommonMessage]
*/ */
fun Flow<CommonMessage<TextContent>>.commandsWithParams(): Flow<Pair<CommonMessage<TextContent>, List<Pair<BotCommandTextSource, Array<TextSource>>>>> = mapNotNull { fun Flow<CommonMessage<TextContent>>.commandsWithParams(): Flow<Pair<CommonMessage<TextContent>, List<Pair<BotCommandTextSource, Array<TextSource>>>>> = mapNotNull {
var currentCommandTextSource: BotCommandTextSource? = null it to it.content.textSources.parseCommandsWithArgsSources().toList()
val currentArgs = mutableListOf<TextSource>()
val result = mutableListOf<Pair<BotCommandTextSource, Array<TextSource>>>()
fun addCurrentCommandToResult() {
currentCommandTextSource ?.let {
result.add(it to currentArgs.toTypedArray())
currentArgs.clear()
}
}
it.content.textSources.forEach {
it.ifBotCommandTextSource {
addCurrentCommandToResult()
currentCommandTextSource = it
return@forEach
}
currentArgs.add(it)
}
addCurrentCommandToResult()
result.toList().takeIf { it.isNotEmpty() } ?.let { result ->
it to result
}
} }
/**
* Uses [parseCommandsWithArgs] on incoming text sources and map them with [CommonMessage]
*/
fun Flow<CommonMessage<TextContent>>.commandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, Array<String>>>>> = mapNotNull {
val commandsWithArgs = it.content.textSources.parseCommandsWithArgs(argsSeparator).toList().ifEmpty {
return@mapNotNull null
}
it to commandsWithArgs
}
/**
* Uses [parseCommandsWithArgs] on incoming text sources and map them with [CommonMessage]
*/
fun Flow<CommonMessage<TextContent>>.commandsWithArgs(
argsSeparator: String
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, Array<String>>>>> = commandsWithArgs(Regex(argsSeparator))
/**
* Uses [parseCommandsWithNamedArgs] on incoming text sources and map them with [CommonMessage]
*/
fun Flow<CommonMessage<TextContent>>.commandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, List<Pair<String, String>>>>>> = mapNotNull {
val commandsWithArgs = it.content.textSources.parseCommandsWithNamedArgs(argsSeparator, nameArgSeparator).toList().ifEmpty {
return@mapNotNull null
}
it to commandsWithArgs
}
/**
* Uses [parseCommandsWithNamedArgs] on incoming text sources and map them with [CommonMessage]
*/
fun Flow<CommonMessage<TextContent>>.commandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, List<Pair<String, String>>>>>> = commandsWithNamedArgs(Regex(argsSeparator), nameArgSeparator)
/** /**
* Flat [commandsWithParams]. Each [Pair] of [BotCommandTextSource] and its [Array] of arg text sources will * Flat [commandsWithParams]. Each [Pair] of [BotCommandTextSource] and its [Array] of arg text sources will
* be associated with its source message * be associated with its source message

View File

@@ -11,7 +11,9 @@ 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.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times
import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams import dev.inmo.tgbotapi.extensions.utils.extensions.TelegramBotCommandsDefaults
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithNamedArgs
import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextMessage import dev.inmo.tgbotapi.types.message.content.TextMessage
@@ -124,6 +126,7 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups, initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat, subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory, markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>> scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = command( ) = command(
commandRegex, commandRegex,
@@ -132,7 +135,7 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
subcontextUpdatesFilter = subcontextUpdatesFilter, subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory markerFactory = markerFactory
) { ) {
val args = it.parseCommandsWithParams().let { commandsWithArgs -> val args = it.parseCommandsWithArgs(argsSeparator = argsSeparator).let { commandsWithArgs ->
val key = commandsWithArgs.keys.firstOrNull { it.matches(commandRegex) } ?: return@let null val key = commandsWithArgs.keys.firstOrNull { it.matches(commandRegex) } ?: return@let null
commandsWithArgs[key] commandsWithArgs[key]
} ?: emptyArray() } ?: emptyArray()
@@ -144,12 +147,14 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups, initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat, subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory, markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>> scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = commandWithArgs( ) = commandWithArgs(
command.toRegex(), command.toRegex(),
initialFilter = initialFilter, initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter, subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory, markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver scenarioReceiver = scenarioReceiver
) )
@@ -158,12 +163,72 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups, initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat, subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory, markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>> scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = commandWithArgs( ) = commandWithArgs(
botCommand.command, botCommand.command,
initialFilter = initialFilter, initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter, subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory, markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.commandWithNamedArgs(
commandRegex: Regex,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = command(
commandRegex,
requireOnlyCommandInMessage = false,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory
) {
val args = it.parseCommandsWithNamedArgs(argsSeparator = argsSeparator, nameArgSeparator = nameArgSeparator).let { commandsWithArgs ->
val key = commandsWithArgs.keys.firstOrNull { it.matches(commandRegex) } ?: return@let null
commandsWithArgs[key]
} ?: emptyList()
scenarioReceiver(it, args)
}
suspend fun <BC : BehaviourContext> BC.commandWithNamedArgs(
command: String,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = commandWithNamedArgs(
command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.commandWithNamedArgs(
botCommand: BotCommand,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = commandWithNamedArgs(
botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver scenarioReceiver = scenarioReceiver
) )
@@ -172,21 +237,99 @@ suspend fun <BC : BehaviourContext> BC.onCommandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups, initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat, subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory, markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>> scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
): Job = commandWithArgs(commandRegex, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) ): Job = commandWithArgs(
commandRegex = commandRegex,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithArgs( suspend fun <BC : BehaviourContext> BC.onCommandWithArgs(
command: String, command: String,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups, initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat, subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory, markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>> scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
): Job = onCommandWithArgs(command.toRegex(), initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) ): Job = onCommandWithArgs(
commandRegex = command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithArgs( suspend fun <BC : BehaviourContext> BC.onCommandWithArgs(
botCommand: BotCommand, botCommand: BotCommand,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups, initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat, subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory, markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>> scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
): Job = onCommandWithArgs(botCommand.command, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) ): Job = onCommandWithArgs(
command = botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithNamedArgs(
commandRegex: Regex,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = commandWithNamedArgs(
commandRegex,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver,
)
suspend fun <BC : BehaviourContext> BC.onCommandWithNamedArgs(
command: String,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = onCommandWithNamedArgs(
command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithNamedArgs(
botCommand: BotCommand,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = onCommandWithNamedArgs(
botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver
)

View File

@@ -9,7 +9,7 @@ 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.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times
import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextMessage import dev.inmo.tgbotapi.types.message.content.TextMessage
import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.types.update.abstracts.Update
@@ -65,7 +65,7 @@ suspend fun <BC : BehaviourContext> BC.unhandledCommandWithArgs(
subcontextUpdatesFilter = subcontextUpdatesFilter, subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory markerFactory = markerFactory
) { ) {
val args = it.parseCommandsWithParams().let { commandsWithArgs -> val args = it.parseCommandsWithArgs().let { commandsWithArgs ->
commandsWithArgs commandsWithArgs
} }
scenarioReceiver(it, args) scenarioReceiver(it, args)

View File

@@ -4,22 +4,18 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.* import dev.inmo.micro_utils.coroutines.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitDeepLinks
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.CommonMessageFilterExcludeMediaGroups
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByChatMessageMarkerFactory import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByChatMessageMarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times
import dev.inmo.tgbotapi.extensions.utils.* import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextMessage import dev.inmo.tgbotapi.types.message.content.TextMessage
import dev.inmo.tgbotapi.types.message.textsources.RegularTextSource import dev.inmo.tgbotapi.types.message.textsources.RegularTextSource
import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.types.update.abstracts.Update
import io.ktor.http.decodeURLQueryComponent import io.ktor.http.decodeURLQueryComponent
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.filter
private val startRegex = Regex("start") private val startRegex = Regex("start")
suspend fun <BC : BehaviourContext> BC.onDeepLink( suspend fun <BC : BehaviourContext> BC.onDeepLink(

View File

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

View File

@@ -31,6 +31,8 @@ kotlin {
api libs.microutils.languageCodes api libs.microutils.languageCodes
api libs.ktor.client.core api libs.ktor.client.core
api libs.kslog
} }
} }
commonTest { commonTest {
@@ -55,6 +57,12 @@ kotlin {
} }
} }
linuxArm64Main {
dependencies {
api libs.ktor.client.cio
}
}
mingwX64Main { mingwX64Main {
dependencies { dependencies {
api libs.ktor.client.winhttp api libs.ktor.client.winhttp

View File

@@ -1,10 +1,17 @@
package dev.inmo.tgbotapi.abstracts package dev.inmo.tgbotapi.abstracts
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.chat.PreviewChat
/** /**
* All inheritors of this interface have [chat] field and related to this [chat] * All inheritors of this interface have [chat] field and related to this [chat]
*/ */
interface WithChat { interface WithPreviewChat {
val chat: Chat val chat: PreviewChat
} }
/**
* All inheritors of this interface have [chat] field and related to this [chat]
*/
@Deprecated("Renamed", ReplaceWith("WithPreviewChat", "dev.inmo.tgbotapi.abstracts.WithPreviewChat"))
typealias WithChat = WithPreviewChat

View File

@@ -1,8 +1,10 @@
package dev.inmo.tgbotapi.bot.ktor package dev.inmo.tgbotapi.bot.ktor
import dev.inmo.kslog.common.KSLog
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
import io.ktor.client.* import io.ktor.client.*
@@ -14,12 +16,35 @@ import kotlinx.serialization.json.Json
* * On JS, JVM and MingwX64 platforms it is [dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor] * * On JS, JVM and MingwX64 platforms it is [dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor]
* * On LinuxX64 it is [dev.inmo.tgbotapi.bot.ktor.base.MultipleClientKtorRequestsExecutor] * * On LinuxX64 it is [dev.inmo.tgbotapi.bot.ktor.base.MultipleClientKtorRequestsExecutor]
*/ */
expect class KtorRequestsExecutor ( expect class KtorRequestsExecutor internal constructor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient,
callsFactories: List<KtorCallFactory>,
excludeDefaultFactories: Boolean,
requestsLimiter: RequestLimiter,
jsonFormatter: Json,
pipelineStepsHolder: KtorPipelineStepsHolder,
logger: KSLog,
diff: Unit // just a diff property to know where constructor and where calling function with defaults
) : BaseRequestsExecutor
fun KtorRequestsExecutor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper, telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient = HttpClient(), client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(), callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false, excludeDefaultFactories: Boolean = false,
requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter, requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
jsonFormatter: Json = nonstrictJsonFormat, jsonFormatter: Json = nonstrictJsonFormat,
pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder,
) : BaseRequestsExecutor logger: KSLog = DefaultKTgBotAPIKSLog,
) = KtorRequestsExecutor(
telegramAPIUrlsKeeper = telegramAPIUrlsKeeper,
client = client,
callsFactories = callsFactories,
excludeDefaultFactories = excludeDefaultFactories,
requestsLimiter = requestsLimiter,
jsonFormatter = jsonFormatter,
pipelineStepsHolder = pipelineStepsHolder,
logger = logger,
diff = kotlin.Unit,
)

View File

@@ -1,5 +1,6 @@
package dev.inmo.tgbotapi.bot.ktor package dev.inmo.tgbotapi.bot.ktor
import dev.inmo.kslog.common.KSLog
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.ktor.base.* import dev.inmo.tgbotapi.bot.ktor.base.*
@@ -10,9 +11,9 @@ import io.ktor.client.HttpClient
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@RiskFeature @RiskFeature
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf( fun createTelegramBotDefaultKtorCallRequestsFactories(logger: KSLog? = null) = listOf(
SimpleRequestCallFactory(), SimpleRequestCallFactory(logger),
MultipartRequestCallFactory(), MultipartRequestCallFactory(logger),
DownloadFileRequestCallFactory, DownloadFileRequestCallFactory,
DownloadFileChannelRequestCallFactory DownloadFileChannelRequestCallFactory
) )
@@ -25,6 +26,8 @@ class KtorRequestsExecutorBuilder(
var excludeDefaultFactories: Boolean = false var excludeDefaultFactories: Boolean = false
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
var jsonFormatter: Json = nonstrictJsonFormat var jsonFormatter: Json = nonstrictJsonFormat
var logger: KSLog = DefaultKTgBotAPIKSLog
var pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
fun build() = KtorRequestsExecutor( fun build() = KtorRequestsExecutor(
telegramAPIUrlsKeeper, telegramAPIUrlsKeeper,
@@ -32,7 +35,9 @@ class KtorRequestsExecutorBuilder(
callsFactories, callsFactories,
excludeDefaultFactories, excludeDefaultFactories,
requestsLimiter, requestsLimiter,
jsonFormatter jsonFormatter,
pipelineStepsHolder,
logger
) )
} }

View File

@@ -1,23 +1,30 @@
package dev.inmo.tgbotapi.bot.ktor.base package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.v
import dev.inmo.kslog.common.w
import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.bot.exceptions.newRequestException import dev.inmo.tgbotapi.bot.exceptions.newRequestException
import dev.inmo.tgbotapi.requests.GetUpdatesRequest import dev.inmo.tgbotapi.requests.GetUpdatesRequest
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.Response import dev.inmo.tgbotapi.types.Response
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.plugins.timeout import io.ktor.client.plugins.timeout
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType import io.ktor.http.ContentType
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlin.collections.set import kotlin.collections.set
var defaultUpdateTimeoutForZeroDelay = 1000L var defaultUpdateTimeoutForZeroDelay = 1000L
abstract class AbstractRequestCallFactory : KtorCallFactory { abstract class AbstractRequestCallFactory(
protected open val logger: KSLog = DefaultKTgBotAPIKSLog
) : KtorCallFactory {
private val methodsCache: MutableMap<String, String> = mutableMapOf() private val methodsCache: MutableMap<String, String> = mutableMapOf()
override suspend fun <T : Any> makeCall( override suspend fun <T : Any> makeCall(
client: HttpClient, client: HttpClient,
@@ -26,6 +33,7 @@ abstract class AbstractRequestCallFactory : KtorCallFactory {
jsonFormatter: Json jsonFormatter: Json
): T? { ): T? {
val preparedBody = prepareCallBody(client, urlsKeeper, request) ?: return null val preparedBody = prepareCallBody(client, urlsKeeper, request) ?: return null
logger.v { "Prepared body for $request: $preparedBody" }
client.post { client.post {
url( url(
@@ -54,7 +62,9 @@ abstract class AbstractRequestCallFactory : KtorCallFactory {
setBody(preparedBody) setBody(preparedBody)
}.let { response -> }.let { response ->
val content = response.bodyAsText() val content = response.bodyAsText()
logger.v { "Raw answer for $request: $content" }
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content) val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
logger.v { "Answer as json for $request: $responseObject" }
return runCatchingSafely { return runCatchingSafely {
(responseObject.result?.let { (responseObject.result?.let {
@@ -66,6 +76,8 @@ abstract class AbstractRequestCallFactory : KtorCallFactory {
"Can't get result object from $content" "Can't get result object from $content"
) )
}) })
}.onFailure {
logger.w { "Got exception answer for $request: $it" }
}.getOrThrow() }.getOrThrow()
} }
} }

View File

@@ -1,12 +1,16 @@
package dev.inmo.tgbotapi.bot.ktor.base package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.kslog.common.*
import dev.inmo.micro_utils.coroutines.defaultSafelyExceptionHandler
import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.exceptions.BotException import dev.inmo.tgbotapi.bot.exceptions.BotException
import dev.inmo.tgbotapi.bot.exceptions.CommonBotException import dev.inmo.tgbotapi.bot.exceptions.CommonBotException
import dev.inmo.tgbotapi.bot.exceptions.GetUpdatesConflict
import dev.inmo.tgbotapi.bot.exceptions.newRequestException import dev.inmo.tgbotapi.bot.exceptions.newRequestException
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutor
import dev.inmo.tgbotapi.bot.ktor.createTelegramBotDefaultKtorCallRequestsFactories import dev.inmo.tgbotapi.bot.ktor.createTelegramBotDefaultKtorCallRequestsFactories
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
@@ -19,19 +23,23 @@ import io.ktor.client.plugins.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
class DefaultKtorRequestsExecutor( class DefaultKtorRequestsExecutor internal constructor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper, telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient = HttpClient(), client: HttpClient,
callsFactories: List<KtorCallFactory> = emptyList(), callsFactories: List<KtorCallFactory>,
excludeDefaultFactories: Boolean = false, excludeDefaultFactories: Boolean,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter, private val requestsLimiter: RequestLimiter,
private val jsonFormatter: Json = nonstrictJsonFormat, private val jsonFormatter: Json,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder private val pipelineStepsHolder: KtorPipelineStepsHolder,
private val logger: KSLog,
diff: Unit
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run { private val callsFactories: List<KtorCallFactory> = callsFactories.run {
if (!excludeDefaultFactories) { if (!excludeDefaultFactories) {
this + createTelegramBotDefaultKtorCallRequestsFactories() logger.v { "Installing default factories" }
this + createTelegramBotDefaultKtorCallRequestsFactories(logger)
} else { } else {
logger.v { "Default factories will not be installed" }
this this
} }
} }
@@ -44,19 +52,23 @@ class DefaultKtorRequestsExecutor(
override suspend fun <T : Any> execute(request: Request<T>): T { override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatchingSafely { return runCatchingSafely {
logger.v { "Start request $request" }
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories) pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit(request) { requestsLimiter.limit(request) {
var result: T? = null var result: T? = null
lateinit var factoryHandledRequest: KtorCallFactory lateinit var factoryHandledRequest: KtorCallFactory
for (potentialFactory in callsFactories) { for (potentialFactory in callsFactories) {
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory) pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
result = potentialFactory.makeCall( logger.v { "Trying factory $potentialFactory for $request" }
val resultFromFactory = potentialFactory.makeCall(
client, client,
telegramAPIUrlsKeeper, telegramAPIUrlsKeeper,
request, request,
jsonFormatter jsonFormatter
) )
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(result, request, potentialFactory) logger.v { "Result of factory $potentialFactory handling $request: $resultFromFactory" }
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(resultFromFactory, request, potentialFactory)
logger.v { "Result of pipeline $pipelineStepsHolder handling $resultFromFactory: $result" }
if (result != null) { if (result != null) {
factoryHandledRequest = potentialFactory factoryHandledRequest = potentialFactory
break break
@@ -69,6 +81,7 @@ class DefaultKtorRequestsExecutor(
} }
}.let { }.let {
val result = it.exceptionOrNull() ?.let { e -> val result = it.exceptionOrNull() ?.let { e ->
logger.v(e) { "Got exception on handling of $request" }
pipelineStepsHolder.onRequestException(request, e) ?.let { return@let it } pipelineStepsHolder.onRequestException(request, e) ?.let { return@let it }
when (e) { when (e) {
@@ -88,9 +101,18 @@ class DefaultKtorRequestsExecutor(
} }
is BotException -> e is BotException -> e
else -> CommonBotException(cause = e) else -> CommonBotException(cause = e)
}.also { newException ->
logger.v(newException) { "Result exception on handling of $request is an exception" }
if (newException is GetUpdatesConflict) {
logger.w(newException) {
"Warning!!! Other bot with the same bot token requests updates with getUpdate in parallel"
}
}
} }
} ?.let { Result.failure(it) } ?: it } ?.let { Result.failure(it) } ?: it
pipelineStepsHolder.onRequestReturnResult(result, request, callsFactories) pipelineStepsHolder.onRequestReturnResult(result, request, callsFactories).also {
logger.v { "Result of handling $request: $it" }
}
} }
} }

View File

@@ -5,6 +5,7 @@ import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.requests.DownloadFileStream import dev.inmo.tgbotapi.requests.DownloadFileStream
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.ByteReadChannelAllocator import dev.inmo.tgbotapi.utils.ByteReadChannelAllocator
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.receive import io.ktor.client.call.receive
@@ -15,6 +16,7 @@ import io.ktor.utils.io.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@RiskFeature
object DownloadFileChannelRequestCallFactory : KtorCallFactory { object DownloadFileChannelRequestCallFactory : KtorCallFactory {
override suspend fun <T : Any> makeCall( override suspend fun <T : Any> makeCall(
client: HttpClient, client: HttpClient,

View File

@@ -4,12 +4,14 @@ import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.requests.DownloadFile import dev.inmo.tgbotapi.requests.DownloadFile
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.readBytes import io.ktor.client.statement.readBytes
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@RiskFeature
object DownloadFileRequestCallFactory : KtorCallFactory { object DownloadFileRequestCallFactory : KtorCallFactory {
override suspend fun <T : Any> makeCall( override suspend fun <T : Any> makeCall(
client: HttpClient, client: HttpClient,

View File

@@ -1,6 +1,8 @@
package dev.inmo.tgbotapi.bot.ktor.base package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.kslog.common.KSLog
import dev.inmo.tgbotapi.requests.abstracts.* import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.mapWithCommonValues import dev.inmo.tgbotapi.utils.mapWithCommonValues
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
@@ -8,7 +10,7 @@ import io.ktor.client.request.forms.*
import io.ktor.http.Headers import io.ktor.http.Headers
import io.ktor.http.HttpHeaders import io.ktor.http.HttpHeaders
class MultipartRequestCallFactory : AbstractRequestCallFactory() { class MultipartRequestCallFactory(logger: KSLog? = null) : AbstractRequestCallFactory(logger ?: DefaultKTgBotAPIKSLog) {
override fun <T : Any> prepareCallBody( override fun <T : Any> prepareCallBody(
client: HttpClient, client: HttpClient,
urlsKeeper: TelegramAPIUrlsKeeper, urlsKeeper: TelegramAPIUrlsKeeper,

View File

@@ -1,12 +1,16 @@
package dev.inmo.tgbotapi.bot.ktor.base package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.kslog.common.KSLog
import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutor
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
import io.ktor.client.* import io.ktor.client.*
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@@ -45,6 +49,7 @@ class MultipleClientKtorRequestsExecutor (
jsonFormatter: Json, jsonFormatter: Json,
pipelineStepsHolder: KtorPipelineStepsHolder, pipelineStepsHolder: KtorPipelineStepsHolder,
requestExecutorsCount: Int, requestExecutorsCount: Int,
logger: KSLog,
clientFactory: () -> HttpClient clientFactory: () -> HttpClient
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val requestExecutors = (0 until requestExecutorsCount).map { private val requestExecutors = (0 until requestExecutorsCount).map {
@@ -55,7 +60,9 @@ class MultipleClientKtorRequestsExecutor (
excludeDefaultFactories, excludeDefaultFactories,
requestsLimiter, requestsLimiter,
jsonFormatter, jsonFormatter,
pipelineStepsHolder pipelineStepsHolder,
logger,
Unit
) )
}.toSet() }.toSet()
private val freeClients = MutableStateFlow<Set<DefaultKtorRequestsExecutor>>(requestExecutors) private val freeClients = MutableStateFlow<Set<DefaultKtorRequestsExecutor>>(requestExecutors)
@@ -68,14 +75,16 @@ class MultipleClientKtorRequestsExecutor (
} }
} }
constructor( internal constructor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper, telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient, client: HttpClient,
callsFactories: List<KtorCallFactory>, callsFactories: List<KtorCallFactory>,
excludeDefaultFactories: Boolean, excludeDefaultFactories: Boolean,
requestsLimiter: RequestLimiter, requestsLimiter: RequestLimiter,
jsonFormatter: Json, jsonFormatter: Json,
pipelineStepsHolder: KtorPipelineStepsHolder pipelineStepsHolder: KtorPipelineStepsHolder,
logger: KSLog,
diff: Unit
) : this( ) : this(
telegramAPIUrlsKeeper, telegramAPIUrlsKeeper,
callsFactories, callsFactories,
@@ -84,6 +93,7 @@ class MultipleClientKtorRequestsExecutor (
jsonFormatter, jsonFormatter,
pipelineStepsHolder, pipelineStepsHolder,
client.engineConfig.threadsCount, client.engineConfig.threadsCount,
logger,
{ platformClientCopy(client) } { platformClientCopy(client) }
) )

View File

@@ -1,12 +1,14 @@
package dev.inmo.tgbotapi.bot.ktor.base package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.kslog.common.KSLog
import dev.inmo.tgbotapi.requests.abstracts.* import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.content.TextContent import io.ktor.http.content.TextContent
class SimpleRequestCallFactory : AbstractRequestCallFactory() { class SimpleRequestCallFactory(logger: KSLog? = null) : AbstractRequestCallFactory(logger ?: DefaultKTgBotAPIKSLog) {
override fun <T : Any> prepareCallBody( override fun <T : Any> prepareCallBody(
client: HttpClient, client: HttpClient,
urlsKeeper: TelegramAPIUrlsKeeper, urlsKeeper: TelegramAPIUrlsKeeper,

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.types.commands.* import dev.inmo.tgbotapi.types.commands.*
import dev.inmo.tgbotapi.types.languageCodeField import dev.inmo.tgbotapi.types.languageCodeField
import dev.inmo.tgbotapi.types.scopeField import dev.inmo.tgbotapi.types.scopeField
@@ -14,8 +15,8 @@ data class DeleteMyCommands(
@Serializable(BotCommandScopeSerializer::class) @Serializable(BotCommandScopeSerializer::class)
override val scope: BotCommandScope = BotCommandScopeDefault, override val scope: BotCommandScope = BotCommandScopeDefault,
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : MyCommandsRequest<Boolean> { ) : MyCommandsRequest<Boolean> {
override fun method(): String = "deleteMyCommands" override fun method(): String = "deleteMyCommands"
override val requestSerializer: SerializationStrategy<DeleteMyCommands> override val requestSerializer: SerializationStrategy<DeleteMyCommands>
@@ -28,7 +29,7 @@ data class DeleteMyCommands(
languageCode: String? languageCode: String?
) : this( ) : this(
scope, scope,
languageCode ?.let(::IetfLanguageCode) languageCode ?.let(::IetfLang)
) )
companion object : MyCommandsRequest<Boolean> by DeleteMyCommands() companion object : MyCommandsRequest<Boolean> by DeleteMyCommands()

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.commands.* import dev.inmo.tgbotapi.types.commands.*
import kotlinx.serialization.* import kotlinx.serialization.*
@@ -15,8 +16,8 @@ data class GetMyCommands(
@Serializable(BotCommandScopeSerializer::class) @Serializable(BotCommandScopeSerializer::class)
override val scope: BotCommandScope = BotCommandScopeDefault, override val scope: BotCommandScope = BotCommandScopeDefault,
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : MyCommandsRequest<List<BotCommand>> { ) : MyCommandsRequest<List<BotCommand>> {
override fun method(): String = "getMyCommands" override fun method(): String = "getMyCommands"
override val resultDeserializer: DeserializationStrategy<List<BotCommand>> override val resultDeserializer: DeserializationStrategy<List<BotCommand>>
@@ -29,7 +30,7 @@ data class GetMyCommands(
languageCode: String? languageCode: String?
) : this( ) : this(
scope, scope,
languageCode ?.let(::IetfLanguageCode) languageCode ?.let(::IetfLang)
) )
companion object : MyCommandsRequest<List<BotCommand>> by GetMyCommands() companion object : MyCommandsRequest<List<BotCommand>> by GetMyCommands()

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
@@ -12,8 +13,8 @@ import kotlinx.serialization.builtins.serializer
@Serializable @Serializable
class GetMyDescription( class GetMyDescription(
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : SimpleRequest<BotDescription>, WithOptionalLanguageCode { ) : SimpleRequest<BotDescription>, WithOptionalLanguageCode {
override fun method(): String = "getMyDescription" override fun method(): String = "getMyDescription"
override val resultDeserializer: DeserializationStrategy<BotDescription> override val resultDeserializer: DeserializationStrategy<BotDescription>

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
@@ -12,8 +13,8 @@ import kotlinx.serialization.builtins.serializer
@Serializable @Serializable
class GetMyName( class GetMyName(
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : SimpleRequest<BotName>, WithOptionalLanguageCode { ) : SimpleRequest<BotName>, WithOptionalLanguageCode {
override fun method(): String = "getMyName" override fun method(): String = "getMyName"
override val resultDeserializer: DeserializationStrategy<BotName> override val resultDeserializer: DeserializationStrategy<BotName>

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
@@ -12,8 +13,8 @@ import kotlinx.serialization.builtins.serializer
@Serializable @Serializable
class GetMyShortDescription( class GetMyShortDescription(
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : SimpleRequest<BotShortDescription>, WithOptionalLanguageCode { ) : SimpleRequest<BotShortDescription>, WithOptionalLanguageCode {
override fun method(): String = "getMyShortDescription" override fun method(): String = "getMyShortDescription"
override val resultDeserializer: DeserializationStrategy<BotShortDescription> override val resultDeserializer: DeserializationStrategy<BotShortDescription>

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.commands.* import dev.inmo.tgbotapi.types.commands.*
import kotlinx.serialization.* import kotlinx.serialization.*
@@ -15,8 +16,8 @@ class SetMyCommands(
@Serializable(BotCommandScopeSerializer::class) @Serializable(BotCommandScopeSerializer::class)
override val scope: BotCommandScope = BotCommandScopeDefault, override val scope: BotCommandScope = BotCommandScopeDefault,
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : MyCommandsRequest<Boolean> { ) : MyCommandsRequest<Boolean> {
override fun method(): String = "setMyCommands" override fun method(): String = "setMyCommands"
override val resultDeserializer: DeserializationStrategy<Boolean> override val resultDeserializer: DeserializationStrategy<Boolean>
@@ -31,7 +32,7 @@ class SetMyCommands(
) : this( ) : this(
commands, commands,
scope, scope,
languageCode ?.let(::IetfLanguageCode) languageCode ?.let(::IetfLang)
) )
init { init {

View File

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
@@ -14,8 +14,8 @@ class SetMyDescription(
@SerialName(descriptionField) @SerialName(descriptionField)
val description: String? = null, val description: String? = null,
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : SimpleRequest<Boolean>, WithOptionalLanguageCode { ) : SimpleRequest<Boolean>, WithOptionalLanguageCode {
override fun method(): String = "setMyDescription" override fun method(): String = "setMyDescription"
override val resultDeserializer: DeserializationStrategy<Boolean> override val resultDeserializer: DeserializationStrategy<Boolean>

View File

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
@@ -14,8 +14,8 @@ class SetMyName(
@SerialName(nameField) @SerialName(nameField)
val name: String? = null, val name: String? = null,
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : SimpleRequest<Boolean>, WithOptionalLanguageCode { ) : SimpleRequest<Boolean>, WithOptionalLanguageCode {
override fun method(): String = "setMyName" override fun method(): String = "setMyName"
override val resultDeserializer: DeserializationStrategy<Boolean> override val resultDeserializer: DeserializationStrategy<Boolean>

View File

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.requests.bot package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
@@ -14,8 +14,8 @@ class SetMyShortDescription(
@SerialName(shortDescriptionField) @SerialName(shortDescriptionField)
val shortDescription: String? = null, val shortDescription: String? = null,
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null override val ietfLanguageCode: IetfLang? = null
) : SimpleRequest<Boolean>, WithOptionalLanguageCode { ) : SimpleRequest<Boolean>, WithOptionalLanguageCode {
override fun method(): String = "setMyShortDescription" override fun method(): String = "setMyShortDescription"
override val resultDeserializer: DeserializationStrategy<Boolean> override val resultDeserializer: DeserializationStrategy<Boolean>

View File

@@ -1,9 +1,9 @@
package dev.inmo.tgbotapi.types.abstracts package dev.inmo.tgbotapi.types.abstracts
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
interface WithOptionalLanguageCode { interface WithOptionalLanguageCode {
val ietfLanguageCode: IetfLanguageCode? val ietfLanguageCode: IetfLang?
val languageCode: String? val languageCode: String?
get() = ietfLanguageCode ?.code get() = ietfLanguageCode ?.code

View File

@@ -4,51 +4,51 @@ import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface UsernameChat : Chat { sealed interface UsernameChat : Chat {
val username: Username? val username: Username?
} }
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface PrivateChat : Chat, UsernameChat { sealed interface PrivateChat : Chat, UsernameChat {
override val id: UserId override val id: UserId
val firstName: String val firstName: String
val lastName: String val lastName: String
} }
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface PublicChat : Chat { sealed interface PublicChat : Chat {
val title: String val title: String
} }
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface SuperPublicChat : PublicChat, UsernameChat sealed interface SuperPublicChat : PublicChat, UsernameChat
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface ChannelChat : SuperPublicChat { sealed interface ChannelChat : SuperPublicChat {
override val id: ChatId override val id: ChatId
} }
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface GroupChat : PublicChat sealed interface GroupChat : PublicChat
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface SupergroupChat : GroupChat, SuperPublicChat sealed interface SupergroupChat : GroupChat, SuperPublicChat
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface ForumChat : SupergroupChat sealed interface ForumChat : SupergroupChat
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface PossiblyPremiumChat : Chat { sealed interface PossiblyPremiumChat : Chat {
val isPremium: Boolean val isPremium: Boolean
} }
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
sealed interface AbleToAddInAttachmentMenuChat : Chat { sealed interface AbleToAddInAttachmentMenuChat : Chat {
val addedToAttachmentMenu: Boolean val addedToAttachmentMenu: Boolean
} }
@Serializable(PreviewChatSerializer::class) @Serializable(ChatSerializer::class)
@ClassCastsIncluded(excludeRegex = ".*Impl") @ClassCastsIncluded(excludeRegex = ".*Impl")
sealed interface Chat { sealed interface Chat {
val id: IdChatIdentifier val id: IdChatIdentifier

View File

@@ -49,13 +49,54 @@ object ChatTypeSerializer : KSerializer<ChatType> {
} }
@RiskFeature @RiskFeature
object PreviewChatSerializer : KSerializer<Chat> { object ChatSerializer : KSerializer<Chat> {
@OptIn(InternalSerializationApi::class) @OptIn(InternalSerializationApi::class)
override val descriptor: SerialDescriptor = buildSerialDescriptor("PreviewChatSerializer", PolymorphicKind.OPEN) override val descriptor: SerialDescriptor = buildSerialDescriptor("PreviewChatSerializer", PolymorphicKind.OPEN)
override fun deserialize(decoder: Decoder): Chat { override fun deserialize(decoder: Decoder): Chat {
val decodedJson = JsonObject.serializer().deserialize(decoder) val decodedJson = JsonObject.serializer().deserialize(decoder)
return try {
formatter.decodeFromJsonElement(ExtendedChatSerializer, decodedJson)
} catch (e: SerializationException) {
val type = decodedJson[typeField] ?.jsonPrimitive ?.content ?.asChatType ?: error("Field $typeField must be presented, but absent in $decodedJson")
val isForum = decodedJson[isForumField] ?.jsonPrimitive ?.booleanOrNull == true
when (type) {
ChatType.PrivateChatType -> formatter.decodeFromJsonElement(PrivateChatImpl.serializer(), decodedJson)
ChatType.GroupChatType -> formatter.decodeFromJsonElement(GroupChatImpl.serializer(), decodedJson)
ChatType.SupergroupChatType -> if (isForum) {
formatter.decodeFromJsonElement(ForumChatImpl.serializer(), decodedJson)
} else {
formatter.decodeFromJsonElement(SupergroupChatImpl.serializer(), decodedJson)
}
ChatType.ChannelChatType -> formatter.decodeFromJsonElement(ChannelChatImpl.serializer(), decodedJson)
is ChatType.UnknownChatType -> UnknownChatType(
formatter.decodeFromJsonElement(Long.serializer(), decodedJson[chatIdField] ?: JsonPrimitive(-1)).toChatId(),
decodedJson.toString(),
decodedJson
)
}
}
}
override fun serialize(encoder: Encoder, value: Chat) {
when (value) {
is ExtendedChat -> ExtendedChatSerializer.serialize(encoder, value)
is PreviewChat -> PreviewChatSerializer.serialize(encoder, value)
is ExtendedBot -> ExtendedBot.serializer().serialize(encoder, value)
}
}
}
@RiskFeature
object PreviewChatSerializer : KSerializer<PreviewChat> {
@OptIn(InternalSerializationApi::class)
override val descriptor: SerialDescriptor = buildSerialDescriptor("PreviewChatSerializer", PolymorphicKind.OPEN)
override fun deserialize(decoder: Decoder): PreviewChat {
val decodedJson = JsonObject.serializer().deserialize(decoder)
val type = decodedJson[typeField] ?.jsonPrimitive ?.content ?.asChatType ?: error("Field $typeField must be presented, but absent in $decodedJson") val type = decodedJson[typeField] ?.jsonPrimitive ?.content ?.asChatType ?: error("Field $typeField must be presented, but absent in $decodedJson")
val isForum = decodedJson[isForumField] ?.jsonPrimitive ?.booleanOrNull == true val isForum = decodedJson[isForumField] ?.jsonPrimitive ?.booleanOrNull == true
@@ -76,16 +117,14 @@ object PreviewChatSerializer : KSerializer<Chat> {
} }
} }
override fun serialize(encoder: Encoder, value: Chat) { override fun serialize(encoder: Encoder, value: PreviewChat) {
when (value) { when (value) {
is ExtendedChat -> ExtendedChatSerializer.serialize(encoder, value)
is PrivateChatImpl -> PrivateChatImpl.serializer().serialize(encoder, value) is PrivateChatImpl -> PrivateChatImpl.serializer().serialize(encoder, value)
is GroupChatImpl -> GroupChatImpl.serializer().serialize(encoder, value) is GroupChatImpl -> GroupChatImpl.serializer().serialize(encoder, value)
is SupergroupChatImpl -> SupergroupChatImpl.serializer().serialize(encoder, value) is SupergroupChatImpl -> SupergroupChatImpl.serializer().serialize(encoder, value)
is ForumChatImpl -> ForumChatImpl.serializer().serialize(encoder, value) is ForumChatImpl -> ForumChatImpl.serializer().serialize(encoder, value)
is ChannelChatImpl -> ChannelChatImpl.serializer().serialize(encoder, value) is ChannelChatImpl -> ChannelChatImpl.serializer().serialize(encoder, value)
is CommonBot -> CommonBot.serializer().serialize(encoder, value) is CommonBot -> CommonBot.serializer().serialize(encoder, value)
is ExtendedBot -> ExtendedBot.serializer().serialize(encoder, value)
is CommonUser -> CommonUser.serializer().serialize(encoder, value) is CommonUser -> CommonUser.serializer().serialize(encoder, value)
is UnknownChatType -> JsonObject.serializer().serialize(encoder, value.rawJson) is UnknownChatType -> JsonObject.serializer().serialize(encoder, value.rawJson)
} }
@@ -128,6 +167,7 @@ sealed class ExtendedChatSerializer : KSerializer<ExtendedChat> {
is ExtendedSupergroupChatImpl -> ExtendedSupergroupChatImpl.serializer().serialize(encoder, value) is ExtendedSupergroupChatImpl -> ExtendedSupergroupChatImpl.serializer().serialize(encoder, value)
is ExtendedForumChatImpl -> ExtendedForumChatImpl.serializer().serialize(encoder, value) is ExtendedForumChatImpl -> ExtendedForumChatImpl.serializer().serialize(encoder, value)
is ExtendedChannelChatImpl -> ExtendedChannelChatImpl.serializer().serialize(encoder, value) is ExtendedChannelChatImpl -> ExtendedChannelChatImpl.serializer().serialize(encoder, value)
is ExtendedBot -> ExtendedBot.serializer().serialize(encoder, value)
is UnknownExtendedChat -> JsonObject.serializer().serialize(encoder, value.rawJson) is UnknownExtendedChat -> JsonObject.serializer().serialize(encoder, value.rawJson)
} }
} }

View File

@@ -184,8 +184,10 @@ data class ExtendedBot(
@SerialName(canReadAllGroupMessagesField) @SerialName(canReadAllGroupMessagesField)
val canReadAllGroupMessages: Boolean = false, val canReadAllGroupMessages: Boolean = false,
@SerialName(supportInlineQueriesField) @SerialName(supportInlineQueriesField)
val supportsInlineQueries: Boolean = false val supportsInlineQueries: Boolean = false,
) : Bot() { @SerialName(photoField)
override val chatPhoto: ChatPhoto? = null
) : Bot(), ExtendedChat {
@SerialName(isBotField) @SerialName(isBotField)
private val isBot = true private val isBot = true
} }

View File

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.types.chat package dev.inmo.tgbotapi.types.chat
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer import dev.inmo.micro_utils.language_codes.IetfLangSerializer
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.RiskFeature
@@ -15,7 +15,7 @@ data class GroupChatImpl(
override val id: ChatId, override val id: ChatId,
@SerialName(titleField) @SerialName(titleField)
override val title: String override val title: String
) : GroupChat ) : PreviewGroupChat
@Serializable @Serializable
@RiskFeature("This class is a subject of changes. It is better to use PrivateChat due") @RiskFeature("This class is a subject of changes. It is better to use PrivateChat due")
@@ -28,7 +28,7 @@ data class PrivateChatImpl(
override val firstName: String = "", override val firstName: String = "",
@SerialName(lastNameField) @SerialName(lastNameField)
override val lastName: String = "" override val lastName: String = ""
) : PrivateChat ) : PreviewPrivateChat
@Serializable @Serializable
@RiskFeature("This class is a subject of changes. It is better to use SupergroupChat due") @RiskFeature("This class is a subject of changes. It is better to use SupergroupChat due")
@@ -39,7 +39,7 @@ data class SupergroupChatImpl(
override val title: String, override val title: String,
@SerialName(usernameField) @SerialName(usernameField)
override val username: Username? = null override val username: Username? = null
) : SupergroupChat ) : PreviewSupergroupChat
@Serializable @Serializable
@RiskFeature("This class is a subject of changes. It is better to use ForumChat due") @RiskFeature("This class is a subject of changes. It is better to use ForumChat due")
@@ -50,7 +50,7 @@ data class ForumChatImpl(
override val title: String, override val title: String,
@SerialName(usernameField) @SerialName(usernameField)
override val username: Username? = null override val username: Username? = null
) : ForumChat ) : PreviewForumChat
@Serializable @Serializable
@RiskFeature("This class is a subject of changes. It is better to use ChannelChat due") @RiskFeature("This class is a subject of changes. It is better to use ChannelChat due")
@@ -61,14 +61,20 @@ data class ChannelChatImpl(
override val title: String, override val title: String,
@SerialName(usernameField) @SerialName(usernameField)
override val username: Username? = null override val username: Username? = null
) : ChannelChat ) : PreviewChannelChat
@Serializable(UserSerializer::class) @Serializable(UserSerializer::class)
sealed class User : PrivateChat sealed class User : PrivateChat
@Serializable(UserSerializer::class)
sealed class PreviewUser : PreviewPrivateChat, User()
@Serializable(UserSerializer::class) @Serializable(UserSerializer::class)
sealed class Bot : User() sealed class Bot : User()
@Serializable(UserSerializer::class)
sealed class PreviewBot : PreviewUser()
@Serializable @Serializable
data class CommonBot( data class CommonBot(
override val id: UserId, override val id: UserId,
@@ -78,7 +84,7 @@ data class CommonBot(
override val lastName: String = "", override val lastName: String = "",
@SerialName(usernameField) @SerialName(usernameField)
override val username: Username? = null, override val username: Username? = null,
) : Bot() { ) : PreviewBot() {
@SerialName(isBotField) @SerialName(isBotField)
private val isBot = true private val isBot = true
} }
@@ -93,18 +99,18 @@ data class CommonUser(
@SerialName(usernameField) @SerialName(usernameField)
override val username: Username? = null, override val username: Username? = null,
@SerialName(languageCodeField) @SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class) @Serializable(IetfLangSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null, override val ietfLanguageCode: IetfLang? = null,
@SerialName(isPremiumField) @SerialName(isPremiumField)
override val isPremium: Boolean = false, override val isPremium: Boolean = false,
@SerialName(addedToAttachmentMenuField) @SerialName(addedToAttachmentMenuField)
override val addedToAttachmentMenu: Boolean = false override val addedToAttachmentMenu: Boolean = false
) : User(), WithOptionalLanguageCode, PossiblyPremiumChat, AbleToAddInAttachmentMenuChat { ) : PreviewUser(), WithOptionalLanguageCode, PossiblyPremiumChat, AbleToAddInAttachmentMenuChat {
constructor( constructor(
id: UserId, id: UserId,
firstName: String, firstName: String,
lastName: String = "", lastName: String = "",
username: Username? = null, username: Username? = null,
languageCode: String languageCode: String
) : this(id, firstName, lastName, username, IetfLanguageCode(languageCode)) ) : this(id, firstName, lastName, username, IetfLang(languageCode))
} }

View File

@@ -0,0 +1,30 @@
package dev.inmo.tgbotapi.types.chat
import kotlinx.serialization.Serializable
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewChat : Chat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewUsernameChat : PreviewChat, UsernameChat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewPrivateChat : PreviewUsernameChat, PrivateChat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewPublicChat : PreviewChat, PublicChat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewSuperPublicChat : PreviewPublicChat, PreviewUsernameChat, SuperPublicChat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewChannelChat : PreviewSuperPublicChat, ChannelChat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewGroupChat : PreviewPublicChat, GroupChat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewSupergroupChat : PreviewGroupChat, PreviewSuperPublicChat, SupergroupChat
@Serializable(PreviewChatSerializer::class)
sealed interface PreviewForumChat : PreviewSupergroupChat, ForumChat

View File

@@ -7,4 +7,4 @@ data class UnknownChatType(
override val id: IdChatIdentifier, override val id: IdChatIdentifier,
val raw: String, val raw: String,
val rawJson: JsonObject val rawJson: JsonObject
) : Chat ) : Chat, PreviewChat

View File

@@ -1,9 +1,10 @@
package dev.inmo.tgbotapi.types.chat.member package dev.inmo.tgbotapi.types.chat.member
import dev.inmo.tgbotapi.abstracts.WithChat import dev.inmo.tgbotapi.abstracts.WithPreviewChat
import dev.inmo.tgbotapi.abstracts.WithUser import dev.inmo.tgbotapi.abstracts.WithUser
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -11,7 +12,7 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class ChatMemberUpdated( data class ChatMemberUpdated(
@SerialName(chatField) @SerialName(chatField)
override val chat: Chat, override val chat: PreviewChat,
@SerialName(fromField) @SerialName(fromField)
override val user: User, override val user: User,
@SerialName(dateField) @SerialName(dateField)
@@ -24,4 +25,4 @@ data class ChatMemberUpdated(
val inviteLink: ChatInviteLink? = null, val inviteLink: ChatInviteLink? = null,
@SerialName(viaChatFolderInviteLinkField) @SerialName(viaChatFolderInviteLinkField)
val viaChatFolderInviteLink: Boolean? = false val viaChatFolderInviteLink: Boolean? = false
) : WithChat, WithUser ) : WithPreviewChat, WithUser

View File

@@ -5,12 +5,13 @@ import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.ChannelChat import dev.inmo.tgbotapi.types.chat.ChannelChat
import dev.inmo.tgbotapi.types.chat.CommonBot import dev.inmo.tgbotapi.types.chat.CommonBot
import dev.inmo.tgbotapi.types.chat.PreviewChannelChat
import dev.inmo.tgbotapi.types.message.abstracts.* import dev.inmo.tgbotapi.types.message.abstracts.*
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
data class ChannelContentMessageImpl<T: MessageContent>( data class ChannelContentMessageImpl<T: MessageContent>(
override val messageId: MessageId, override val messageId: MessageId,
override val chat: ChannelChat, override val chat: PreviewChannelChat,
override val content: T, override val content: T,
override val date: DateTime, override val date: DateTime,
override val editDate: DateTime?, override val editDate: DateTime?,

View File

@@ -3,12 +3,13 @@ package dev.inmo.tgbotapi.types.message
import korlibs.time.DateTime import korlibs.time.DateTime
import dev.inmo.tgbotapi.types.MessageId import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.chat.ChannelChat import dev.inmo.tgbotapi.types.chat.ChannelChat
import dev.inmo.tgbotapi.types.chat.PreviewChannelChat
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.ChannelEvent import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.ChannelEvent
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
data class ChannelEventMessage<T : ChannelEvent>( data class ChannelEventMessage<T : ChannelEvent>(
override val messageId: MessageId, override val messageId: MessageId,
override val chat: ChannelChat, override val chat: PreviewChannelChat,
override val chatEvent: T, override val chatEvent: T,
override val date: DateTime override val date: DateTime
) : ChatEventMessage<T> ) : ChatEventMessage<T>

View File

@@ -4,13 +4,14 @@ import korlibs.time.DateTime
import dev.inmo.tgbotapi.types.MessageId import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.chat.GroupChat import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.chat.PreviewGroupChat
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.GroupEvent import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.GroupEvent
import dev.inmo.tgbotapi.types.message.abstracts.GroupEventMessage import dev.inmo.tgbotapi.types.message.abstracts.GroupEventMessage
data class CommonGroupEventMessage<T : GroupEvent>( data class CommonGroupEventMessage<T : GroupEvent>(
override val messageId: MessageId, override val messageId: MessageId,
override val from: User, override val from: User,
override val chat: GroupChat, override val chat: PreviewGroupChat,
override val chatEvent: T, override val chatEvent: T,
override val date: DateTime override val date: DateTime
) : GroupEventMessage<T> ) : GroupEventMessage<T>

View File

@@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.types.message
import korlibs.time.DateTime import korlibs.time.DateTime
import dev.inmo.tgbotapi.types.MessageId import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.chat.PreviewSupergroupChat
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.chat.SupergroupChat import dev.inmo.tgbotapi.types.chat.SupergroupChat
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.SupergroupEvent import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.SupergroupEvent
@@ -10,7 +11,7 @@ import dev.inmo.tgbotapi.types.message.abstracts.SupergroupEventMessage
data class CommonSupergroupEventMessage<T : SupergroupEvent>( data class CommonSupergroupEventMessage<T : SupergroupEvent>(
override val messageId: MessageId, override val messageId: MessageId,
override val from: User, override val from: User,
override val chat: SupergroupChat, override val chat: PreviewSupergroupChat,
override val chatEvent: T, override val chatEvent: T,
override val date: DateTime override val date: DateTime
) : SupergroupEventMessage<T> ) : SupergroupEventMessage<T>

View File

@@ -10,8 +10,8 @@ import dev.inmo.tgbotapi.types.message.abstracts.*
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
data class ConnectedFromChannelGroupContentMessageImpl<T : MessageContent>( data class ConnectedFromChannelGroupContentMessageImpl<T : MessageContent>(
override val chat: GroupChat, override val chat: PreviewGroupChat,
override val channel: ChannelChat, override val channel: PreviewChannelChat,
override val messageId: MessageId, override val messageId: MessageId,
override val date: DateTime, override val date: DateTime,
override val forwardInfo: ForwardInfo?, override val forwardInfo: ForwardInfo?,
@@ -26,8 +26,8 @@ data class ConnectedFromChannelGroupContentMessageImpl<T : MessageContent>(
) : ConnectedFromChannelGroupContentMessage<T> ) : ConnectedFromChannelGroupContentMessage<T>
data class UnconnectedFromChannelGroupContentMessageImpl<T: MessageContent>( data class UnconnectedFromChannelGroupContentMessageImpl<T: MessageContent>(
override val chat: GroupChat, override val chat: PreviewGroupChat,
override val channel: ChannelChat, override val channel: PreviewChannelChat,
override val messageId: MessageId, override val messageId: MessageId,
override val date: DateTime, override val date: DateTime,
override val forwardInfo: ForwardInfo?, override val forwardInfo: ForwardInfo?,
@@ -42,7 +42,7 @@ data class UnconnectedFromChannelGroupContentMessageImpl<T: MessageContent>(
) : UnconnectedFromChannelGroupContentMessage<T> ) : UnconnectedFromChannelGroupContentMessage<T>
data class AnonymousGroupContentMessageImpl<T : MessageContent>( data class AnonymousGroupContentMessageImpl<T : MessageContent>(
override val chat: GroupChat, override val chat: PreviewGroupChat,
override val messageId: MessageId, override val messageId: MessageId,
override val date: DateTime, override val date: DateTime,
override val forwardInfo: ForwardInfo?, override val forwardInfo: ForwardInfo?,
@@ -57,7 +57,7 @@ data class AnonymousGroupContentMessageImpl<T : MessageContent>(
) : AnonymousGroupContentMessage<T> ) : AnonymousGroupContentMessage<T>
data class CommonGroupContentMessageImpl<T : MessageContent>( data class CommonGroupContentMessageImpl<T : MessageContent>(
override val chat: GroupChat, override val chat: PreviewGroupChat,
override val messageId: MessageId, override val messageId: MessageId,
override val from: User, override val from: User,
override val date: DateTime, override val date: DateTime,
@@ -72,8 +72,8 @@ data class CommonGroupContentMessageImpl<T : MessageContent>(
) : CommonGroupContentMessage<T> ) : CommonGroupContentMessage<T>
data class FromChannelForumContentMessageImpl<T: MessageContent>( data class FromChannelForumContentMessageImpl<T: MessageContent>(
override val chat: ForumChat, override val chat: PreviewForumChat,
override val channel: ChannelChat, override val channel: PreviewChannelChat,
override val messageId: MessageId, override val messageId: MessageId,
override val threadId: MessageThreadId, override val threadId: MessageThreadId,
override val date: DateTime, override val date: DateTime,
@@ -89,7 +89,7 @@ data class FromChannelForumContentMessageImpl<T: MessageContent>(
) : FromChannelForumContentMessage<T> ) : FromChannelForumContentMessage<T>
data class AnonymousForumContentMessageImpl<T : MessageContent>( data class AnonymousForumContentMessageImpl<T : MessageContent>(
override val chat: ForumChat, override val chat: PreviewForumChat,
override val messageId: MessageId, override val messageId: MessageId,
override val threadId: MessageThreadId, override val threadId: MessageThreadId,
override val date: DateTime, override val date: DateTime,
@@ -105,7 +105,7 @@ data class AnonymousForumContentMessageImpl<T : MessageContent>(
) : AnonymousForumContentMessage<T> ) : AnonymousForumContentMessage<T>
data class CommonForumContentMessageImpl<T : MessageContent>( data class CommonForumContentMessageImpl<T : MessageContent>(
override val chat: ForumChat, override val chat: PreviewForumChat,
override val messageId: MessageId, override val messageId: MessageId,
override val threadId: MessageThreadId, override val threadId: MessageThreadId,
override val from: User, override val from: User,

View File

@@ -4,13 +4,14 @@ import korlibs.time.DateTime
import dev.inmo.tgbotapi.types.MessageId import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.message.abstracts.FromUserMessage import dev.inmo.tgbotapi.types.message.abstracts.FromUserMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.passport.PassportData import dev.inmo.tgbotapi.types.passport.PassportData
data class PassportMessage( data class PassportMessage(
override val messageId: MessageId, override val messageId: MessageId,
override val chat: Chat, override val chat: PreviewChat,
override val from: User, override val from: User,
override val date: DateTime, override val date: DateTime,
val passportData: PassportData val passportData: PassportData

View File

@@ -13,7 +13,7 @@ import dev.inmo.tgbotapi.types.message.content.MessageContent
data class PrivateContentMessageImpl<T: MessageContent>( data class PrivateContentMessageImpl<T: MessageContent>(
override val messageId: MessageId, override val messageId: MessageId,
override val from: User, override val from: User,
override val chat: Chat, override val chat: PreviewPrivateChat,
override val content: T, override val content: T,
override val date: DateTime, override val date: DateTime,
override val editDate: DateTime?, override val editDate: DateTime?,

View File

@@ -2,13 +2,14 @@ package dev.inmo.tgbotapi.types.message
import korlibs.time.DateTime import korlibs.time.DateTime
import dev.inmo.tgbotapi.types.MessageId import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.chat.PreviewPrivateChat
import dev.inmo.tgbotapi.types.chat.PrivateChat import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.PrivateEvent import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.PrivateEvent
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
data class PrivateEventMessage<T : PrivateEvent>( data class PrivateEventMessage<T : PrivateEvent>(
override val messageId: MessageId, override val messageId: MessageId,
override val chat: PrivateChat, override val chat: PreviewPrivateChat,
override val chatEvent: T, override val chatEvent: T,
override val date: DateTime override val date: DateTime
) : ChatEventMessage<T> ) : ChatEventMessage<T>

View File

@@ -43,11 +43,11 @@ internal data class RawMessage(
@SerialName(messageIdField) @SerialName(messageIdField)
val messageId: MessageId, val messageId: MessageId,
val date: TelegramDate, val date: TelegramDate,
private val chat: Chat, private val chat: PreviewChat,
@SerialName(messageThreadIdField) @SerialName(messageThreadIdField)
private val messageThreadId: MessageThreadId? = null, private val messageThreadId: MessageThreadId? = null,
private val from: User? = null, private val from: User? = null,
private val sender_chat: PublicChat? = null, private val sender_chat: PreviewPublicChat? = null,
private val forward_from: User? = null, private val forward_from: User? = null,
private val forward_from_chat: Chat? = null, private val forward_from_chat: Chat? = null,
private val forward_from_message_id: MessageId? = null, private val forward_from_message_id: MessageId? = null,
@@ -274,27 +274,27 @@ internal data class RawMessage(
try { try {
chatEvent?.let { chatEvent -> chatEvent?.let { chatEvent ->
when (chat) { when (chat) {
is SupergroupChat -> CommonSupergroupEventMessage( is PreviewSupergroupChat -> CommonSupergroupEventMessage(
messageId, messageId,
from ?: error("Supergroup events are expected to contain 'from' field"), from ?: error("Supergroup events are expected to contain 'from' field"),
chat, chat,
chatEvent as? SupergroupEvent ?: throwWrongChatEvent(SupergroupEvent::class, chatEvent), chatEvent as? SupergroupEvent ?: throwWrongChatEvent(SupergroupEvent::class, chatEvent),
date.asDate date.asDate
) )
is GroupChat -> CommonGroupEventMessage( is PreviewGroupChat -> CommonGroupEventMessage(
messageId, messageId,
from ?: error("Supergroup events are expected to contain 'from' field"), from ?: error("Supergroup events are expected to contain 'from' field"),
chat, chat,
chatEvent as? GroupEvent ?: throwWrongChatEvent(GroupChat::class, chatEvent), chatEvent as? GroupEvent ?: throwWrongChatEvent(GroupChat::class, chatEvent),
date.asDate date.asDate
) )
is ChannelChat -> ChannelEventMessage( is PreviewChannelChat -> ChannelEventMessage(
messageId, messageId,
chat, chat,
chatEvent as? ChannelEvent ?: throwWrongChatEvent(ChannelEvent::class, chatEvent), chatEvent as? ChannelEvent ?: throwWrongChatEvent(ChannelEvent::class, chatEvent),
date.asDate date.asDate
) )
is PrivateChat -> PrivateEventMessage( is PreviewPrivateChat -> PrivateEventMessage(
messageId, messageId,
chat, chat,
chatEvent as? PrivateEvent ?: throwWrongChatEvent(PrivateEvent::class, chatEvent), chatEvent as? PrivateEvent ?: throwWrongChatEvent(PrivateEvent::class, chatEvent),
@@ -303,8 +303,8 @@ internal data class RawMessage(
else -> error("Expected one of the public chats, but was $chat (in extracting of chat event message)") else -> error("Expected one of the public chats, but was $chat (in extracting of chat event message)")
} }
} ?: content?.let { content -> when (chat) { } ?: content?.let { content -> when (chat) {
is PublicChat -> when (chat) { is PreviewPublicChat -> when (chat) {
is ChannelChat -> ChannelContentMessageImpl( is PreviewChannelChat -> ChannelContentMessageImpl(
messageId, messageId,
chat, chat,
content, content,
@@ -318,17 +318,16 @@ internal data class RawMessage(
author_signature, author_signature,
media_group_id media_group_id
) )
is ForumChat -> if (messageThreadId != null) { is PreviewForumChat -> if (messageThreadId != null) {
val chatId = ChatIdWithThreadId( val chatId = ChatIdWithThreadId(
chat.id.chatId, chat.id.chatId,
messageThreadId messageThreadId
) )
val actualForumChat = when (chat) { val actualForumChat = when (chat) {
is ExtendedForumChatImpl -> chat.copy(id = chatId)
is ForumChatImpl -> chat.copy(id = chatId) is ForumChatImpl -> chat.copy(id = chatId)
} }
when (sender_chat) { when (sender_chat) {
is ChannelChat -> FromChannelForumContentMessageImpl( is PreviewChannelChat -> FromChannelForumContentMessageImpl(
actualForumChat, actualForumChat,
sender_chat, sender_chat,
messageId, messageId,
@@ -344,7 +343,7 @@ internal data class RawMessage(
author_signature, author_signature,
media_group_id media_group_id
) )
is GroupChat -> AnonymousForumContentMessageImpl( is PreviewGroupChat -> AnonymousForumContentMessageImpl(
actualForumChat, actualForumChat,
messageId, messageId,
messageThreadId, messageThreadId,
@@ -377,7 +376,7 @@ internal data class RawMessage(
} }
} else { } else {
when (sender_chat) { when (sender_chat) {
is ChannelChat -> if (is_automatic_forward == true) { is PreviewChannelChat -> if (is_automatic_forward == true) {
ConnectedFromChannelGroupContentMessageImpl( ConnectedFromChannelGroupContentMessageImpl(
chat, chat,
sender_chat, sender_chat,
@@ -440,8 +439,8 @@ internal data class RawMessage(
) )
} }
} }
is GroupChat -> when (sender_chat) { is PreviewGroupChat -> when (sender_chat) {
is ChannelChat -> if (is_automatic_forward == true) { is PreviewChannelChat -> if (is_automatic_forward == true) {
ConnectedFromChannelGroupContentMessageImpl( ConnectedFromChannelGroupContentMessageImpl(
chat, chat,
sender_chat, sender_chat,
@@ -474,7 +473,7 @@ internal data class RawMessage(
media_group_id media_group_id
) )
} }
is GroupChat -> AnonymousGroupContentMessageImpl( is PreviewGroupChat -> AnonymousGroupContentMessageImpl(
chat, chat,
messageId, messageId,
date.asDate, date.asDate,
@@ -504,7 +503,7 @@ internal data class RawMessage(
) )
} }
} }
is PrivateChat -> PrivateContentMessageImpl( is PreviewPrivateChat -> PrivateContentMessageImpl(
messageId, messageId,
from ?: error("Was detected common message, but owner (sender) of the message was not found"), from ?: error("Was detected common message, but owner (sender) of the message was not found"),
chat, chat,

View File

@@ -1,10 +1,11 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.chat.ChannelChat import dev.inmo.tgbotapi.types.chat.ChannelChat
import dev.inmo.tgbotapi.types.chat.PreviewChannelChat
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
interface ChannelContentMessage<T: MessageContent> : PossiblySentViaBotCommonMessage<T>, SignedMessage, WithSenderChatMessage { interface ChannelContentMessage<T: MessageContent> : PossiblySentViaBotCommonMessage<T>, SignedMessage, WithSenderChatMessage {
override val chat: ChannelChat override val chat: PreviewChannelChat
override val senderChat: ChannelChat override val senderChat: PreviewChannelChat
get() = chat get() = chat
} }

View File

@@ -1,5 +1,9 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.chat.PreviewGroupChat
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.GroupEvent import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.GroupEvent
interface GroupEventMessage<T : GroupEvent> : ChatEventMessage<T>, FromUserMessage interface GroupEventMessage<T : GroupEvent> : ChatEventMessage<T>, FromUserMessage {
override val chat: PreviewGroupChat
}

View File

@@ -1,24 +1,22 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.MessageThreadId import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.chat.ChannelChat import dev.inmo.tgbotapi.types.chat.*
import dev.inmo.tgbotapi.types.chat.ForumChat
import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
sealed interface GroupContentMessage<T : MessageContent> : PublicContentMessage<T> { sealed interface GroupContentMessage<T : MessageContent> : PublicContentMessage<T> {
override val chat: GroupChat override val chat: PreviewGroupChat
} }
sealed interface ForumContentMessage<T : MessageContent> : GroupContentMessage<T>, PossiblyTopicMessage { sealed interface ForumContentMessage<T : MessageContent> : GroupContentMessage<T>, PossiblyTopicMessage {
override val chat: ForumChat override val chat: PreviewForumChat
override val threadId: MessageThreadId override val threadId: MessageThreadId
} }
sealed interface FromChannelGroupContentMessage<T : MessageContent> : GroupContentMessage<T>, SignedMessage, WithSenderChatMessage { sealed interface FromChannelGroupContentMessage<T : MessageContent> : GroupContentMessage<T>, SignedMessage, WithSenderChatMessage {
val channel: ChannelChat val channel: PreviewChannelChat
override val senderChat: ChannelChat override val senderChat: PreviewChannelChat
get() = channel get() = channel
} }
@@ -26,7 +24,7 @@ interface ConnectedFromChannelGroupContentMessage<T: MessageContent> : FromChann
interface UnconnectedFromChannelGroupContentMessage<T: MessageContent> : FromChannelGroupContentMessage<T> interface UnconnectedFromChannelGroupContentMessage<T: MessageContent> : FromChannelGroupContentMessage<T>
interface AnonymousGroupContentMessage<T : MessageContent> : GroupContentMessage<T>, SignedMessage, WithSenderChatMessage { interface AnonymousGroupContentMessage<T : MessageContent> : GroupContentMessage<T>, SignedMessage, WithSenderChatMessage {
override val senderChat: GroupChat override val senderChat: PreviewGroupChat
get() = chat get() = chat
} }
@@ -35,7 +33,7 @@ interface CommonGroupContentMessage<T : MessageContent> : GroupContentMessage<T>
interface FromChannelForumContentMessage<T: MessageContent> : FromChannelGroupContentMessage<T>, ForumContentMessage<T> interface FromChannelForumContentMessage<T: MessageContent> : FromChannelGroupContentMessage<T>, ForumContentMessage<T>
interface AnonymousForumContentMessage<T : MessageContent> : ForumContentMessage<T>, SignedMessage, WithSenderChatMessage { interface AnonymousForumContentMessage<T : MessageContent> : ForumContentMessage<T>, SignedMessage, WithSenderChatMessage {
override val senderChat: GroupChat override val senderChat: PreviewGroupChat
get() = chat get() = chat
} }

View File

@@ -1,10 +1,11 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import korlibs.time.DateTime import korlibs.time.DateTime
import dev.inmo.tgbotapi.abstracts.WithChat import dev.inmo.tgbotapi.abstracts.WithPreviewChat
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import dev.inmo.tgbotapi.types.MessageId import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.message.RawMessage import dev.inmo.tgbotapi.types.message.RawMessage
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.descriptors.* import kotlinx.serialization.descriptors.*
@@ -12,14 +13,14 @@ import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
@ClassCastsIncluded(excludeRegex = ".*Impl") @ClassCastsIncluded(excludeRegex = ".*Impl")
interface Message : WithChat { interface Message : WithPreviewChat {
val messageId: MessageId val messageId: MessageId
val date: DateTime val date: DateTime
} }
data class UnknownMessageType( data class UnknownMessageType(
override val messageId: MessageId, override val messageId: MessageId,
override val chat: Chat, override val chat: PreviewChat,
override val date: DateTime, override val date: DateTime,
val insideException: Exception val insideException: Exception
) : Message ) : Message

View File

@@ -1,5 +1,9 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.chat.PreviewPrivateChat
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
interface PrivateContentMessage<T: MessageContent> : PossiblySentViaBotCommonMessage<T>, FromUserMessage interface PrivateContentMessage<T: MessageContent> : PossiblySentViaBotCommonMessage<T>, FromUserMessage {
override val chat: PreviewPrivateChat
}

View File

@@ -1,8 +1,9 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.chat.PreviewPublicChat
import dev.inmo.tgbotapi.types.chat.PublicChat import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
sealed interface PublicContentMessage<T: MessageContent> : PossiblySentViaBotCommonMessage<T> { sealed interface PublicContentMessage<T: MessageContent> : PossiblySentViaBotCommonMessage<T> {
override val chat: PublicChat override val chat: PreviewPublicChat
} }

View File

@@ -1,5 +1,9 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.chat.PreviewGroupChat
import dev.inmo.tgbotapi.types.chat.PreviewSupergroupChat
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.SupergroupEvent import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.SupergroupEvent
interface SupergroupEventMessage<T : SupergroupEvent> : GroupEventMessage<T> interface SupergroupEventMessage<T : SupergroupEvent> : GroupEventMessage<T> {
override val chat: PreviewSupergroupChat
}

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.types.message.abstracts package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.chat.PreviewChat
interface WithSenderChatMessage { interface WithSenderChatMessage {
val senderChat: Chat val senderChat: PreviewChat
} }

View File

@@ -7,7 +7,9 @@ import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.MessageThreadId import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.abstracts.PossiblyForwardedMessage
import dev.inmo.tgbotapi.types.stories.Story import dev.inmo.tgbotapi.types.stories.Story
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -25,7 +27,7 @@ data class StoryContent(
replyToMessageId: MessageId?, replyToMessageId: MessageId?,
allowSendingWithoutReply: Boolean?, allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup? replyMarkup: KeyboardMarkup?
): Request<out Message> { ): Request<PossiblyForwardedMessage> {
return ForwardMessage( return ForwardMessage(
chat.id, chat.id,
toChatId = chatId, toChatId = chatId,

View File

@@ -10,6 +10,7 @@ import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.VoiceFile import dev.inmo.tgbotapi.types.files.VoiceFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.threadId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@@ -27,16 +28,16 @@ data class VoiceContent(
allowSendingWithoutReply: Boolean?, allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup? replyMarkup: KeyboardMarkup?
): Request<ContentMessage<VoiceContent>> = SendVoice( ): Request<ContentMessage<VoiceContent>> = SendVoice(
chatId, chatId = chatId,
media.fileId, voice = media.fileId,
textSources, entities = textSources,
media.duration, threadId = messageThreadId,
messageThreadId, duration = media.duration,
disableNotification, disableNotification = disableNotification,
protectContent, protectContent = protectContent,
replyToMessageId, replyToMessageId = replyToMessageId,
allowSendingWithoutReply, allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup replyMarkup = replyMarkup
) )
override fun asTelegramMedia(): TelegramMediaAudio = TelegramMediaAudio( override fun asTelegramMedia(): TelegramMediaAudio = TelegramMediaAudio(

View File

@@ -0,0 +1,11 @@
package dev.inmo.tgbotapi.utils
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.TagLogger
/**
* Default realization of [KSLog] which will be used everywhere where there is no some custom variant of [KSLog]
*
* By default, uses [TagLogger] with tag `KTgBot` (which in fact falling back to [KSLog.default] with `KTgBot` default tag)
*/
var DefaultKTgBotAPIKSLog: KSLog = TagLogger("KTgBot")

View File

@@ -1,8 +1,8 @@
package dev.inmo.tgbotapi.types package dev.inmo.tgbotapi.types
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
import java.util.* import java.util.*
fun IetfLanguageCode?.javaLocale() = this ?.code ?.let { Locale.forLanguageTag(it) } fun IetfLang?.javaLocale() = this ?.code ?.let { Locale.forLanguageTag(it) }
fun WithOptionalLanguageCode?.javaLocale() = this ?.ietfLanguageCode ?.javaLocale() fun WithOptionalLanguageCode?.javaLocale() = this ?.ietfLanguageCode ?.javaLocale()

View File

@@ -0,0 +1 @@
package dev.inmo.tgbotapi

View File

@@ -0,0 +1,5 @@
package dev.inmo.tgbotapi.bot.ktor
import dev.inmo.tgbotapi.bot.ktor.base.MultipleClientKtorRequestsExecutor
actual typealias KtorRequestsExecutor = MultipleClientKtorRequestsExecutor

View File

@@ -0,0 +1,22 @@
package dev.inmo.tgbotapi.bot.ktor.base
import io.ktor.client.*
import io.ktor.client.engine.cio.*
/**
* This function is used in default constructor of [MultipleClientKtorRequestsExecutor] and on all non-native
* platforms and MingwX64 should return [client]
*
* On LinuxX64 it will create copy with Curl engine or throw an exception if engine is different with Curl
*
* @throws IllegalArgumentException When pass non Curl-based [HttpClient] on LinuxX64
*/
internal actual inline fun platformClientCopy(client: HttpClient): HttpClient = (client.engineConfig as? CIOEngineConfig) ?.let {
lateinit var config: HttpClientConfig<out CIOEngineConfig>
client.config {
config = this as HttpClientConfig<out CIOEngineConfig>
}.close()
HttpClient(CIO) {
this.plusAssign(config)
}
} ?: throw IllegalArgumentException("On LinuxX64 TelegramBotAPI currently support only Curl Ktor HttpClient engine")

View File

@@ -0,0 +1,9 @@
package dev.inmo.tgbotapi.requests.abstracts
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.ktor.common.input
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
actual fun MPPFile.asMultipartFile(): MultipartFile = MultipartFile(this.name) {
input()
}

View File

@@ -0,0 +1,7 @@
package dev.inmo.tgbotapi.utils
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.Input
import io.ktor.utils.io.readRemaining
actual suspend fun ByteReadChannel.asInput(): Input = readRemaining()

View File

@@ -0,0 +1,12 @@
package dev.inmo.tgbotapi.utils
import kotlinx.serialization.Serializable
//actual typealias MimeType = MimeType
@OptIn(RiskFeature::class)
@Serializable(MimeTypeSerializer::class)
actual data class MimeType(
actual val raw: String
)
internal actual fun createMimeType(raw: String): MimeType = MimeType(raw)

View File

@@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable
//actual typealias MimeType = MimeType //actual typealias MimeType = MimeType
@OptIn(RiskFeature::class)
@Serializable(MimeTypeSerializer::class) @Serializable(MimeTypeSerializer::class)
actual data class MimeType( actual data class MimeType(
actual val raw: String actual val raw: String

View File

@@ -24,6 +24,7 @@ class TelegramBotAPISymbolProcessor(
private val outputFile: String = "Output", private val outputFile: String = "Output",
private val outputFolder: String? = null private val outputFolder: String? = null
) : SymbolProcessor { ) : SymbolProcessor {
@OptIn(RiskFeature::class)
private val classCastsIncludedClassName = ClassCastsIncluded::class.asClassName() private val classCastsIncludedClassName = ClassCastsIncluded::class.asClassName()
@OptIn(KspExperimental::class, RiskFeature::class) @OptIn(KspExperimental::class, RiskFeature::class)
override fun process(resolver: Resolver): List<KSAnnotated> { override fun process(resolver: Resolver): List<KSAnnotated> {

View File

@@ -13,7 +13,7 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api project(":tgbotapi.core") api project(path: ":tgbotapi.core")
} }
} }
} }

View File

@@ -126,6 +126,17 @@ import dev.inmo.tgbotapi.types.chat.ExtendedSupergroupChat
import dev.inmo.tgbotapi.types.chat.ForumChat import dev.inmo.tgbotapi.types.chat.ForumChat
import dev.inmo.tgbotapi.types.chat.GroupChat import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.chat.PossiblyPremiumChat import dev.inmo.tgbotapi.types.chat.PossiblyPremiumChat
import dev.inmo.tgbotapi.types.chat.PreviewBot
import dev.inmo.tgbotapi.types.chat.PreviewChannelChat
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.chat.PreviewForumChat
import dev.inmo.tgbotapi.types.chat.PreviewGroupChat
import dev.inmo.tgbotapi.types.chat.PreviewPrivateChat
import dev.inmo.tgbotapi.types.chat.PreviewPublicChat
import dev.inmo.tgbotapi.types.chat.PreviewSuperPublicChat
import dev.inmo.tgbotapi.types.chat.PreviewSupergroupChat
import dev.inmo.tgbotapi.types.chat.PreviewUser
import dev.inmo.tgbotapi.types.chat.PreviewUsernameChat
import dev.inmo.tgbotapi.types.chat.PrivateChat import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.chat.PublicChat import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.chat.SuperPublicChat import dev.inmo.tgbotapi.types.chat.SuperPublicChat
@@ -1969,12 +1980,30 @@ public inline fun Chat.userOrThrow(): User = this as dev.inmo.tgbotapi.types.cha
public inline fun <T> Chat.ifUser(block: (User) -> T): T? = userOrNull() ?.let(block) public inline fun <T> Chat.ifUser(block: (User) -> T): T? = userOrNull() ?.let(block)
public inline fun Chat.previewUserOrNull(): PreviewUser? = this as?
dev.inmo.tgbotapi.types.chat.PreviewUser
public inline fun Chat.previewUserOrThrow(): PreviewUser = this as
dev.inmo.tgbotapi.types.chat.PreviewUser
public inline fun <T> Chat.ifPreviewUser(block: (PreviewUser) -> T): T? = previewUserOrNull()
?.let(block)
public inline fun Chat.botOrNull(): Bot? = this as? dev.inmo.tgbotapi.types.chat.Bot public inline fun Chat.botOrNull(): Bot? = this as? dev.inmo.tgbotapi.types.chat.Bot
public inline fun Chat.botOrThrow(): Bot = this as dev.inmo.tgbotapi.types.chat.Bot public inline fun Chat.botOrThrow(): Bot = this as dev.inmo.tgbotapi.types.chat.Bot
public inline fun <T> Chat.ifBot(block: (Bot) -> T): T? = botOrNull() ?.let(block) public inline fun <T> Chat.ifBot(block: (Bot) -> T): T? = botOrNull() ?.let(block)
public inline fun Chat.previewBotOrNull(): PreviewBot? = this as?
dev.inmo.tgbotapi.types.chat.PreviewBot
public inline fun Chat.previewBotOrThrow(): PreviewBot = this as
dev.inmo.tgbotapi.types.chat.PreviewBot
public inline fun <T> Chat.ifPreviewBot(block: (PreviewBot) -> T): T? = previewBotOrNull()
?.let(block)
public inline fun Chat.commonBotOrNull(): CommonBot? = this as? public inline fun Chat.commonBotOrNull(): CommonBot? = this as?
dev.inmo.tgbotapi.types.chat.CommonBot dev.inmo.tgbotapi.types.chat.CommonBot
@@ -1992,6 +2021,87 @@ public inline fun Chat.commonUserOrThrow(): CommonUser = this as
public inline fun <T> Chat.ifCommonUser(block: (CommonUser) -> T): T? = commonUserOrNull() public inline fun <T> Chat.ifCommonUser(block: (CommonUser) -> T): T? = commonUserOrNull()
?.let(block) ?.let(block)
public inline fun Chat.previewChatOrNull(): PreviewChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewChat
public inline fun Chat.previewChatOrThrow(): PreviewChat = this as
dev.inmo.tgbotapi.types.chat.PreviewChat
public inline fun <T> Chat.ifPreviewChat(block: (PreviewChat) -> T): T? = previewChatOrNull()
?.let(block)
public inline fun Chat.previewUsernameChatOrNull(): PreviewUsernameChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewUsernameChat
public inline fun Chat.previewUsernameChatOrThrow(): PreviewUsernameChat = this as
dev.inmo.tgbotapi.types.chat.PreviewUsernameChat
public inline fun <T> Chat.ifPreviewUsernameChat(block: (PreviewUsernameChat) -> T): T? =
previewUsernameChatOrNull() ?.let(block)
public inline fun Chat.previewPrivateChatOrNull(): PreviewPrivateChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewPrivateChat
public inline fun Chat.previewPrivateChatOrThrow(): PreviewPrivateChat = this as
dev.inmo.tgbotapi.types.chat.PreviewPrivateChat
public inline fun <T> Chat.ifPreviewPrivateChat(block: (PreviewPrivateChat) -> T): T? =
previewPrivateChatOrNull() ?.let(block)
public inline fun Chat.previewPublicChatOrNull(): PreviewPublicChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewPublicChat
public inline fun Chat.previewPublicChatOrThrow(): PreviewPublicChat = this as
dev.inmo.tgbotapi.types.chat.PreviewPublicChat
public inline fun <T> Chat.ifPreviewPublicChat(block: (PreviewPublicChat) -> T): T? =
previewPublicChatOrNull() ?.let(block)
public inline fun Chat.previewSuperPublicChatOrNull(): PreviewSuperPublicChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewSuperPublicChat
public inline fun Chat.previewSuperPublicChatOrThrow(): PreviewSuperPublicChat = this as
dev.inmo.tgbotapi.types.chat.PreviewSuperPublicChat
public inline fun <T> Chat.ifPreviewSuperPublicChat(block: (PreviewSuperPublicChat) -> T): T? =
previewSuperPublicChatOrNull() ?.let(block)
public inline fun Chat.previewChannelChatOrNull(): PreviewChannelChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewChannelChat
public inline fun Chat.previewChannelChatOrThrow(): PreviewChannelChat = this as
dev.inmo.tgbotapi.types.chat.PreviewChannelChat
public inline fun <T> Chat.ifPreviewChannelChat(block: (PreviewChannelChat) -> T): T? =
previewChannelChatOrNull() ?.let(block)
public inline fun Chat.previewGroupChatOrNull(): PreviewGroupChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewGroupChat
public inline fun Chat.previewGroupChatOrThrow(): PreviewGroupChat = this as
dev.inmo.tgbotapi.types.chat.PreviewGroupChat
public inline fun <T> Chat.ifPreviewGroupChat(block: (PreviewGroupChat) -> T): T? =
previewGroupChatOrNull() ?.let(block)
public inline fun Chat.previewSupergroupChatOrNull(): PreviewSupergroupChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewSupergroupChat
public inline fun Chat.previewSupergroupChatOrThrow(): PreviewSupergroupChat = this as
dev.inmo.tgbotapi.types.chat.PreviewSupergroupChat
public inline fun <T> Chat.ifPreviewSupergroupChat(block: (PreviewSupergroupChat) -> T): T? =
previewSupergroupChatOrNull() ?.let(block)
public inline fun Chat.previewForumChatOrNull(): PreviewForumChat? = this as?
dev.inmo.tgbotapi.types.chat.PreviewForumChat
public inline fun Chat.previewForumChatOrThrow(): PreviewForumChat = this as
dev.inmo.tgbotapi.types.chat.PreviewForumChat
public inline fun <T> Chat.ifPreviewForumChat(block: (PreviewForumChat) -> T): T? =
previewForumChatOrNull() ?.let(block)
public inline fun Chat.unknownChatTypeOrNull(): UnknownChatType? = this as? public inline fun Chat.unknownChatTypeOrNull(): UnknownChatType? = this as?
dev.inmo.tgbotapi.types.chat.UnknownChatType dev.inmo.tgbotapi.types.chat.UnknownChatType

View File

@@ -0,0 +1,247 @@
package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.TextedWithTextSources
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSource
object TelegramBotCommandsDefaults {
const val defaultArgsSeparator = " "
val defaultArgsSeparatorRegex = Regex(defaultArgsSeparator)
const val defaultNamesArgsSeparator = "="
val defaultNamesArgsSeparatorRegex = Regex(defaultNamesArgsSeparator)
}
@Deprecated(message = "Replaced", replaceWith = ReplaceWith("TelegramBotCommandsDefaults.defaultArgsSeparatorRegex", "dev.inmo.tgbotapi.extensions.utils.extensions.TelegramBotCommandsDefaults"))
val defaultArgsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun List<TextSource>.parseCommandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
): MutableMap<String, Array<String>> {
val result = mutableMapOf<String, Array<String>>()
var currentBotCommandSource: BotCommandTextSource? = null
var currentArgs = ""
fun includeCurrent() = currentBotCommandSource?.let {
currentArgs = currentArgs.trim()
result[it.command] = if (currentArgs.isNotEmpty()) {
currentArgs.split(argsSeparator).toTypedArray()
} else {
emptyArray()
}
currentArgs = ""
currentBotCommandSource = null
}
for (textSource in this) {
if (textSource is BotCommandTextSource) {
includeCurrent()
currentBotCommandSource = textSource
} else {
currentArgs += textSource.source
}
}
includeCurrent()
return result
}
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun TextedWithTextSources.parseCommandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = textSources?.parseCommandsWithArgs(argsSeparator) ?: emptyMap()
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun ContentMessage<TextContent>.parseCommandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = content.parseCommandsWithArgs(argsSeparator)
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun List<TextSource>.parseCommandsWithArgs(
argsSeparator: String
): MutableMap<String, Array<String>> = parseCommandsWithArgs(Regex(argsSeparator))
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun TextedWithTextSources.parseCommandsWithArgs(
argsSeparator: String
) = parseCommandsWithArgs(Regex(argsSeparator))
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun ContentMessage<TextContent>.parseCommandsWithArgs(
argsSeparator: String
) = parseCommandsWithArgs(Regex(argsSeparator))
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun List<TextSource>.parseCommandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Map<String, List<Pair<String, String>>> {
val withArgs = parseCommandsWithArgs(argsSeparator)
return withArgs.mapValues { (k, v) ->
v.flatMap {
it.split(nameArgSeparator, 2).map { v -> it to v }
}
}
}
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun TextedWithTextSources.parseCommandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = textSources?.parseCommandsWithNamedArgs(argsSeparator = argsSeparator, nameArgSeparator = nameArgSeparator) ?: emptyMap()
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun ContentMessage<TextContent>.parseCommandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = content.parseCommandsWithNamedArgs(argsSeparator = argsSeparator, nameArgSeparator = nameArgSeparator)
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun List<TextSource>.parseCommandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Map<String, List<Pair<String, String>>> = parseCommandsWithNamedArgs(Regex(pattern = argsSeparator), nameArgSeparator)
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun TextedWithTextSources.parseCommandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = parseCommandsWithNamedArgs(argsSeparator = Regex(pattern = argsSeparator), nameArgSeparator = nameArgSeparator)
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun ContentMessage<TextContent>.parseCommandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = parseCommandsWithNamedArgs(argsSeparator = Regex(pattern = argsSeparator), nameArgSeparator = nameArgSeparator)
// Deprecations
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
@Deprecated("Renamed", ReplaceWith("parseCommandsWithArgs(argsSeparator)", "dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs"))
fun List<TextSource>.parseCommandsWithParams(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
): MutableMap<String, Array<String>> = parseCommandsWithArgs(argsSeparator)
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
@Deprecated("Renamed", ReplaceWith("parseCommandsWithArgs(argsSeparator)", "dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs"))
fun TextedWithTextSources.parseCommandsWithParams(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = parseCommandsWithArgs(argsSeparator)
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
@Deprecated("Renamed", ReplaceWith("parseCommandsWithArgs(argsSeparator)", "dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs"))
fun ContentMessage<TextContent>.parseCommandsWithParams(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = parseCommandsWithArgs(argsSeparator)

View File

@@ -0,0 +1,53 @@
package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.TextedWithTextSources
import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.ifBotCommandTextSource
import dev.inmo.tgbotapi.extensions.utils.whenBotCommandTextSource
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSource
// Sources
/**
* Parse text sources to find commands with their arguments. This method will skip all the text sources __before__
* first command and all following text sources until the next command will be guessed as an args of last found command
*/
fun List<TextSource>.parseCommandsWithArgsSources(): Map<BotCommandTextSource, Array<TextSource>> {
var currentCommandTextSource: BotCommandTextSource? = null
val currentArgs = mutableListOf<TextSource>()
val result = mutableMapOf<BotCommandTextSource, Array<TextSource>>()
fun addCurrentCommandToResult() {
currentCommandTextSource ?.let {
result[it] = currentArgs.toTypedArray()
currentArgs.clear()
}
}
forEach {
it.whenBotCommandTextSource {
addCurrentCommandToResult()
currentCommandTextSource = it
return@forEach
}
currentArgs.add(it)
}
addCurrentCommandToResult()
return result
}
/**
* Parse text sources to find commands with their arguments. This method will skip all the text sources __before__
* first command and all following text sources until the next command will be guessed as an args of last found command
*/
fun TextedWithTextSources.parseCommandsWithArgsSources() = textSources?.parseCommandsWithArgsSources() ?: emptyMap()
/**
* Parse text sources to find commands with their arguments. This method will skip all the text sources __before__
* first command and all following text sources until the next command will be guessed as an args of last found command
*/
fun ContentMessage<TextContent>.parseCommandsWithArgsSources() = content.parseCommandsWithArgsSources()

View File

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.extensions.utils.extensions package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.FromUser import dev.inmo.tgbotapi.abstracts.FromUser
import dev.inmo.tgbotapi.abstracts.WithChat import dev.inmo.tgbotapi.abstracts.WithPreviewChat
import dev.inmo.tgbotapi.types.ChatIdentifier import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.UserId import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
@@ -15,13 +15,13 @@ import kotlinx.coroutines.flow.transform
* Will pass only those [T] which have [sameChat] as [chatId] * Will pass only those [T] which have [sameChat] as [chatId]
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <T : WithChat> Flow<T>.fromChat(chatId: ChatIdentifier): Flow<T> = filter { it.sameChat(chatId) } inline fun <T : WithPreviewChat> Flow<T>.fromChat(chatId: ChatIdentifier): Flow<T> = filter { it.sameChat(chatId) }
/** /**
* Will pass only those [T] which have [sameChat] as [chatId] * Will pass only those [T] which have [sameChat] as [chatId]
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <T : WithChat> Flow<T>.fromChat(chat: Chat): Flow<T> = fromChat(chat.id) inline fun <T : WithPreviewChat> Flow<T>.fromChat(chat: Chat): Flow<T> = fromChat(chat.id)
/** /**
* @return [Flow] with the [FromUser.user] field [User.id] the same as [userId] * @return [Flow] with the [FromUser.user] field [User.id] the same as [userId]

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.extensions.utils.extensions package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.WithChat import dev.inmo.tgbotapi.abstracts.WithPreviewChat
import dev.inmo.tgbotapi.extensions.utils.usernameChatOrNull import dev.inmo.tgbotapi.extensions.utils.usernameChatOrNull
import dev.inmo.tgbotapi.extensions.utils.whenUsernameChat import dev.inmo.tgbotapi.extensions.utils.whenUsernameChat
import dev.inmo.tgbotapi.types.ChatIdentifier import dev.inmo.tgbotapi.types.ChatIdentifier
@@ -16,21 +16,23 @@ import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
* @return true in case if [this] message is placed in the chat with id == [chatId] * @return true in case if [this] message is placed in the chat with id == [chatId]
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun WithChat.sameChat(chatId: ChatIdentifier) = chat.id == chatId || (chatId is Username && chat.whenUsernameChat { inline fun WithPreviewChat.sameChat(chatId: ChatIdentifier) =
it.username == chatId chat.id == chatId || (chatId is Username && chat.whenUsernameChat {
} ?: false) it.username == chatId
} ?: false)
/** /**
* @return true in case if [this] message is placed in the [chat] * @return true in case if [this] message is placed in the [chat]
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun WithChat.sameChat(chat: Chat) = sameChat(chat.id) || chat.usernameChatOrNull() ?.username ?.let { sameChat(it) } ?: false inline fun WithPreviewChat.sameChat(chat: Chat) =
sameChat(chat.id) || chat.usernameChatOrNull()?.username?.let { sameChat(it) } ?: false
/** /**
* @return true in case if [this] message is placed in the same chat that [other] * @return true in case if [this] message is placed in the same chat that [other]
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun WithChat.sameChat(other: Message) = sameChat(other.chat) inline fun WithPreviewChat.sameChat(other: Message) = sameChat(other.chat)
/** /**
* @return true in case if [this] message is from the same chat (with id == [chatId]) and [this] [Message.messageId] * @return true in case if [this] message is from the same chat (with id == [chatId]) and [this] [Message.messageId]

View File

@@ -1,54 +0,0 @@
package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.TextedWithTextSources
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSource
val defaultArgsSeparator = Regex(" ")
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun List<TextSource>.parseCommandsWithParams(
argsSeparator: Regex = defaultArgsSeparator
): MutableMap<String, Array<String>> {
val result = mutableMapOf<String, Array<String>>()
var currentBotCommandSource: BotCommandTextSource? = null
var currentArgs = ""
fun includeCurrent() = currentBotCommandSource ?.let {
currentArgs = currentArgs.trim()
result[it.command] = if (currentArgs.isNotEmpty()) {
currentArgs.split(argsSeparator).toTypedArray()
} else {
emptyArray()
}
currentArgs = ""
currentBotCommandSource = null
}
for (textSource in this) {
if (textSource is BotCommandTextSource) {
includeCurrent()
currentBotCommandSource = textSource
} else {
currentArgs += textSource.source
}
}
includeCurrent()
return result
}
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun TextedWithTextSources.parseCommandsWithParams(
argsSeparator: Regex = defaultArgsSeparator
) = textSources ?.parseCommandsWithParams(argsSeparator) ?: emptyMap()
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun ContentMessage<TextContent>.parseCommandsWithParams(
argsSeparator: Regex = defaultArgsSeparator
) = content.parseCommandsWithParams(argsSeparator)

View File

@@ -7,6 +7,7 @@ import dev.inmo.tgbotapi.types.message.abstracts.PossiblySentViaBotCommonMessage
import dev.inmo.tgbotapi.types.message.content.MediaGroupPartContent import dev.inmo.tgbotapi.types.message.content.MediaGroupPartContent
import dev.inmo.tgbotapi.types.update.* import dev.inmo.tgbotapi.types.update.*
import dev.inmo.tgbotapi.types.update.abstracts.* import dev.inmo.tgbotapi.types.update.abstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.extensions.asMediaGroupMessage import dev.inmo.tgbotapi.utils.extensions.asMediaGroupMessage
/** /**
@@ -22,6 +23,7 @@ fun List<Update>.lastUpdateIdentifier(): UpdateIdentifier? {
* Will convert incoming list of [Update]s to list with [Update]s, which include [dev.inmo.tgbotapi.types.message.abstracts.ContentMessage]s * Will convert incoming list of [Update]s to list with [Update]s, which include [dev.inmo.tgbotapi.types.message.abstracts.ContentMessage]s
* with [dev.inmo.tgbotapi.types.message.content.MediaGroupContent] * with [dev.inmo.tgbotapi.types.message.content.MediaGroupContent]
*/ */
@OptIn(RiskFeature::class)
fun List<Update>.convertWithMediaGroupUpdates(): List<Update> { fun List<Update>.convertWithMediaGroupUpdates(): List<Update> {
val resultUpdates = mutableListOf<Update>() val resultUpdates = mutableListOf<Update>()
val mediaGroups = mutableMapOf<MediaGroupIdentifier, MutableList<Pair<BaseSentMessageUpdate, PossiblySentViaBotCommonMessage<MediaGroupPartContent>>>>() val mediaGroups = mutableMapOf<MediaGroupIdentifier, MutableList<Pair<BaseSentMessageUpdate, PossiblySentViaBotCommonMessage<MediaGroupPartContent>>>>()

View File

@@ -114,9 +114,6 @@ fun TelegramBot.longPollingFlow(
if (e is RequestException) { if (e is RequestException) {
delay(1000L) delay(1000L)
} }
if (e is GetUpdatesConflict && (exceptionsHandler == null || exceptionsHandler == defaultSafelyExceptionHandler)) {
println("Warning!!! Other bot with the same bot token requests updates with getUpdate in parallel")
}
} }
) { ) {
execute( execute(

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.webapps package dev.inmo.tgbotapi.webapps
import dev.inmo.micro_utils.language_codes.IetfLanguageCode import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.CommonBot import dev.inmo.tgbotapi.types.chat.CommonBot
import dev.inmo.tgbotapi.types.chat.CommonUser import dev.inmo.tgbotapi.types.chat.CommonUser
@@ -42,7 +42,7 @@ fun WebAppUser.asUser() = if (isBot == true) {
firstName = firstName, firstName = firstName,
lastName = lastName ?: "", lastName = lastName ?: "",
username = username ?.let(::Username), username = username ?.let(::Username),
ietfLanguageCode = languageCode ?.let(::IetfLanguageCode), ietfLanguageCode = languageCode ?.let(::IetfLang),
isPremium = isPremium isPremium = isPremium
) )
} }