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

Compare commits

..

78 Commits

Author SHA1 Message Date
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
284fe58848 upfix of chatmember statuses serialization 2023-09-29 22:38:22 +06:00
38c9732da5 update ktor version 2023-09-29 22:17:42 +06:00
dee13c03ae fix serialization/deserialization of chat member statuses 2023-09-29 22:16:10 +06:00
6103b70a47 start 9.2.1 2023-09-29 22:15:38 +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
7f5cd0567b Merge pull request #789 from InsanusMokrassar/9.2.0
9.2.0
2023-09-25 23:27:06 +06:00
af159b89b6 update changelog and readme 2023-09-25 23:26:05 +06:00
efe286c181 improvements and fixes in web apps api 2023-09-25 23:21:48 +06:00
821bb5b45c improve cloud storage 2023-09-25 20:23:37 +06:00
c92ed92f7c add opportunity to create Hex from rgb 2023-09-25 16:26:27 +06:00
88f6b349ea fixes in onEvent/onWriteAccessRequested/onContactRequested 2023-09-25 14:14:04 +06:00
6c4afac8f8 small refactor of chat member 2023-09-24 21:50:32 +06:00
6273990b00 fix in chat member deserializing 2023-09-24 21:33:26 +06:00
1a1fd926dd temporarily change request id main type to ushort 2023-09-24 21:12:48 +06:00
affa2a3a57 ChatAdministratorRightsImpl -> ChatCommonAdministratorRights 2023-09-24 20:11:58 +06:00
73a748d8b3 ChatAdministratorRights -> ChatAdministratorRightsImpl in reply keyboards 2023-09-24 20:08:46 +06:00
fa4d264df4 add preview callbacks support 2023-09-23 00:40:32 +06:00
1b1da33882 add CloudStorage support 2023-09-23 00:15:58 +06:00
ccfb774f33 update promoteChatMember 2023-09-22 23:33:56 +06:00
5215e8a315 add support of stories rights 2023-09-22 22:53:14 +06:00
97a3901cb9 improvements of WriteAccessAllowed 2023-09-22 21:55:22 +06:00
034e87a8ef add support of several new things in bot api 2023-09-22 21:45:28 +06:00
156fbd72d4 start 9.2.0 2023-09-22 21:10:21 +06:00
8ec3e01737 Update build.gradle 2023-09-02 09:36:11 +06:00
1655bedabe Merge pull request #788 from InsanusMokrassar/9.1.2
9.1.2
2023-09-01 03:52:37 +06:00
108 changed files with 2344 additions and 378 deletions

View File

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

View File

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

View File

@@ -7,9 +7,9 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
java-version: 17
- 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
run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"

View File

@@ -1,5 +1,63 @@
# TelegramBotAPI changelog
## 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
* `Version`:
* `Ktor`: `2.3.3` -> `2.3.4`
* `Core`:
* All `ChatMember` inheritors have fixes `status` field
## 9.2.0
**Add support of [Telegram Bots API 6.9](https://core.telegram.org/bots/api-changelog#september-22-2023)**
* Rename `ChatAdministratorRightsImpl` -> `ChatCommonAdministratorRights`
* All the request chat keyboards has changed their parameters `ChatAdministratorRights` to `ChatCommonAdministratorRights`
## 9.1.2
* `Core`:

View File

@@ -1,4 +1,4 @@
# TelegramBotAPI [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-6.8-blue)](https://core.telegram.org/bots/api-changelog#august-18-2023)
# TelegramBotAPI [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-6.9-blue)](https://core.telegram.org/bots/api-changelog#september-22-2023)
| Docs | [![KDocs](https://img.shields.io/static/v1?label=Dokka&message=KDocs&color=blue&logo=kotlin)](https://tgbotapi.inmo.dev/index.html) [![Mini tutorial](https://img.shields.io/static/v1?label=Mk&message=Docs&color=blue&logo=mkdocs)](https://docs.inmo.dev/tgbotapi/index.html) |
|:----------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|

View File

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

View File

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

View File

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

View File

@@ -1,22 +1,25 @@
[versions]
kotlin = "1.8.22"
kotlin-serialization = "1.5.1"
kotlin = "1.9.21"
kotlin-serialization = "1.6.1"
kotlin-coroutines = "1.7.3"
javax-activation = "1.1.1"
korlibs = "4.0.3"
uuid = "0.7.1"
ktor = "2.3.3"
korlibs = "4.0.10"
uuid = "0.8.1"
ktor = "2.3.6"
ksp = "1.8.22-1.0.11"
kotlin-poet = "1.14.2"
ksp = "1.9.20-1.0.14"
kotlin-poet = "1.15.1"
microutils = "0.19.9"
microutils = "0.20.15"
kslog = "1.3.1"
versions = "0.50.0"
github-release-plugin = "2.4.1"
dokka = "1.8.20"
dokka = "1.9.10"
[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-fsm-common = { module = "dev.inmo:micro_utils.fsm.common", version.ref = "microutils" }
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
# ksp dependencies
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-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
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
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip

View File

@@ -5,7 +5,7 @@ kotlin {
jvm {
compilations.main {
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
}
}
@@ -15,6 +15,7 @@ kotlin {
}
linuxX64()
mingwX64()
linuxArm64()
sourceSets {
commonMain {
@@ -40,10 +41,18 @@ kotlin {
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 {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

View File

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

@@ -2,17 +2,17 @@ package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.SetMyDefaultAdministratorRights
import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRightsImpl
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
suspend fun TelegramBot.setMyDefaultAdministratorRights(
rights: ChatAdministratorRightsImpl,
rights: ChatCommonAdministratorRights,
forChannels: Boolean? = null
) = execute(SetMyDefaultAdministratorRights(rights, forChannels))
suspend fun TelegramBot.setMyDefaultAdministratorRightsForChannels(
rights: ChatAdministratorRightsImpl
rights: ChatCommonAdministratorRights
) = setMyDefaultAdministratorRights(rights, forChannels = true)
suspend fun TelegramBot.setMyDefaultAdministratorRightsForGroupsAndSupergroups(
rights: ChatAdministratorRightsImpl
rights: ChatCommonAdministratorRights
) = setMyDefaultAdministratorRights(rights, forChannels = false)

View File

@@ -0,0 +1,157 @@
package dev.inmo.tgbotapi.extensions.api.chat.members
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.members.PromoteChannelAdministrator
import dev.inmo.tgbotapi.requests.chat.members.PromoteChatMember
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.chat.User
suspend fun TelegramBot.promoteChannelAdministrator(
chatId: ChatIdentifier,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canPostMessages: Boolean? = null,
canEditMessages: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = execute(
PromoteChannelAdministrator(
chatId = chatId,
userId = userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
)
suspend fun TelegramBot.promoteChannelAdministrator(
chat: PublicChat,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canPostMessages: Boolean? = null,
canEditMessages: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = promoteChannelAdministrator(
chat.id,
userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
suspend fun TelegramBot.promoteChannelAdministrator(
chatId: IdChatIdentifier,
user: User,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canPostMessages: Boolean? = null,
canEditMessages: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = promoteChannelAdministrator(
chatId,
user.id,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
suspend fun TelegramBot.promoteChannelAdministrator(
chat: PublicChat,
user: User,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canPostMessages: Boolean? = null,
canEditMessages: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = promoteChannelAdministrator(
chat.id,
user.id,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)

View File

@@ -0,0 +1,116 @@
package dev.inmo.tgbotapi.extensions.api.chat.members
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.members.PromoteChatMember
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.chat.User
suspend fun TelegramBot.promoteChatAdministrator(
chatId: ChatIdentifier,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
) = execute(
PromoteChatMember(
chatId,
userId,
untilDate,
isAnonymous,
canChangeInfo,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPromoteMembers,
canManageVideoChats,
canManageChat
)
)
suspend fun TelegramBot.promoteChatAdministrator(
chat: PublicChat,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
) = promoteChatAdministrator(
chat.id,
userId,
untilDate,
isAnonymous,
canChangeInfo,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPromoteMembers,
canManageVideoChats,
canManageChat
)
suspend fun TelegramBot.promoteChatAdministrator(
chatId: IdChatIdentifier,
user: User,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
) = promoteChatAdministrator(
chatId,
user.id,
untilDate,
isAnonymous,
canChangeInfo,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPromoteMembers,
canManageVideoChats,
canManageChat
)
suspend fun TelegramBot.promoteChatAdministrator(
chat: PublicChat,
user: User,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
) = promoteChatAdministrator(
chat.id,
user.id,
untilDate,
isAnonymous,
canChangeInfo,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPromoteMembers,
canManageVideoChats,
canManageChat
)

View File

@@ -1,14 +1,13 @@
package dev.inmo.tgbotapi.extensions.api.chat.members
import dev.inmo.micro_utils.common.Warning
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.members.PromoteChatMember
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.chat.User
@Warning("This method is too common. Use it with caution")
suspend fun TelegramBot.promoteChatMember(
chatId: ChatIdentifier,
userId: UserId,
@@ -24,27 +23,34 @@ suspend fun TelegramBot.promoteChatMember(
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null
canManageTopics: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = execute(
PromoteChatMember(
chatId,
userId,
untilDate,
isAnonymous,
canChangeInfo,
canPostMessages,
canEditMessages,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPinMessages,
canPromoteMembers,
canManageVideoChats,
canManageChat,
canManageTopics
chatId = chatId,
userId = userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
)
@Warning("This method is too common. Use it with caution")
suspend fun TelegramBot.promoteChatMember(
chat: PublicChat,
userId: UserId,
@@ -60,25 +66,32 @@ suspend fun TelegramBot.promoteChatMember(
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null
canManageTopics: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = promoteChatMember(
chat.id,
userId,
untilDate,
isAnonymous,
canChangeInfo,
canPostMessages,
canEditMessages,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPinMessages,
canPromoteMembers,
canManageVideoChats,
canManageChat,
canManageTopics
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
@Warning("This method is too common. Use it with caution")
suspend fun TelegramBot.promoteChatMember(
chatId: IdChatIdentifier,
user: User,
@@ -94,25 +107,32 @@ suspend fun TelegramBot.promoteChatMember(
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null
canManageTopics: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = promoteChatMember(
chatId,
user.id,
untilDate,
isAnonymous,
canChangeInfo,
canPostMessages,
canEditMessages,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPinMessages,
canPromoteMembers,
canManageVideoChats,
canManageChat,
canManageTopics
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
@Warning("This method is too common. Use it with caution")
suspend fun TelegramBot.promoteChatMember(
chat: PublicChat,
user: User,
@@ -128,21 +148,27 @@ suspend fun TelegramBot.promoteChatMember(
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null
canManageTopics: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = promoteChatMember(
chat.id,
user.id,
untilDate,
isAnonymous,
canChangeInfo,
canPostMessages,
canEditMessages,
canDeleteMessages,
canInviteUsers,
canRestrictMembers,
canPinMessages,
canPromoteMembers,
canManageVideoChats,
canManageChat,
canManageTopics
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)

View File

@@ -0,0 +1,133 @@
package dev.inmo.tgbotapi.extensions.api.chat.members
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.members.PromoteChatMember
import dev.inmo.tgbotapi.requests.chat.members.PromoteSupergroupAdministrator
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.chat.User
suspend fun TelegramBot.promoteSupergroupAdministrator(
chatId: ChatIdentifier,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPinMessages: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null,
) = execute(
PromoteSupergroupAdministrator(
chatId = chatId,
userId = userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics
)
)
suspend fun TelegramBot.promoteSupergroupAdministrator(
chat: PublicChat,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPinMessages: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null,
) = promoteSupergroupAdministrator(
chat.id,
userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics
)
suspend fun TelegramBot.promoteSupergroupAdministrator(
chatId: IdChatIdentifier,
user: User,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPinMessages: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null,
) = promoteSupergroupAdministrator(
chatId,
user.id,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics
)
suspend fun TelegramBot.promoteSupergroupAdministrator(
chat: PublicChat,
user: User,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPinMessages: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null,
) = promoteSupergroupAdministrator(
chat.id,
user.id,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics
)

View File

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

View File

@@ -13,7 +13,8 @@ kotlin {
sourceSets {
commonMain {
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.utils.handlers_registrar.doWithRegistration
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.types.BotCommand
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 {
var currentCommandTextSource: BotCommandTextSource? = null
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
}
it to it.content.textSources.parseCommandsWithArgsSources().toList()
}
/**
* 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
* be associated with its source message

View File

@@ -174,6 +174,25 @@ suspend fun BehaviourContext.waitWriteAccessAllowed(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<WriteAccessAllowed>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedFromRequest(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<WriteAccessAllowed.FromRequest>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedFromAttachmentMenu(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<WriteAccessAllowed.FromAttachmentMenu>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedFromWebAppLink(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<WriteAccessAllowed.FromWebAppLink>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedOther(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<WriteAccessAllowed.Other>(initRequest, errorFactory)
suspend fun BehaviourContext.waitChatSharedRequest(
initRequest: Request<*>? = null,

View File

@@ -171,6 +171,22 @@ suspend fun BehaviourContext.waitWriteAccessAllowedEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<WriteAccessAllowed>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedFromRequestEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<WriteAccessAllowed.FromRequest>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedFromAttachmentMenuEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<WriteAccessAllowed.FromAttachmentMenu>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedFromWebAppLinkEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<WriteAccessAllowed.FromWebAppLink>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedOtherEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<WriteAccessAllowed.Other>(initRequest, errorFactory)
suspend fun BehaviourContext.waitChatSharedRequestEventsMessages(
initRequest: Request<*>? = null,

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.times
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.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextMessage
@@ -124,6 +126,7 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = command(
commandRegex,
@@ -132,7 +135,7 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
subcontextUpdatesFilter = subcontextUpdatesFilter,
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
commandsWithArgs[key]
} ?: emptyArray()
@@ -144,12 +147,14 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = commandWithArgs(
command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
@@ -158,12 +163,72 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = commandWithArgs(
botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
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
)
@@ -172,21 +237,99 @@ suspend fun <BC : BehaviourContext> BC.onCommandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
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(
command: String,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
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(
botCommand: BotCommand,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
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.times
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.TextMessage
import dev.inmo.tgbotapi.types.update.abstracts.Update
@@ -65,7 +65,7 @@ suspend fun <BC : BehaviourContext> BC.unhandledCommandWithArgs(
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory
) {
val args = it.parseCommandsWithParams().let { commandsWithArgs ->
val args = it.parseCommandsWithArgs().let { commandsWithArgs ->
commandsWithArgs
}
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.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.utils.SimpleFilter
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.times
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.TextMessage
import dev.inmo.tgbotapi.types.message.textsources.RegularTextSource
import dev.inmo.tgbotapi.types.update.abstracts.Update
import io.ktor.http.decodeURLQueryComponent
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.filter
private val startRegex = Regex("start")
suspend fun <BC : BehaviourContext> BC.onDeepLink(

View File

@@ -655,10 +655,90 @@ suspend fun <BC : BehaviourContext> BC.onGeneralForumTopicUnhidden(
* data
*/
suspend fun <BC : BehaviourContext> BC.onWriteAccessAllowed(
initialFilter: SimpleFilter<SupergroupEventMessage<WriteAccessAllowed>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, SupergroupEventMessage<WriteAccessAllowed>, Update>? = MessageFilterByChat,
initialFilter: SimpleFilter<ChatEventMessage<WriteAccessAllowed>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, ChatEventMessage<WriteAccessAllowed>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<WriteAccessAllowed>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, SupergroupEventMessage<WriteAccessAllowed>>
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, ChatEventMessage<WriteAccessAllowed>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
/**
* @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call
* @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example,
* this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage].
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own.
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times]
* to combinate several filters
* @param markerFactory Will be used to identify different "stream". [scenarioReceiver] will be called synchronously
* in one "stream". Output of [markerFactory] will be used as a key for "stream"
* @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that
* data
*/
suspend fun <BC : BehaviourContext> BC.onWriteAccessAllowedFromRequest(
initialFilter: SimpleFilter<ChatEventMessage<WriteAccessAllowed.FromRequest>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, ChatEventMessage<WriteAccessAllowed.FromRequest>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<WriteAccessAllowed.FromRequest>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, ChatEventMessage<WriteAccessAllowed.FromRequest>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
/**
* @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call
* @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example,
* this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage].
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own.
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times]
* to combinate several filters
* @param markerFactory Will be used to identify different "stream". [scenarioReceiver] will be called synchronously
* in one "stream". Output of [markerFactory] will be used as a key for "stream"
* @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that
* data
*/
suspend fun <BC : BehaviourContext> BC.onWriteAccessAllowedFromAttachmentMenu(
initialFilter: SimpleFilter<ChatEventMessage<WriteAccessAllowed.FromAttachmentMenu>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, ChatEventMessage<WriteAccessAllowed.FromAttachmentMenu>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<WriteAccessAllowed.FromAttachmentMenu>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, ChatEventMessage<WriteAccessAllowed.FromAttachmentMenu>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
/**
* @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call
* @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example,
* this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage].
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own.
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times]
* to combinate several filters
* @param markerFactory Will be used to identify different "stream". [scenarioReceiver] will be called synchronously
* in one "stream". Output of [markerFactory] will be used as a key for "stream"
* @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that
* data
*/
suspend fun <BC : BehaviourContext> BC.onWriteAccessAllowedOther(
initialFilter: SimpleFilter<ChatEventMessage<WriteAccessAllowed.Other>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, ChatEventMessage<WriteAccessAllowed.Other>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<WriteAccessAllowed.Other>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, ChatEventMessage<WriteAccessAllowed.Other>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
/**
* @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call
* @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example,
* this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage].
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own.
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times]
* to combinate several filters
* @param markerFactory Will be used to identify different "stream". [scenarioReceiver] will be called synchronously
* in one "stream". Output of [markerFactory] will be used as a key for "stream"
* @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that
* data
*/
suspend fun <BC : BehaviourContext> BC.onWriteAccessAllowedFromWebAppLink(
initialFilter: SimpleFilter<ChatEventMessage<WriteAccessAllowed.FromWebAppLink>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, ChatEventMessage<WriteAccessAllowed.FromWebAppLink>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<WriteAccessAllowed.FromWebAppLink>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, ChatEventMessage<WriteAccessAllowed.FromWebAppLink>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)

View File

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

View File

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

View File

@@ -1,10 +1,17 @@
package dev.inmo.tgbotapi.abstracts
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]
*/
interface WithChat {
val chat: Chat
interface WithPreviewChat {
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
import dev.inmo.kslog.common.KSLog
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
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.nonstrictJsonFormat
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 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,
client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false,
requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
jsonFormatter: Json = nonstrictJsonFormat,
pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor
pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder,
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
import dev.inmo.kslog.common.KSLog
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.ktor.base.*
@@ -10,9 +11,9 @@ import io.ktor.client.HttpClient
import kotlinx.serialization.json.Json
@RiskFeature
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
SimpleRequestCallFactory(),
MultipartRequestCallFactory(),
fun createTelegramBotDefaultKtorCallRequestsFactories(logger: KSLog? = null) = listOf(
SimpleRequestCallFactory(logger),
MultipartRequestCallFactory(logger),
DownloadFileRequestCallFactory,
DownloadFileChannelRequestCallFactory
)
@@ -25,6 +26,8 @@ class KtorRequestsExecutorBuilder(
var excludeDefaultFactories: Boolean = false
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
var jsonFormatter: Json = nonstrictJsonFormat
var logger: KSLog = DefaultKTgBotAPIKSLog
var pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
fun build() = KtorRequestsExecutor(
telegramAPIUrlsKeeper,
@@ -32,7 +35,9 @@ class KtorRequestsExecutorBuilder(
callsFactories,
excludeDefaultFactories,
requestsLimiter,
jsonFormatter
jsonFormatter,
pipelineStepsHolder,
logger
)
}

View File

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

View File

@@ -1,5 +1,9 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.e
import dev.inmo.kslog.common.i
import dev.inmo.kslog.common.v
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.exceptions.BotException
@@ -7,6 +11,7 @@ import dev.inmo.tgbotapi.bot.exceptions.CommonBotException
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutor
import dev.inmo.tgbotapi.bot.ktor.createTelegramBotDefaultKtorCallRequestsFactories
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
@@ -19,19 +24,23 @@ import io.ktor.client.plugins.*
import io.ktor.client.statement.*
import kotlinx.serialization.json.Json
class DefaultKtorRequestsExecutor(
class DefaultKtorRequestsExecutor internal constructor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
private val jsonFormatter: Json = nonstrictJsonFormat,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
client: HttpClient,
callsFactories: List<KtorCallFactory>,
excludeDefaultFactories: Boolean,
private val requestsLimiter: RequestLimiter,
private val jsonFormatter: Json,
private val pipelineStepsHolder: KtorPipelineStepsHolder,
private val logger: KSLog,
diff: Unit
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
if (!excludeDefaultFactories) {
this + createTelegramBotDefaultKtorCallRequestsFactories()
logger.v { "Installing default factories" }
this + createTelegramBotDefaultKtorCallRequestsFactories(logger)
} else {
logger.v { "Default factories will not be installed" }
this
}
}
@@ -44,19 +53,23 @@ class DefaultKtorRequestsExecutor(
override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatchingSafely {
logger.v { "Start request $request" }
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit(request) {
var result: T? = null
lateinit var factoryHandledRequest: KtorCallFactory
for (potentialFactory in callsFactories) {
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
result = potentialFactory.makeCall(
logger.v { "Trying factory $potentialFactory for $request" }
val resultFromFactory = potentialFactory.makeCall(
client,
telegramAPIUrlsKeeper,
request,
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) {
factoryHandledRequest = potentialFactory
break
@@ -69,6 +82,7 @@ class DefaultKtorRequestsExecutor(
}
}.let {
val result = it.exceptionOrNull() ?.let { e ->
logger.v(e) { "Got exception on handling of $request" }
pipelineStepsHolder.onRequestException(request, e) ?.let { return@let it }
when (e) {
@@ -88,9 +102,13 @@ class DefaultKtorRequestsExecutor(
}
is BotException -> e
else -> CommonBotException(cause = e)
}.also {
logger.v(e) { "Result exception on handling of $request: $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.abstracts.Request
import dev.inmo.tgbotapi.utils.ByteReadChannelAllocator
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient
import io.ktor.client.call.receive
@@ -15,6 +16,7 @@ import io.ktor.utils.io.*
import kotlinx.coroutines.*
import kotlinx.serialization.json.Json
@RiskFeature
object DownloadFileChannelRequestCallFactory : KtorCallFactory {
override suspend fun <T : Any> makeCall(
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.requests.DownloadFile
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.readBytes
import kotlinx.serialization.json.Json
@RiskFeature
object DownloadFileRequestCallFactory : KtorCallFactory {
override suspend fun <T : Any> makeCall(
client: HttpClient,

View File

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

View File

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

View File

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

View File

@@ -2,14 +2,14 @@ package dev.inmo.tgbotapi.requests.bot
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRightsImpl
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
class SetMyDefaultAdministratorRights(
@SerialName(rightsField)
val rights: ChatAdministratorRightsImpl,
val rights: ChatCommonAdministratorRights,
@SerialName(forChannelsField)
val forChannels: Boolean? = null
) : SimpleRequest<Boolean> {

View File

@@ -1,5 +1,6 @@
package dev.inmo.tgbotapi.requests.chat.members
import dev.inmo.micro_utils.common.Warning
import dev.inmo.tgbotapi.abstracts.types.UntilDate
import dev.inmo.tgbotapi.requests.chat.abstracts.ChatMemberRequest
import dev.inmo.tgbotapi.types.*
@@ -7,6 +8,7 @@ import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
@Warning("This method is too common. Use it with caution")
data class PromoteChatMember(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@@ -37,7 +39,13 @@ data class PromoteChatMember(
@SerialName(canManageChatField)
private val canManageChat: Boolean? = null,
@SerialName(canManageTopicsField)
private val canManageTopics: Boolean? = null
private val canManageTopics: Boolean? = null,
@SerialName(canPostStoriesField)
private val canPostStories: Boolean? = null,
@SerialName(canEditStoriesField)
private val canEditStories: Boolean? = null,
@SerialName(canDeleteStoriesField)
private val canDeleteStories: Boolean? = null
) : ChatMemberRequest<Boolean>, UntilDate {
override fun method(): String = "promoteChatMember"
override val resultDeserializer: DeserializationStrategy<Boolean>
@@ -45,3 +53,109 @@ data class PromoteChatMember(
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}
fun PromoteChatMember(
chatId: ChatIdentifier,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
) = PromoteChatMember(
chatId = chatId,
userId = userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = null,
canEditMessages = null,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = null,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = null,
canPostStories = null,
canEditStories = null,
canDeleteStories = null
)
fun PromoteChannelAdministrator(
chatId: ChatIdentifier,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canPostMessages: Boolean? = null,
canEditMessages: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canPostStories: Boolean? = null,
canEditStories: Boolean? = null,
canDeleteStories: Boolean? = null
) = PromoteChatMember(
chatId = chatId,
userId = userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = null,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = null,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
fun PromoteSupergroupAdministrator(
chatId: ChatIdentifier,
userId: UserId,
untilDate: TelegramDate? = null,
isAnonymous: Boolean? = null,
canChangeInfo: Boolean? = null,
canDeleteMessages: Boolean? = null,
canInviteUsers: Boolean? = null,
canRestrictMembers: Boolean? = null,
canPinMessages: Boolean? = null,
canPromoteMembers: Boolean? = null,
canManageVideoChats: Boolean? = null,
canManageChat: Boolean? = null,
canManageTopics: Boolean? = null,
) = PromoteChatMember(
chatId = chatId,
userId = userId,
untilDate = untilDate,
isAnonymous = isAnonymous,
canChangeInfo = canChangeInfo,
canPostMessages = null,
canEditMessages = null,
canDeleteMessages = canDeleteMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
canManageTopics = canManageTopics,
canPostStories = null,
canEditStories = null,
canDeleteStories = null
)

View File

@@ -177,6 +177,11 @@ val stickerKeywordLengthLimit = 0 .. 64
const val botActionActualityTime: Seconds = 5
val cloudStorageKeyLimit = 1 .. 128
val cloudStorageValueLimit = 0 .. 4096
val cloudStorageKeyRegex = Regex("[A-Za-z0-9_-]{${cloudStorageKeyLimit.first},${cloudStorageKeyLimit.last}}")
val cloudStorageValueRegex = Regex(".{${cloudStorageValueLimit.first},${cloudStorageValueLimit.last}}")
// Made as lazy for correct work in K/JS
val telegramInlineModeGifPermittedMimeTypes by lazy {
listOf(
@@ -210,6 +215,7 @@ const val firstNameField = "first_name"
const val lastNameField = "last_name"
const val languageCodeField = "language_code"
const val addedToAttachmentMenuField = "added_to_attachment_menu"
const val allowsWriteToPMField = "allows_write_to_pm"
const val isPremiumField = "is_premium"
const val hasPrivateForwardsField = "has_private_forwards"
const val hasRestrictedVoiceAndVideoMessagesField = "has_restricted_voice_and_video_messages"
@@ -274,6 +280,9 @@ const val correctOptionIdField = "correct_option_id"
const val allowsMultipleAnswersField = "allows_multiple_answers"
const val isAnonymousField = "is_anonymous"
const val canManageTopicsField = "can_manage_topics"
const val canPostStoriesField = "can_post_stories"
const val canEditStoriesField = "can_edit_stories"
const val canDeleteStoriesField = "can_delete_stories"
const val captionEntitiesField = "caption_entities"
const val hasSpoilerField = "has_spoiler"
const val loginUrlField = "login_url"

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.buttons
import dev.inmo.tgbotapi.types.botAdministratorRightsField
import dev.inmo.tgbotapi.types.botIsMemberField
import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
import dev.inmo.tgbotapi.types.chatHasUsernameField
import dev.inmo.tgbotapi.types.chatIsChannelField
import dev.inmo.tgbotapi.types.chatIsCreatedField
@@ -30,9 +30,9 @@ data class KeyboardButtonRequestChat(
@SerialName(chatIsCreatedField)
val isOwnedBy: Boolean? = null,
@SerialName(userAdministratorRightsField)
val userRightsInChat: ChatAdministratorRights? = null,
val userRightsInChat: ChatCommonAdministratorRights? = null,
@SerialName(botAdministratorRightsField)
val botRightsInChat: ChatAdministratorRights? = null,
val botRightsInChat: ChatCommonAdministratorRights? = null,
@SerialName(botIsMemberField)
val botIsMember: Boolean? = null
) {
@@ -41,8 +41,8 @@ data class KeyboardButtonRequestChat(
requestId: RequestId,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean? = null
) = KeyboardButtonRequestChat(
requestId = requestId,
@@ -60,8 +60,8 @@ data class KeyboardButtonRequestChat(
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean? = null
) = KeyboardButtonRequestChat(
requestId = requestId,

View File

@@ -1,8 +1,7 @@
package dev.inmo.tgbotapi.types.buttons.reply
import dev.inmo.tgbotapi.types.buttons.*
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.*
import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
@@ -120,8 +119,8 @@ inline fun requestChatReplyButton(
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean = false
) = requestChatReplyButton(
text,
@@ -145,8 +144,8 @@ inline fun requestChannelReplyButton(
requestId: RequestId,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean = false
) = requestChatReplyButton(
text,
@@ -170,8 +169,8 @@ inline fun requestChannelReplyButton(
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean? = null
) = requestChatReplyButton(
text,

View File

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

View File

@@ -49,13 +49,54 @@ object ChatTypeSerializer : KSerializer<ChatType> {
}
@RiskFeature
object PreviewChatSerializer : KSerializer<Chat> {
object ChatSerializer : KSerializer<Chat> {
@OptIn(InternalSerializationApi::class)
override val descriptor: SerialDescriptor = buildSerialDescriptor("PreviewChatSerializer", PolymorphicKind.OPEN)
override fun deserialize(decoder: Decoder): Chat {
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 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) {
is ExtendedChat -> ExtendedChatSerializer.serialize(encoder, value)
is PrivateChatImpl -> PrivateChatImpl.serializer().serialize(encoder, value)
is GroupChatImpl -> GroupChatImpl.serializer().serialize(encoder, value)
is SupergroupChatImpl -> SupergroupChatImpl.serializer().serialize(encoder, value)
is ForumChatImpl -> ForumChatImpl.serializer().serialize(encoder, value)
is ChannelChatImpl -> ChannelChatImpl.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 UnknownChatType -> JsonObject.serializer().serialize(encoder, value.rawJson)
}
@@ -128,6 +167,7 @@ sealed class ExtendedChatSerializer : KSerializer<ExtendedChat> {
is ExtendedSupergroupChatImpl -> ExtendedSupergroupChatImpl.serializer().serialize(encoder, value)
is ExtendedForumChatImpl -> ExtendedForumChatImpl.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)
}
}

View File

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

View File

@@ -15,7 +15,7 @@ data class GroupChatImpl(
override val id: ChatId,
@SerialName(titleField)
override val title: String
) : GroupChat
) : PreviewGroupChat
@Serializable
@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 = "",
@SerialName(lastNameField)
override val lastName: String = ""
) : PrivateChat
) : PreviewPrivateChat
@Serializable
@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,
@SerialName(usernameField)
override val username: Username? = null
) : SupergroupChat
) : PreviewSupergroupChat
@Serializable
@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,
@SerialName(usernameField)
override val username: Username? = null
) : ForumChat
) : PreviewForumChat
@Serializable
@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,
@SerialName(usernameField)
override val username: Username? = null
) : ChannelChat
) : PreviewChannelChat
@Serializable(UserSerializer::class)
sealed class User : PrivateChat
@Serializable(UserSerializer::class)
sealed class PreviewUser : PreviewPrivateChat, User()
@Serializable(UserSerializer::class)
sealed class Bot : User()
@Serializable(UserSerializer::class)
sealed class PreviewBot : PreviewUser()
@Serializable
data class CommonBot(
override val id: UserId,
@@ -78,7 +84,7 @@ data class CommonBot(
override val lastName: String = "",
@SerialName(usernameField)
override val username: Username? = null,
) : Bot() {
) : PreviewBot() {
@SerialName(isBotField)
private val isBot = true
}
@@ -99,7 +105,7 @@ data class CommonUser(
override val isPremium: Boolean = false,
@SerialName(addedToAttachmentMenuField)
override val addedToAttachmentMenu: Boolean = false
) : User(), WithOptionalLanguageCode, PossiblyPremiumChat, AbleToAddInAttachmentMenuChat {
) : PreviewUser(), WithOptionalLanguageCode, PossiblyPremiumChat, AbleToAddInAttachmentMenuChat {
constructor(
id: UserId,
firstName: String,

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,
val raw: String,
val rawJson: JsonObject
) : Chat
) : Chat, PreviewChat

View File

@@ -35,9 +35,15 @@ data class AdministratorChatMemberImpl(
@SerialName(customTitleField)
override val customTitle: String? = null,
@SerialName(canManageTopicsField)
override val canManageTopics: Boolean = false
override val canManageTopics: Boolean = false,
@SerialName(canPostStoriesField)
override val canPostStories: Boolean = false,
@SerialName(canEditStoriesField)
override val canEditStories: Boolean = false,
@SerialName(canDeleteStoriesField)
override val canDeleteStories: Boolean = false
) : AdministratorChatMember {
@SerialName(statusField)
@Required
private val type: String = "administrator"
override val status: ChatMember.Status = ChatMember.Status.Administrator
}

View File

@@ -16,4 +16,43 @@ sealed interface ChatAdministratorRights : SpecialChatAdministratorRights {
val canPromoteMembers: Boolean
val canPostMessages: Boolean
val canEditMessages: Boolean
val canPostStories: Boolean
val canEditStories: Boolean
val canDeleteStories: Boolean
companion object {
operator fun invoke(
canChangeInfo: Boolean = false,
canPostMessages: Boolean = false,
canEditMessages: Boolean = false,
canRemoveMessages: Boolean = false,
canInviteUsers: Boolean = false,
canRestrictMembers: Boolean = false,
canPinMessages: Boolean = false,
canPromoteMembers: Boolean = false,
canManageVideoChats: Boolean = false,
canManageChat: Boolean = false,
isAnonymous: Boolean = false,
canManageTopics: Boolean = false,
canPostStories: Boolean = false,
canEditStories: Boolean = false,
canDeleteStories: Boolean = false
) = ChatCommonAdministratorRights(
canChangeInfo = canChangeInfo,
canPostMessages = canPostMessages,
canEditMessages = canEditMessages,
canRemoveMessages = canRemoveMessages,
canInviteUsers = canInviteUsers,
canRestrictMembers = canRestrictMembers,
canPinMessages = canPinMessages,
canPromoteMembers = canPromoteMembers,
canManageVideoChats = canManageVideoChats,
canManageChat = canManageChat,
isAnonymous = isAnonymous,
canManageTopics = canManageTopics,
canPostStories = canPostStories,
canEditStories = canEditStories,
canDeleteStories = canDeleteStories
)
}
}

View File

@@ -5,7 +5,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class ChatAdministratorRightsImpl(
data class ChatCommonAdministratorRights(
@SerialName(canChangeInfoField)
override val canChangeInfo: Boolean = false,
@SerialName(canPostMessagesField)
@@ -29,5 +29,20 @@ data class ChatAdministratorRightsImpl(
@SerialName(isAnonymousField)
override val isAnonymous: Boolean = false,
@SerialName(canManageTopicsField)
override val canManageTopics: Boolean = false
override val canManageTopics: Boolean = false,
@SerialName(canPostStoriesField)
override val canPostStories: Boolean = false,
@SerialName(canEditStoriesField)
override val canEditStories: Boolean = false,
@SerialName(canDeleteStoriesField)
override val canDeleteStories: Boolean = false
) : ChatAdministratorRights
@Deprecated(
"Renamed to ChatCommonAdministratorRights and will be removed soon",
ReplaceWith(
"ChatCommonAdministratorRights",
"dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights"
)
)
typealias ChatAdministratorRightsImpl = ChatCommonAdministratorRights

View File

@@ -4,8 +4,10 @@ import dev.inmo.tgbotapi.abstracts.WithUser
import dev.inmo.tgbotapi.types.statusField
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@@ -13,7 +15,38 @@ import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive
@Serializable(ChatMemberSerializer::class)
sealed interface ChatMember : WithUser
sealed interface ChatMember : WithUser {
@Serializable(StatusSerializer::class)
enum class Status(
val status: String,
val deserializationStrategy: DeserializationStrategy<ChatMember>
) {
Creator("creator", OwnerChatMember.serializer()),
Administrator("administrator", AdministratorChatMemberImpl.serializer()),
Member("member", MemberChatMemberImpl.serializer()),
Restricted("restricted", RestrictedChatMember.serializer()),
Left("left", LeftChatMemberImpl.serializer()),
Kicked("kicked", KickedChatMember.serializer())
}
object StatusSerializer : KSerializer<Status> {
override val descriptor: SerialDescriptor
get() = String.serializer().descriptor
override fun deserialize(decoder: Decoder): Status {
val status = decoder.decodeString()
return Status.values().first {
it.status == status
}
}
override fun serialize(encoder: Encoder, value: Status) {
encoder.encodeString(value.status)
}
}
val status: Status
}
@RiskFeature
object ChatMemberSerializer : KSerializer<ChatMember> {
@@ -21,15 +54,14 @@ object ChatMemberSerializer : KSerializer<ChatMember> {
override fun deserialize(decoder: Decoder): ChatMember {
val json = JsonObject.serializer().deserialize(decoder)
return when (json[statusField] ?.jsonPrimitive ?.content ?: error("Status field of chat member must be specified, but incoming json contains next: $json")) {
"creator" -> nonstrictJsonFormat.decodeFromJsonElement(OwnerChatMember.serializer(), json)
"administrator" -> nonstrictJsonFormat.decodeFromJsonElement(AdministratorChatMemberImpl.serializer(), json)
"member" -> nonstrictJsonFormat.decodeFromJsonElement(MemberChatMemberImpl.serializer(), json)
"restricted" -> nonstrictJsonFormat.decodeFromJsonElement(RestrictedChatMember.serializer(), json)
"left" -> nonstrictJsonFormat.decodeFromJsonElement(LeftChatMemberImpl.serializer(), json)
"kicked" -> nonstrictJsonFormat.decodeFromJsonElement(KickedChatMember.serializer(), json)
else -> error("Unknown type of chat member in json: $json")
}
val status = json[statusField] ?.jsonPrimitive ?.content ?: error("Status field of chat member must be specified, but incoming json contains next: $json")
return ChatMember.Status.values().firstNotNullOfOrNull {
if (it.status == status) {
nonstrictJsonFormat.decodeFromJsonElement(it.deserializationStrategy, json)
} else {
null
}
} ?: error("Unknown type of chat member in json: $json")
}
override fun serialize(encoder: Encoder, value: ChatMember) {

View File

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

View File

@@ -13,5 +13,5 @@ data class KickedChatMember(
) : BannedChatMember {
@SerialName(statusField)
@Required
private val type: String = "kicked"
override val status: ChatMember.Status = ChatMember.Status.Kicked
}

View File

@@ -5,8 +5,11 @@ import dev.inmo.tgbotapi.types.chat.User
import kotlinx.serialization.*
@Serializable
data class LeftChatMemberImpl(@SerialName(userField) override val user: User) : LeftChatMember {
data class LeftChatMemberImpl(
@SerialName(userField)
override val user: User
) : LeftChatMember {
@SerialName(statusField)
@Required
private val type: String = "left"
override val status: ChatMember.Status = ChatMember.Status.Left
}

View File

@@ -5,8 +5,11 @@ import dev.inmo.tgbotapi.types.chat.User
import kotlinx.serialization.*
@Serializable
data class MemberChatMemberImpl(@SerialName(userField) override val user: User) : MemberChatMember {
data class MemberChatMemberImpl(
@SerialName(userField)
override val user: User
) : MemberChatMember {
@SerialName(statusField)
@Required
private val type: String = "member"
override val status: ChatMember.Status = ChatMember.Status.Member
}

View File

@@ -36,7 +36,14 @@ data class OwnerChatMember(
override val canManageChat: Boolean = true
@Transient
override val canManageTopics: Boolean = true
@Transient
override val canPostStories: Boolean = true
@Transient
override val canEditStories: Boolean = true
@Transient
override val canDeleteStories: Boolean = true
@SerialName(statusField)
@Required
private val type: String = "creator"
override val status: ChatMember.Status = ChatMember.Status.Creator
}

View File

@@ -44,5 +44,5 @@ data class RestrictedChatMember(
) : BannedChatMember, SpecialRightsChatMember, ChatPermissions {
@SerialName(statusField)
@Required
private val type: String = "restricted"
override val status: ChatMember.Status = ChatMember.Status.Restricted
}

View File

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

View File

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

View File

@@ -1,12 +1,75 @@
package dev.inmo.tgbotapi.types.message.ChatEvents.forum
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.ChatEvent
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.ForumEvent
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.PrivateEvent
import dev.inmo.tgbotapi.types.webAppNameField
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable
data class WriteAccessAllowed(
@SerialName(webAppNameField)
val webAppName: String? = null
) : ForumEvent
@Serializable(WriteAccessAllowed.Companion::class)
sealed interface WriteAccessAllowed : PrivateEvent, ForumEvent {
val webAppName: String?
get() = null
val fromRequest: Boolean
get() = false
val fromAttachmentMenu: Boolean
get() = false
@Serializable
object Other : WriteAccessAllowed
@Serializable
data class FromWebAppLink(
override val webAppName: String
) : WriteAccessAllowed
@Serializable
object FromRequest : WriteAccessAllowed {
override val fromRequest: Boolean
get() = true
}
@Serializable
object FromAttachmentMenu : WriteAccessAllowed {
override val fromAttachmentMenu: Boolean
get() = true
}
@Serializable
private class WriteAccessAllowedRaw(
val web_app_name: String? = null,
val from_request: Boolean = false,
val from_attachment_menu: Boolean = false
)
companion object : KSerializer<WriteAccessAllowed> {
override val descriptor: SerialDescriptor
get() = WriteAccessAllowedRaw.serializer().descriptor
override fun deserialize(decoder: Decoder): WriteAccessAllowed {
val raw = WriteAccessAllowedRaw.serializer().deserialize(decoder)
return when {
raw.web_app_name != null -> FromWebAppLink(raw.web_app_name)
raw.from_request -> FromRequest
raw.from_attachment_menu -> Other
else -> Other
}
}
override fun serialize(encoder: Encoder, value: WriteAccessAllowed) {
val raw = when (value) {
FromAttachmentMenu -> WriteAccessAllowedRaw(from_attachment_menu = true)
FromRequest -> WriteAccessAllowedRaw(from_request = true)
Other -> WriteAccessAllowedRaw()
is FromWebAppLink -> WriteAccessAllowedRaw(web_app_name = value.webAppName)
}
WriteAccessAllowedRaw.serializer().serialize(encoder, raw)
}
operator fun invoke(webAppName: String?): WriteAccessAllowed = webAppName ?.let(::FromWebAppLink) ?: Other
}
}

View File

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

View File

@@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.types.message
import korlibs.time.DateTime
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.SupergroupChat
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>(
override val messageId: MessageId,
override val from: User,
override val chat: SupergroupChat,
override val chat: PreviewSupergroupChat,
override val chatEvent: T,
override val date: DateTime
) : SupergroupEventMessage<T>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,9 @@
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
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
import dev.inmo.tgbotapi.types.chat.PreviewPublicChat
import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.message.content.MessageContent
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
import dev.inmo.tgbotapi.types.chat.PreviewGroupChat
import dev.inmo.tgbotapi.types.chat.PreviewSupergroupChat
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
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.chat.PreviewChat
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.buttons.KeyboardMarkup
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.PossiblyForwardedMessage
import dev.inmo.tgbotapi.types.stories.Story
import kotlinx.serialization.Serializable
@@ -25,7 +27,7 @@ data class StoryContent(
replyToMessageId: MessageId?,
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?
): Request<out Message> {
): Request<PossiblyForwardedMessage> {
return ForwardMessage(
chat.id,
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.files.VoiceFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.threadId
import kotlinx.serialization.Serializable
@Serializable
@@ -27,16 +28,16 @@ data class VoiceContent(
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?
): Request<ContentMessage<VoiceContent>> = SendVoice(
chatId,
media.fileId,
textSources,
media.duration,
messageThreadId,
disableNotification,
protectContent,
replyToMessageId,
allowSendingWithoutReply,
replyMarkup
chatId = chatId,
voice = media.fileId,
entities = textSources,
threadId = messageThreadId,
duration = media.duration,
disableNotification = disableNotification,
protectContent = protectContent,
replyToMessageId = replyToMessageId,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
override fun asTelegramMedia(): TelegramMediaAudio = TelegramMediaAudio(

View File

@@ -7,8 +7,9 @@ import kotlin.random.Random
@Serializable
@JvmInline
value class RequestId(
val int: Int
val uShort: UShort
) {
constructor(int: Int) : this(int.toUShort())
companion object {
fun random() = RequestId(Random.nextInt())
}

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

@@ -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
@OptIn(RiskFeature::class)
@Serializable(MimeTypeSerializer::class)
actual data class MimeType(
actual val raw: String

View File

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

View File

@@ -13,7 +13,7 @@ kotlin {
sourceSets {
commonMain {
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.GroupChat
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.PublicChat
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 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.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 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?
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()
?.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?
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
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.UserId
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]
*/
@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]
*/
@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]

View File

@@ -1,6 +1,6 @@
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.whenUsernameChat
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]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun WithChat.sameChat(chatId: ChatIdentifier) = chat.id == chatId || (chatId is Username && chat.whenUsernameChat {
it.username == chatId
} ?: false)
inline fun WithPreviewChat.sameChat(chatId: ChatIdentifier) =
chat.id == chatId || (chatId is Username && chat.whenUsernameChat {
it.username == chatId
} ?: false)
/**
* @return true in case if [this] message is placed in the [chat]
*/
@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]
*/
@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]

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

@@ -3,7 +3,7 @@ package dev.inmo.tgbotapi.extensions.utils.types.buttons
import dev.inmo.tgbotapi.types.buttons.*
import dev.inmo.tgbotapi.types.buttons.reply.requestChatReplyButton
import dev.inmo.tgbotapi.types.buttons.reply.requestUserReplyButton
import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
import dev.inmo.tgbotapi.utils.*
@@ -220,8 +220,8 @@ inline fun ReplyKeyboardRowBuilder.requestChatButton(
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean? = null
) = requestChatButton(
text,
@@ -248,8 +248,8 @@ inline fun ReplyKeyboardRowBuilder.requestChannelButton(
requestId: RequestId,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean? = null
) = requestChatButton(
text,
@@ -275,8 +275,8 @@ inline fun ReplyKeyboardRowBuilder.requestGroupButton(
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
userRightsInChat: ChatCommonAdministratorRights? = null,
botRightsInChat: ChatCommonAdministratorRights? = null,
botIsMember: Boolean? = null
) = requestChatButton(
text,

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.update.*
import dev.inmo.tgbotapi.types.update.abstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
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
* with [dev.inmo.tgbotapi.types.message.content.MediaGroupContent]
*/
@OptIn(RiskFeature::class)
fun List<Update>.convertWithMediaGroupUpdates(): List<Update> {
val resultUpdates = mutableListOf<Update>()
val mediaGroups = mutableMapOf<MediaGroupIdentifier, MutableList<Pair<BaseSentMessageUpdate, PossiblySentViaBotCommonMessage<MediaGroupPartContent>>>>()

View File

@@ -8,7 +8,9 @@ sealed interface Color {
value class BackgroundColor(override val value: String) : Color
@Serializable
value class Hex(override val value: String) : Color
value class Hex(override val value: String) : Color {
constructor(r: UByte, g: UByte, b: UByte) : this("#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}")
}
companion object {
val BackgroundColor = BackgroundColor("bg_color")

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