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

Compare commits

..

29 Commits

Author SHA1 Message Date
61be689aca fix of build 2021-04-17 14:36:23 +06:00
03f8d65bb6 fix of build 2021-04-17 11:14:35 +06:00
2dc8beba8a update classcasts 2021-04-16 22:00:45 +06:00
5451adf4ac FromUserMessage extends Message 2021-04-16 18:07:27 +06:00
9982534001 updates in TextSourceSerializer 2021-04-15 14:34:05 +06:00
c0451d4c8f Update gradle-wrapper.properties 2021-04-15 13:56:52 +06:00
a9d65944e6 update MicroUtils 2021-04-13 15:53:35 +06:00
39598dcb69 fill CHANGELOG 2021-04-13 01:54:45 +06:00
3069a6084c fixes in parseCommandsWithParams 2021-04-13 01:50:41 +06:00
e7bbce3fa7 update uuid dependency 2021-04-12 23:32:50 +06:00
0b361163f2 TextSourceSerializer 2021-04-12 23:23:18 +06:00
6aba2ff641 start 0.33.4 2021-04-12 20:57:12 +06:00
6b761ab37d Update gradle wrapper 2021-04-11 04:06:30 +06:00
b9c8a89af9 Merge pull request #357 from InsanusMokrassar/0.33.3
0.33.3
2021-04-05 19:41:52 +06:00
a42c5c63c4 fillup changelog 2021-04-05 19:08:12 +06:00
fc1a029acb fix of #358 2021-04-05 19:06:49 +06:00
f90ae2f918 include weakLaunch in behaviour builder 2021-04-05 18:15:10 +06:00
019b260888 update ktor 2021-04-05 18:11:29 +06:00
0831f2fa75 update microutils 2021-04-05 17:21:59 +06:00
32ae9d2b16 hotfix 2021-04-05 16:50:31 +06:00
b975a1b036 updates in behaviour builder 2021-04-05 16:41:27 +06:00
f4b1e4a150 start 0.33.3 2021-04-05 13:42:48 +06:00
97031512d4 Merge pull request #356 from InsanusMokrassar/0.33.2
0.33.2
2021-04-03 15:39:14 +06:00
353df43109 update microutils 2021-04-03 15:13:20 +06:00
4e55460834 fix in doInSubContext 2021-04-03 15:05:25 +06:00
e2e235bd6c behaviour builder updates 2021-04-03 14:51:21 +06:00
8ced95bc2f start 0.33.2 2021-04-03 14:24:24 +06:00
685e4af8f5 Merge pull request #345 from InsanusMokrassar/0.33.1
0.33.1
2021-03-26 09:13:15 +06:00
80093cb5a9 Update kdocs.yml 2021-03-21 23:22:16 +06:00
43 changed files with 333 additions and 42 deletions

View File

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

View File

@@ -1,5 +1,45 @@
# TelegramBotAPI changelog
## 0.33.4
* `Common`:
* `Version`:
* `uuid`: `0.2.3` -> `0.2.4`
* `MicroUtils`: `0.4.33` -> `0.4.35`
* `Core`:
* All `TextSource` implementators have become `Serializable`
* New serializer `TextSourceSerializer`
* Interface`FromUserMessage` now extends `Message`
* New interface `FromUser`
* Interface `FromUserMessage` now extends `FromUser`
* `Extensions Utils`
* Fixes in `parseCommandsWithParams`
## 0.33.3
* `Common`:
* `Version`:
* `MicroUtils`: `0.4.32` -> `0.4.33`
* `Ktor`: `1.5.2` -> `1.5.3`
* `API`:
* Bot actions DSL (fix for [#358](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/358))
* `Behaviour Builder`:
* Rewrite logic of `doInSubContextWithUpdatesFilter` and `doInSubContextWithFlowsUpdatesFilterSetup` extensions
* All triggers now work with `stopOnCompletion` set up to `false`
## 0.33.2
* `Common`:
* `Version`:
* `MicroUtils`: `0.4.30` -> `0.4.32`
* `Behaviour Builder`:
* New typealias `MediaGroupFilter` has been added for `MediaGroup` expectators
* Several typealiases became `suspend`:
* `CallbackQueryMapper`
* `ChatMemberUpdatedMapper`
* `InlineQueryMapper`
* Commands got an additional parameter - `additionalFilter`. It will be called when all command filters were passed
## 0.33.1
* `Common`:

View File

@@ -18,6 +18,17 @@ plugins {
id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version" apply false
}
// temporal crutch until legacy tests will be stabled or legacy target will be removed
allprojects {
if (it != rootProject.findProject("docs")) {
tasks.whenTaskAdded { task ->
if(task.name == "jsLegacyBrowserTest" || task.name == "jsLegacyNodeTest") {
task.enabled = false
}
}
}
}
private String getCurrentVersionChangelog() {
OutputStream changelogDataOS = new ByteArrayOutputStream()
exec {

View File

@@ -9,14 +9,14 @@ kotlin_version=1.4.32
kotlin_coroutines_version=1.4.3
kotlin_serialisation_runtime_version=1.1.0
klock_version=2.0.7
uuid_version=0.2.3
ktor_version=1.5.2
uuid_version=0.2.4
ktor_version=1.5.3
micro_utils_version=0.4.30
micro_utils_version=0.4.35
javax_activation_version=1.1.1
library_group=dev.inmo
library_version=0.33.1
library_version=0.33.4
github_release_plugin_version=2.2.12

View File

@@ -52,6 +52,7 @@ kotlin {
api "dev.inmo:micro_utils.coroutines:$micro_utils_version"
api "dev.inmo:micro_utils.serialization.base64:$micro_utils_version"
api "dev.inmo:micro_utils.serialization.encapsulator:$micro_utils_version"
api "dev.inmo:micro_utils.serialization.typed_serializer:$micro_utils_version"
api "io.ktor:ktor-client-core:$ktor_version"
}

View File

@@ -0,0 +1,7 @@
package dev.inmo.tgbotapi.CommonAbstracts
import dev.inmo.tgbotapi.types.User
interface FromUser {
val user: User
}

View File

@@ -1,14 +1,17 @@
package dev.inmo.tgbotapi.CommonAbstracts
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourceSerializer
import dev.inmo.tgbotapi.types.MessageEntity.textsources.regular
import dev.inmo.tgbotapi.types.MessageEntity.toTextParts
import dev.inmo.tgbotapi.types.captionLength
import dev.inmo.tgbotapi.types.textLength
import kotlinx.serialization.Serializable
const val DirectInvocationOfTextSourceConstructor = "It is strongly not recommended to use constructors directly instead of factory methods"
typealias TextSourcesList = List<TextSource>
@Serializable(TextSourceSerializer::class)
interface TextSource {
val markdown: String
val markdownV2: String
@@ -17,6 +20,10 @@ interface TextSource {
val asText: String
get() = source
companion object {
fun serializer() = TextSourceSerializer
}
}
@Suppress("NOTHING_TO_INLINE")
@@ -28,8 +35,13 @@ inline operator fun TextSource.plus(text: String) = listOf(this, regular(text))
@Suppress("NOTHING_TO_INLINE")
inline operator fun List<TextSource>.plus(text: String) = this + regular(text)
@Serializable(TextSourceSerializer::class)
interface MultilevelTextSource : TextSource {
val subsources: List<TextSource>
companion object {
fun serializer() = TextSourceSerializer
}
}
data class TextPart(

View File

@@ -80,6 +80,8 @@ val openPeriodPollSecondsLimit = 5 .. 600
val membersLimit = 1 .. 99999
const val botActionActualityTime: Seconds = 5
// Made as lazy for correct work in K/JS
val telegramInlineModeGifPermittedMimeTypes by lazy {
listOf(

View File

@@ -3,10 +3,13 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* @see bold
*/
@Serializable
data class BoldTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -4,12 +4,14 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
private val commandRegex = Regex("[/!][^@\\s]*")
/**
* @see botCommand
*/
@Serializable
data class BotCommandTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see cashTag
*/
@Serializable
data class CashTagTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see code
*/
@Serializable
data class CodeTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see email
*/
@Serializable
data class EMailTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see hashtag
*/
@Serializable
data class HashTagTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see italic
*/
@Serializable
data class ItalicTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -3,6 +3,7 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
private val String.withoutCommercialAt
get() = if (startsWith("@")) {
@@ -14,6 +15,7 @@ private val String.withoutCommercialAt
/**
* @see mention
*/
@Serializable
data class MentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see phone
*/
@Serializable
data class PhoneNumberTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see pre
*/
@Serializable
data class PreTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
val language: String? = null

View File

@@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see regular
*/
@Serializable
data class RegularTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see strikethrough
*/
@Serializable
data class StrikethroughTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see link
*/
@Serializable
data class TextLinkTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
val url: String

View File

@@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see mention
*/
@Serializable
data class TextMentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
val user: User,

View File

@@ -0,0 +1,43 @@
package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.CommonAbstracts.justTextSources
import dev.inmo.tgbotapi.types.MessageEntity.*
import dev.inmo.tgbotapi.types.MessageEntity.RawMessageEntities
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
private val baseSerializers: Map<String, KSerializer<out TextSource>> = mapOf(
"regular" to RegularTextSource.serializer(),
"text_link" to TextLinkTextSource.serializer(),
"code" to CodeTextSource.serializer(),
"url" to URLTextSource.serializer(),
"pre" to PreTextSource.serializer(),
"bot_command" to BotCommandTextSource.serializer(),
"strikethrough" to StrikethroughTextSource.serializer(),
"italic" to ItalicTextSource.serializer(),
"bold" to BoldTextSource.serializer(),
"email" to EMailTextSource.serializer(),
"underline" to UnderlineTextSource.serializer(),
"mention" to MentionTextSource.serializer(),
"phone_number" to PhoneNumberTextSource.serializer(),
"text_mention" to TextMentionTextSource.serializer(),
"hashtag" to HashTagTextSource.serializer(),
"cashtag" to CashTagTextSource.serializer(),
)
object TextSourceSerializer : TypedSerializer<TextSource>(TextSource::class, baseSerializers) {
override fun <T: TextSource> include(type: String, serializer: KSerializer<T>) {
require(type !in baseSerializers.keys)
super.include(type, serializer)
}
override fun exclude(type: String) {
require(type !in baseSerializers.keys)
super.exclude(type)
}
}

View File

@@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see link
*/
@Serializable
data class URLTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see underline
*/
@Serializable
data class UnderlineTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@@ -1,7 +1,5 @@
package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.User
import dev.inmo.tgbotapi.CommonAbstracts.FromUser
interface FromUserMessage {
val user: User
}
interface FromUserMessage : FromUser, Message

View File

@@ -0,0 +1,40 @@
package dev.inmo.tgbotapi.types
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.CommonAbstracts.makeString
import dev.inmo.tgbotapi.TestsJsonFormat
import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourceSerializer
import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.encodeToString
import kotlin.test.Test
import kotlin.test.assertEquals
class TextSourcesTests {
@Test
fun testThatTextSourcesSerializedCorrectly() {
val testList = buildEntities {
bold(
buildEntities {
italic("It")
regular(" ")
link("is example", "https://is.example")
}
)
regular(" ")
underline("of")
regular(" ")
strikethrough("complex")
regular(" ")
pre("text", "kotlin")
}
val serialized = TestsJsonFormat.encodeToString(ListSerializer(TextSource.serializer()), testList)
val deserialized = TestsJsonFormat.decodeFromString(
ListSerializer(TextSource.serializer()),
serialized
)
assertEquals(testList, deserialized)
assertEquals(testList.makeString(), deserialized.makeString())
}
}

View File

@@ -0,0 +1,76 @@
package dev.inmo.tgbotapi.extensions.api.send
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.send.SendAction
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.actions.*
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import kotlinx.coroutines.*
private const val refreshTime: MilliSeconds = (botActionActualityTime - 1) * 1000L
typealias TelegramBotActionCallback<T> = suspend TelegramBot.() -> T
suspend fun <T> TelegramBot.withAction(
actionRequest: SendAction,
block: TelegramBotActionCallback<T>
): T {
val botActionJob = supervisorScope {
launch {
while (isActive) {
delay(refreshTime)
safelyWithoutExceptions {
execute(actionRequest)
}
}
}
}
return try {
safely { block() }
} finally {
botActionJob.cancel()
}
}
suspend fun <T> TelegramBot.withAction(
chatId: ChatId,
action: BotAction,
block: TelegramBotActionCallback<T>
) = withAction(
SendAction(chatId, action),
block
)
suspend fun <T> TelegramBot.withAction(
chat: Chat,
action: BotAction,
block: TelegramBotActionCallback<T>
) = withAction(
chat.id,
action,
block
)
suspend fun <T> TelegramBot.withTypingAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, TypingAction, block)
suspend fun <T> TelegramBot.withUploadPhotoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadPhotoAction, block)
suspend fun <T> TelegramBot.withRecordVideoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, RecordVideoAction, block)
suspend fun <T> TelegramBot.withUploadVideoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadVideoAction, block)
suspend fun <T> TelegramBot.withRecordAudioAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, RecordAudioAction, block)
suspend fun <T> TelegramBot.withUploadAudioAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadAudioAction, block)
suspend fun <T> TelegramBot.withUploadDocumentAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadDocumentAction, block)
suspend fun <T> TelegramBot.withFindLocationAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, FindLocationAction, block)
suspend fun <T> TelegramBot.withRecordVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, RecordVideoNoteAction, block)
suspend fun <T> TelegramBot.withUploadVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadVideoNoteAction, block)
suspend fun <T> TelegramBot.withTypingAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, TypingAction, block)
suspend fun <T> TelegramBot.withUploadPhotoAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadPhotoAction, block)
suspend fun <T> TelegramBot.withRecordVideoAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, RecordVideoAction, block)
suspend fun <T> TelegramBot.withUploadVideoAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadVideoAction, block)
suspend fun <T> TelegramBot.withRecordAudioAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, RecordAudioAction, block)
suspend fun <T> TelegramBot.withUploadAudioAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadAudioAction, block)
suspend fun <T> TelegramBot.withUploadDocumentAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadDocumentAction, block)
suspend fun <T> TelegramBot.withFindLocationAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, FindLocationAction, block)
suspend fun <T> TelegramBot.withRecordVideoNoteAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, RecordVideoNoteAction, block)
suspend fun <T> TelegramBot.withUploadVideoNoteAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadVideoNoteAction, block)

View File

@@ -1,6 +1,7 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.weakLaunch
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
@@ -36,14 +37,15 @@ suspend fun <T> BehaviourContext.doInSubContextWithFlowsUpdatesFilterSetup(
newFlowsUpdatesFilterSetUp: BehaviourContextAndTypeReceiver<Unit, FlowsUpdatesFilter>?,
stopOnCompletion: Boolean = true,
behaviourContextReceiver: BehaviourContextReceiver<T>
) = copy(
flowsUpdatesFilter = FlowsUpdatesFilter(),
scope = CoroutineScope(scope.coroutineContext + SupervisorJob())
).run {
): T = supervisorScope {
val newContext = copy(
flowsUpdatesFilter = FlowsUpdatesFilter(),
scope = this
)
newFlowsUpdatesFilterSetUp ?.let {
it.apply { invoke(this@run, this@doInSubContextWithFlowsUpdatesFilterSetup.flowsUpdatesFilter) }
it.apply { invoke(newContext, this@doInSubContextWithFlowsUpdatesFilterSetup.flowsUpdatesFilter) }
}
behaviourContextReceiver().also { if (stopOnCompletion) stop() }
newContext.behaviourContextReceiver().also { if (stopOnCompletion) stop() }
}
/**
@@ -54,13 +56,17 @@ suspend fun <T> BehaviourContext.doInSubContextWithUpdatesFilter(
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?,
stopOnCompletion: Boolean = true,
behaviourContextReceiver: BehaviourContextReceiver<T>
) = doInSubContextWithFlowsUpdatesFilterSetup(
): T = doInSubContextWithFlowsUpdatesFilterSetup(
newFlowsUpdatesFilterSetUp = updatesFilter ?.let {
{ oldOne ->
oldOne.allUpdatesFlow.filter { updatesFilter(it) }.subscribeSafelyWithoutExceptions(scope, asUpdateReceiver)
weakLaunch {
oldOne.allUpdatesFlow.filter { updatesFilter(it) }.subscribeSafelyWithoutExceptions(this, asUpdateReceiver)
}
}
} ?: { oldOne ->
oldOne.allUpdatesFlow.subscribeSafelyWithoutExceptions(scope, asUpdateReceiver)
weakLaunch {
oldOne.allUpdatesFlow.subscribeSafelyWithoutExceptions(this, asUpdateReceiver)
}
},
stopOnCompletion,
behaviourContextReceiver

View File

@@ -8,7 +8,7 @@ import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.CallbackQuery.*
import kotlinx.coroutines.flow.toList
typealias CallbackQueryMapper<T> = T.() -> T?
typealias CallbackQueryMapper<T> = suspend T.() -> T?
private suspend fun <O> BehaviourContext.waitCallbackQueries(
count: Int = 1,

View File

@@ -11,7 +11,7 @@ import dev.inmo.tgbotapi.types.update.MyChatMemberUpdatedUpdate
import dev.inmo.tgbotapi.types.update.abstracts.ChatMemberUpdatedUpdate
import kotlinx.coroutines.flow.toList
typealias ChatMemberUpdatedMapper<T> = T.() -> T?
typealias ChatMemberUpdatedMapper<T> = suspend T.() -> T?
private suspend inline fun <reified T : ChatMemberUpdatedUpdate> BehaviourContext.waitChatMemberUpdated(
count: Int = 1,

View File

@@ -8,7 +8,7 @@ import dev.inmo.tgbotapi.types.InlineQueries.query.BaseInlineQuery
import dev.inmo.tgbotapi.types.InlineQueries.query.LocationInlineQuery
import kotlinx.coroutines.flow.toList
typealias InlineQueryMapper<T> = T.() -> T?
typealias InlineQueryMapper<T> = suspend T.() -> T?
private suspend fun <O> BehaviourContext.waitInlineQueries(
count: Int = 1,

View File

@@ -11,12 +11,14 @@ import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList
typealias MediaGroupFilter<T> = suspend List<MediaGroupMessage<T>>.() -> Boolean
@PreviewFeature
internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.buildMediaGroupWaiter(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
noinline filter: (suspend (List<MediaGroupMessage<T>>) -> Boolean)? = null
noinline filter: MediaGroupFilter<T>? = null
) = flowsUpdatesFilter.expectFlow(bot, initRequest, count, errorFactory) { update ->
update.asSentMediaGroupUpdate() ?.data ?.let { mediaGroup ->
if (mediaGroup.all { message -> message.content is T } && (filter == null || filter(mediaGroup as List<MediaGroupMessage<T>>))) {
@@ -33,35 +35,35 @@ suspend fun BehaviourContext.waitMediaGroup(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<MediaGroupContent>>) -> Boolean)? = null
filter: MediaGroupFilter<MediaGroupContent>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPlaylist(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<AudioMediaGroupContent>>) -> Boolean)? = null
filter: MediaGroupFilter<AudioMediaGroupContent>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentsGroup(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<DocumentMediaGroupContent>>) -> Boolean)? = null
filter: MediaGroupFilter<DocumentMediaGroupContent>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVisualGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<VisualMediaGroupContent>>) -> Boolean)? = null
filter: MediaGroupFilter<VisualMediaGroupContent>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPhotoGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<PhotoContent>>) -> Boolean)? = null
filter: MediaGroupFilter<PhotoContent>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVideoGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<VideoContent>>) -> Boolean)? = null
filter: MediaGroupFilter<VideoContent>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)

View File

@@ -26,7 +26,8 @@ internal suspend inline fun <reified T : CallbackQuery> BehaviourContext.onCallb
{ it.sourceChat() ?.id ?.chatId == triggerQuery.user.id.chatId }
} else {
null
}
},
stopOnCompletion = false
) {
scenarioReceiver(triggerQuery)
}

View File

@@ -24,7 +24,8 @@ internal suspend inline fun <reified U : ChatMemberUpdatedUpdate> BehaviourConte
{ it.sourceChat() ?.id ?.chatId == triggerChatMemberUpdated.chat.id.chatId }
} else {
null
}
},
stopOnCompletion = false
) {
scenarioReceiver(triggerChatMemberUpdated)
}

View File

@@ -11,6 +11,7 @@ suspend fun BehaviourContext.command(
commandRegex: Regex,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: CommonMessageFilter<TextContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<TextContent>>
): Job = onText(
includeFilterByChatInBehaviourSubContext,
@@ -22,7 +23,9 @@ suspend fun BehaviourContext.command(
} else {
true
}
sizeRequirement && textSources.any { commandRegex.matches(it.asBotCommandTextSource() ?.command ?: return@any false) }
sizeRequirement && textSources.any {
commandRegex.matches(it.asBotCommandTextSource() ?.command ?: return@any false)
} && (additionalFilter ?.invoke(message) != false)
},
scenarioReceiver
)
@@ -30,19 +33,22 @@ suspend fun BehaviourContext.command(
command: String,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: CommonMessageFilter<TextContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<TextContent>>
) = command(command.toRegex(), requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, scenarioReceiver)
) = command(command.toRegex(), requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend inline fun BehaviourContext.onCommand(
commandRegex: Regex,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline additionalFilter: CommonMessageFilter<TextContent>? = null,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<TextContent>>
): Job = command(commandRegex, requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, scenarioReceiver)
): Job = command(commandRegex, requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend inline fun BehaviourContext.onCommand(
command: String,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline additionalFilter: CommonMessageFilter<TextContent>? = null,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<TextContent>>
): Job = onCommand(command.toRegex(), requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, scenarioReceiver)
): Job = onCommand(command.toRegex(), requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)

View File

@@ -50,7 +50,8 @@ internal suspend inline fun <reified T : MessageContent> BehaviourContext.onCont
{ it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId }
} else {
null
}
},
stopOnCompletion = false
) {
scenarioReceiver(triggerMessage)
}

View File

@@ -29,7 +29,8 @@ internal suspend inline fun <reified T : ChatEvent> BehaviourContext.onEvent(
doInSubContextWithUpdatesFilter(
updatesFilter = if (includeFilterByChatInBehaviourSubContext) {
{ it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId }
} else null
} else null,
stopOnCompletion = false
) {
scenarioReceiver(triggerMessage)
}

View File

@@ -27,7 +27,8 @@ internal suspend inline fun <reified T : InlineQuery> BehaviourContext.onInlineQ
{ it.sourceChat() ?.id ?.chatId == triggerQuery.from.id.chatId }
} else {
null
}
},
stopOnCompletion = false
) {
scenarioReceiver(triggerQuery)
}

View File

@@ -32,7 +32,8 @@ internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.bui
doInSubContextWithUpdatesFilter(
updatesFilter = if (includeFilterByChatInBehaviourSubContext) {
{ it.sourceChat() ?.id ?.chatId == mediaGroupChat.id.chatId }
} else null
} else null,
stopOnCompletion = false
) {
scenarioReceiver(mediaGroup)
}

View File

@@ -25,7 +25,8 @@ suspend inline fun <reified T : EncryptedPassportElement> BehaviourContext.onPas
doInSubContextWithUpdatesFilter(
updatesFilter = if (includeFilterByChatInBehaviourSubContext) {
{ it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId }
} else null
} else null,
stopOnCompletion = false
) {
scenarioReceiver(triggerMessage)
}

View File

@@ -513,6 +513,10 @@ inline fun Message.asPossiblySentViaBotCommonMessage(): PossiblySentViaBotCommon
@PreviewFeature
inline fun Message.requirePossiblySentViaBotCommonMessage(): PossiblySentViaBotCommonMessage<MessageContent> = this as PossiblySentViaBotCommonMessage<MessageContent>
@PreviewFeature
inline fun Message.asFromUserMessage(): FromUserMessage? = this as? FromUserMessage
@PreviewFeature
inline fun Message.requireFromUserMessage(): FromUserMessage = this as FromUserMessage
@PreviewFeature
inline fun BotAction.asFindLocationAction(): FindLocationAction? = this as? FindLocationAction
@PreviewFeature
inline fun BotAction.requireFindLocationAction(): FindLocationAction = this as FindLocationAction

View File

@@ -18,8 +18,11 @@ fun List<TextSource>.parseCommandsWithParams(
var currentBotCommandSource: BotCommandTextSource? = null
var currentArgs = ""
fun includeCurrent() = currentBotCommandSource ?.let {
result[it.command] = currentArgs.split(argsSeparator).toTypedArray()
currentArgs = ""
currentArgs = currentArgs.trim()
if (currentArgs.isNotEmpty()) {
result[it.command] = currentArgs.split(argsSeparator).toTypedArray()
currentArgs = ""
}
}
for (textSource in this) {
if (textSource is BotCommandTextSource) {