diff --git a/CHANGELOG.md b/CHANGELOG.md index e76cd51097..c710e3abd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # TelegramBotAPI changelog +## 7.1.3 + +* `Versions`: + * `Serialization`: `1.5.0` -> `1.5.1` + * `MicroUtils`: `0.18.1` -> `0.18.4` +* `Core`: + * Actualize kdocs in `InputFile` +* `BehaviourBuilder`: + * Now it is possible to use `waitMediaContent`/`waitMediaContentMessage`/`onMediaContent` + * Add `onMention`/`waitMention` functionality + * Add opportunity to map content with extensions to `Flow` + ## 7.1.2 * `Versions`: diff --git a/gradle.properties b/gradle.properties index 187a1d1c57..414883468b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,4 @@ kotlin.incremental=true kotlin.incremental.js=true library_group=dev.inmo -library_version=7.1.2 +library_version=7.1.3 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 26c68c0fcc..c3447eda98 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.8.21" -kotlin-serialization = "1.5.0" +kotlin-serialization = "1.5.1" kotlin-coroutines = "1.6.4" javax-activation = "1.1.1" @@ -13,7 +13,7 @@ ktor = "2.3.0" ksp = "1.8.21-1.0.11" kotlin-poet = "1.13.2" -microutils = "0.18.1" +microutils = "0.18.4" github-release-plugin = "2.4.1" dokka = "1.8.10" diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt index 007ff4d2cd..fa854b6c77 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt @@ -10,117 +10,127 @@ import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull typealias CommonMessageToContentMapper = suspend CommonMessage.() -> T? @RiskFeature(lowLevelRiskFeatureMessage) -suspend inline fun BehaviourContext.waitContent( +suspend inline fun BehaviourContext.waitContent( initRequest: Request<*>? = null, noinline errorFactory: NullableRequestBuilder<*> = { null } -): Flow = waitContentMessage(initRequest, errorFactory).map { it.content } +): Flow = waitContentMessage(initRequest, errorFactory).map { it.content } +inline fun Flow.mapContent() = mapNotNull { it as? T } suspend fun BehaviourContext.waitAnyContent( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory) +suspend fun BehaviourContext.waitTextedContent( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitContact( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitDice( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitGame( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitLocation( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitLiveLocation( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitStaticLocation( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitPoll( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitText( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitVenue( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitAudioMediaGroupContent( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitDocumentMediaGroupContent( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitMedia( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitAnyMediaGroupContent( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitVisualMediaGroupContent( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitTextedMediaContent( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitAnimation( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitAudio( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitDocument( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitPhoto( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitSticker( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitVideo( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitVideoNote( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitVoice( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitInvoice( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() suspend fun BehaviourContext.waitVisualContent( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContent(initRequest, errorFactory) +) = waitContent(initRequest, errorFactory).mapContent() +suspend fun BehaviourContext.waitMediaContent( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitContent(initRequest, errorFactory).mapContent() diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt index e79ae00a13..99b8c27a76 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt @@ -5,6 +5,7 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext import dev.inmo.tgbotapi.extensions.utils.withContent +import dev.inmo.tgbotapi.extensions.utils.withContentOrNull import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.message.content.* @@ -12,142 +13,138 @@ import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.mapNotNull typealias CommonMessageToCommonMessageMapper = suspend CommonMessage.() -> CommonMessage? @RiskFeature(lowLevelRiskFeatureMessage) -suspend inline fun BehaviourContext.waitContentMessage( +suspend inline fun BehaviourContext.waitContentMessage( initRequest: Request<*>? = null, noinline errorFactory: NullableRequestBuilder<*> = { null } -): Flow> = expectFlow( +): Flow> = expectFlow( initRequest, errorFactory ) { if (it !is BaseSentMessageUpdate) { return@expectFlow emptyList() } - listOfNotNull((it.data as? CommonMessage<*>) ?.withContent()) + listOfNotNull((it.data as? CommonMessage<*>)) } -internal inline fun contentMessageConverter( - noinline mapper: CommonMessageToCommonMessageMapper? = null -): suspend CommonMessage.() -> CommonMessage? = mapper ?.let { - { - if (content is T) { - @Suppress("UNCHECKED_CAST") - val message = (this as CommonMessage) - safelyWithoutExceptions { mapper(message) } - } else { - null - } - } -} ?: { - @Suppress("UNCHECKED_CAST") - if (content is T) this as CommonMessage else null -} +inline fun Flow>.mapWithContent() = mapNotNull { it.withContentOrNull() } suspend fun BehaviourContext.waitAnyContentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null }, -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory) +suspend fun BehaviourContext.waitTextedContentMessage( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null }, +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitContactMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitDiceMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitGameMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitLocationMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitLiveLocationMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitStaticLocationMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitPollMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitTextMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitVenueMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitAudioMediaGroupContentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitDocumentMediaGroupContentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitMediaMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitAnyMediaGroupContentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitVisualMediaGroupContentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitTextedMediaContentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitAnimationMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitAudioMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitDocumentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitPhotoMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitStickerMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitVideoMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitVideoNoteMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitVoiceMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitInvoiceMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() suspend fun BehaviourContext.waitVisualContentMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } -) = waitContentMessage(initRequest, errorFactory) +) = waitContentMessage(initRequest, errorFactory).mapWithContent() + +suspend fun BehaviourContext.waitMediaContentMessage( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitContentMessage(initRequest, errorFactory).mapWithContent() diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitMention.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitMention.kt new file mode 100644 index 0000000000..817251f08b --- /dev/null +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitMention.kt @@ -0,0 +1,107 @@ +package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations + +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.utils.whenMentionTextSource +import dev.inmo.tgbotapi.extensions.utils.whenTextMentionTextSource +import dev.inmo.tgbotapi.requests.abstracts.Request +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.Username +import dev.inmo.tgbotapi.types.chat.User +import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage +import dev.inmo.tgbotapi.types.message.content.MessageContent +import dev.inmo.tgbotapi.types.message.content.TextedContent +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter + +/** + * Check, that [TextedContent.textSources] contains: + * + * * Any [dev.inmo.tgbotapi.types.message.textsources.MentionTextSource] with [dev.inmo.tgbotapi.types.message.textsources.MentionTextSource.username] + * equal to [username] + * * Any [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource] with [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource.user] + * with the same [username] + */ +fun TextedContent.isWithMention(username: Username) = textSources.any { + it.whenMentionTextSource { + it.username == username + } ?: it.whenTextMentionTextSource { + it.user.username == username + } ?: false +} + +/** + * Check, that [TextedContent.textSources] contains: + * + * * Any [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource] with [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource.user] + * with the same [userId] + */ +fun TextedContent.isWithTextMention(userId: UserId) = textSources.any { + it.whenTextMentionTextSource { + it.user.id == userId + } ?: false +} + +/** + * Uses [isWithMention] with [user] [Username] (is presented) or [isWithTextMention] with [user] [UserId] to determine + * user mentioning in [this] [CommonMessage] + */ +fun TextedContent.isWithMention(user: User): Boolean = user.username ?.let { username -> isWithMention(username) } == true || isWithTextMention(user.id) + +/** + * Uses [isWithMention] passing [username] as argument to take only messages with [username] mentions or text mentions + */ +fun Flow.filterMentions(username: Username) = filter { + it.isWithMention(username) +} + +/** + * Uses [isWithTextMention] passing [userId] as argument to take only messages with [userId] text mentions + */ +fun Flow.filterTextMentions(userId: UserId) = filter { + it.isWithTextMention(userId) +} + +/** + * Uses [isWithMention] passing [user] as argument to take only messages with [user] mentions or text mentions + */ +fun Flow.filterMentions(user: User) = filter { + it.isWithMention(user) +} + +/** + * Creates cold [Flow] with the messages with [TextedContent] where [username] has been mentioned + * + * @see filterMentions + * @see filterTextMentions + */ +suspend fun BehaviourContext.waitContentWithMentions ( + username: Username, + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitContent(initRequest, errorFactory).mapContent().filterMentions(username) + +/** + * Creates cold [Flow] with the messages with [TextedContent] where [userId] has been mentioned with text + * + * @see filterTextMentions + * @see filterMentions + * @see dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource + */ +suspend fun BehaviourContext.waitContentWithTextMentions ( + userId: UserId, + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitTextedContent(initRequest, errorFactory).filterTextMentions(userId) + +/** + * Creates cold [Flow] with the messages with [TextedContent] where [user] has been mentioned as text or mentioned + * with text + * + * @see filterMentions + * @see filterTextMentions + */ +suspend fun BehaviourContext.waitContentWithMentions ( + user: User, + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitTextedContent(initRequest, errorFactory).filterMentions(user) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitMentionMessage.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitMentionMessage.kt new file mode 100644 index 0000000000..27e61ae457 --- /dev/null +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitMentionMessage.kt @@ -0,0 +1,82 @@ +package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations + +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.utils.whenMentionTextSource +import dev.inmo.tgbotapi.extensions.utils.whenTextMentionTextSource +import dev.inmo.tgbotapi.requests.abstracts.Request +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.Username +import dev.inmo.tgbotapi.types.chat.User +import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage +import dev.inmo.tgbotapi.types.message.content.TextedContent +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter + +fun CommonMessage.isWithMention(username: Username) = content.isWithMention(username) + +fun CommonMessage.isWithTextMention(userId: UserId) = content.isWithTextMention(userId) + +/** + * Uses [isWithMention] with [user] [Username] (is presented) or [isWithTextMention] with [user] [UserId] to determine + * user mentioning in [this] [CommonMessage] + */ +fun CommonMessage.isWithMention(user: User): Boolean = content.isWithMention(user) + +/** + * Uses [isWithMention] passing [username] as argument to take only messages with [username] mentions or text mentions + */ +fun Flow>.filterMentionsMessages(username: Username) = filter { + it.isWithMention(username) +} + +/** + * Uses [isWithTextMention] passing [userId] as argument to take only messages with [userId] text mentions + */ +fun Flow>.filterTextMentionsMessages(userId: UserId) = filter { + it.isWithTextMention(userId) +} + +/** + * Uses [isWithMention] passing [user] as argument to take only messages with [user] mentions or text mentions + */ +fun Flow>.filterMentionsMessages(user: User) = filter { + it.isWithMention(user) +} + +/** + * Creates cold [Flow] with the messages with [TextedContent] where [username] has been mentioned + * + * @see filterMentions + * @see filterTextMentions + */ +suspend fun BehaviourContext.waitContentMessageWithMentions ( + username: Username, + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitContentMessage(initRequest, errorFactory).mapWithContent().filterMentionsMessages(username) + +/** + * Creates cold [Flow] with the messages with [TextedContent] where [userId] has been mentioned with text + * + * @see filterTextMentions + * @see filterMentions + * @see dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource + */ +suspend fun BehaviourContext.waitContentMessageWithTextMentions ( + userId: UserId, + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitTextedContentMessage(initRequest, errorFactory).filterTextMentionsMessages(userId) + +/** + * Creates cold [Flow] with the messages with [TextedContent] where [user] has been mentioned as text or mentioned + * with text + * + * @see filterMentions + * @see filterTextMentions + */ +suspend fun BehaviourContext.waitContentMessageWithMentions ( + user: User, + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitTextedContentMessage(initRequest, errorFactory).filterMentionsMessages(user) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt index e070df87f3..2ff230af77 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt @@ -248,6 +248,30 @@ suspend fun BC.onText( scenarioReceiver ) +/** + * @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call + * @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example, + * this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage]. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times] + * to combinate several filters + * @param [markerFactory] Will be used to identify different "stream". [scenarioReceiver] will be called synchronously + * in one "stream". Output of [markerFactory] will be used as a key for "stream" + * @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that + * data + */ +suspend fun BC.onTextedContent( + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver = MessageFilterByChat, + markerFactory: MarkerFactory = ByChatMessageMarkerFactory, + scenarioReceiver: CustomBehaviourContextAndTypeReceiver +) = onContentMessageWithType( + initialFilter, + subcontextUpdatesFilter, + markerFactory, + scenarioReceiver +) + /** * @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call * @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example, @@ -631,3 +655,27 @@ suspend fun BC.onVisualContent( markerFactory, scenarioReceiver ) + +/** + * @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call + * @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example, + * this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage]. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times] + * to combinate several filters + * @param [markerFactory] Will be used to identify different "stream". [scenarioReceiver] will be called synchronously + * in one "stream". Output of [markerFactory] will be used as a key for "stream" + * @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that + * data + */ +suspend fun BC.onMediaContent( + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver = MessageFilterByChat, + markerFactory: MarkerFactory = ByChatMessageMarkerFactory, + scenarioReceiver: CustomBehaviourContextAndTypeReceiver +) = onContentMessageWithType( + initialFilter, + subcontextUpdatesFilter, + markerFactory, + scenarioReceiver +) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/MentionTriggers.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/MentionTriggers.kt new file mode 100644 index 0000000000..1acc11d2dc --- /dev/null +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/MentionTriggers.kt @@ -0,0 +1,357 @@ +package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling + +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.behaviour_builder.CustomBehaviourContextAndTwoTypesReceiver +import dev.inmo.tgbotapi.extensions.behaviour_builder.CustomBehaviourContextAndTypeReceiver +import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.isWithMention +import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.isWithTextMention +import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.AnyMarkerFactory +import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory +import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.Username +import dev.inmo.tgbotapi.types.chat.User +import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage +import dev.inmo.tgbotapi.types.message.content.AnimationContent +import dev.inmo.tgbotapi.types.message.content.AudioContent +import dev.inmo.tgbotapi.types.message.content.DocumentContent +import dev.inmo.tgbotapi.types.message.content.MediaGroupContent +import dev.inmo.tgbotapi.types.message.content.MediaGroupPartContent +import dev.inmo.tgbotapi.types.message.content.PhotoContent +import dev.inmo.tgbotapi.types.message.content.TextContent +import dev.inmo.tgbotapi.types.message.content.TextedContent +import dev.inmo.tgbotapi.types.message.content.VideoContent +import dev.inmo.tgbotapi.types.message.content.VisualMediaGroupPartContent +import dev.inmo.tgbotapi.types.message.content.VoiceContent +import dev.inmo.tgbotapi.types.update.abstracts.Update + +internal suspend inline fun BC.onMention( + username: Username, + initialFilter: CommonMessageFilter? = null, + noinline subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onContentMessageWithType( + initialFilter * { + it.content.isWithMention(username) + }, + subcontextUpdatesFilter, + markerFactory, + scenarioReceiver +) + +internal suspend inline fun BC.onTextMention( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + noinline subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onContentMessageWithType( + initialFilter * { + it.content.isWithTextMention(userId) + }, + subcontextUpdatesFilter, + markerFactory, + scenarioReceiver +) + +internal suspend inline fun BC.onMention( + user: User, + initialFilter: CommonMessageFilter? = null, + noinline subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onContentMessageWithType( + initialFilter * { + it.content.isWithMention(user) + }, + subcontextUpdatesFilter, + markerFactory, + scenarioReceiver +) + + +suspend fun BC.onMentionWithAnyContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithAnyContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithAnyContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithVoiceContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithVoiceContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithVoiceContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithMediaGroupContent( + username: Username, + initialFilter: CommonMessageFilter>? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver>, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver>> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithMediaGroupContent( + userId: UserId, + initialFilter: CommonMessageFilter>? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver>, Update>? = null, + markerFactory: MarkerFactory>, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver>> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithMediaGroupContent( + user: User, + initialFilter: CommonMessageFilter>? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver>, Update>? = null, + markerFactory: MarkerFactory>, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver>> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithMediaGroupPartContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithMediaGroupPartContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithMediaGroupPartContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithAudioContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithAudioContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithAudioContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithDocumentContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithDocumentContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithDocumentContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithVisualMediaGroupPartContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithVisualMediaGroupPartContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithVisualMediaGroupPartContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithVideoContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithVideoContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithVideoContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithPhotoContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithPhotoContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithPhotoContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithAnimationContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithAnimationContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithAnimationContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +suspend fun BC.onMentionWithTextContent( + username: Username, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onTextMentionWithTextContent( + userId: UserId, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + +suspend fun BC.onMentionWithTextContent( + user: User, + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = null, + markerFactory: MarkerFactory, Any> = AnyMarkerFactory(), + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt index 67b126c235..8aeebbfd18 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt @@ -21,6 +21,11 @@ import kotlinx.serialization.encoding.Encoder * @see ByteArray.asMultipartFile * @see ByteReadChannel.asMultipartFile * @see ByteReadChannelAllocator.asMultipartFile + * + * @see fromInput + * @see fromFile + * @see fromId + * @see fromUrl */ @Serializable(InputFileSerializer::class) sealed class InputFile { @@ -29,9 +34,24 @@ sealed class InputFile { companion object { operator fun invoke(file: MPPFile) = file.asMultipartFile() + /** + * Creates [MultipartFile] based on incoming [filename] and [inputSource] + */ fun fromInput(filename: String, inputSource: () -> Input) = MultipartFile(filename, inputSource) + + /** + * Creates [MultipartFile] based on incoming [MPPFile] (common File in java, for example) + */ fun fromFile(file: MPPFile) = invoke(file) + + /** + * Creates [FileId] from the incomming [id] [String] with believe that it is [FileId] + */ fun fromId(id: String) = FileId(id) + + /** + * Creates [FileUrl] from the incomming [url] [String] + */ fun fromUrl(url: String) = FileUrl(url) } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Abstracts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Abstracts.kt index 9d21571e14..9d6eaac1c9 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Abstracts.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Abstracts.kt @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.types.message.content import dev.inmo.tgbotapi.abstracts.SpoilerableData +import dev.inmo.tgbotapi.abstracts.TextedInput import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.types.ChatIdentifier @@ -51,6 +52,18 @@ sealed interface MessageContent: ResendableContent { additionalBuilder() } + + polymorphic(TextedContent::class) { + subclass(TextContent::class) + subclass(VoiceContent::class) + subclass(MediaGroupContent::class) + subclass(AudioContent::class) + subclass(DocumentContent::class) + subclass(VideoContent::class) + subclass(PhotoContent::class) + subclass(AnimationContent::class) + } + polymorphic(MediaCollectionContent::class) { subclass(PhotoContent::class) @@ -115,6 +128,11 @@ sealed interface MediaCollectionContent: MessageContent, M val mediaCollection: List } +/** + * All the subtypes of this content will have [text] and [textSources] fields + */ +sealed interface TextedContent : MessageContent, TextedInput + sealed interface MediaContent: MessageContent { val media: TelegramMediaFile fun asTelegramMedia(): TelegramMedia diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/AbstractsMedia.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/AbstractsMedia.kt index 724552a98f..2b7cd80eaf 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/AbstractsMedia.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/AbstractsMedia.kt @@ -22,7 +22,7 @@ sealed interface DocumentMediaGroupPartContent : MediaGroupPartContent { override fun toMediaGroupMemberTelegramMedia(): DocumentMediaGroupMemberTelegramMedia } -sealed interface TextedMediaContent : MediaContent, TextedInput +sealed interface TextedMediaContent : TextedContent, MediaContent sealed interface MediaGroupCollectionContent : TextedMediaContent { @Serializable diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/TextContent.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/TextContent.kt index d16933944b..ff83c6abc2 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/TextContent.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/TextContent.kt @@ -15,7 +15,7 @@ import kotlinx.serialization.Serializable data class TextContent( override val text: String, override val textSources: TextSourcesList = emptyList(), -) : MessageContent, TextedInput { +) : TextedContent { override fun createResend( chatId: ChatIdentifier, messageThreadId: MessageThreadId?, diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Typealiases.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Typealiases.kt index 9ed6c11430..7cd305fdff 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Typealiases.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/Typealiases.kt @@ -2,6 +2,8 @@ package dev.inmo.tgbotapi.types.message.content import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage +typealias TextedMessage = CommonMessage + typealias InvoiceMessage = CommonMessage typealias VenueMessage = CommonMessage typealias GameMessage = CommonMessage diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt index 0f55cd2733..72626842e5 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt @@ -329,6 +329,7 @@ import dev.inmo.tgbotapi.types.message.content.SpoilerableMediaContent import dev.inmo.tgbotapi.types.message.content.StaticLocationContent import dev.inmo.tgbotapi.types.message.content.StickerContent import dev.inmo.tgbotapi.types.message.content.TextContent +import dev.inmo.tgbotapi.types.message.content.TextedContent import dev.inmo.tgbotapi.types.message.content.TextedMediaContent import dev.inmo.tgbotapi.types.message.content.VenueContent import dev.inmo.tgbotapi.types.message.content.VideoContent @@ -3712,6 +3713,15 @@ public inline fun ResendableContent.ifMediaCollectionContent(block: (MediaCollectionContent) -> T): T? = mediaCollectionContentOrNull() ?.let(block) +public inline fun ResendableContent.textedContentOrNull(): TextedContent? = this as? + dev.inmo.tgbotapi.types.message.content.TextedContent + +public inline fun ResendableContent.textedContentOrThrow(): TextedContent = this as + dev.inmo.tgbotapi.types.message.content.TextedContent + +public inline fun ResendableContent.ifTextedContent(block: (TextedContent) -> T): T? = + textedContentOrNull() ?.let(block) + public inline fun ResendableContent.mediaContentOrNull(): MediaContent? = this as? dev.inmo.tgbotapi.types.message.content.MediaContent