1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-29 13:38:43 +00:00

migration to avoid using of TextPart

This commit is contained in:
InsanusMokrassar 2021-04-28 19:54:57 +06:00
parent 42a8d649cd
commit 167c214e35
48 changed files with 203 additions and 240 deletions

View File

@ -10,6 +10,7 @@
* Interface `Captioned` and `CaptionedInput` now is deprecated * Interface `Captioned` and `CaptionedInput` now is deprecated
* Most of captions usages were replaced with texts * Most of captions usages were replaced with texts
* `textSources` become main field in `TextedInput` * `textSources` become main field in `TextedInput`
* **MIGRATION** Remove all `import dev.inmo.tgbotapi.CommonAbstracts.textSources` in your project
* `textEntities` become are calculable property in `TextedInput` * `textEntities` become are calculable property in `TextedInput`
## 0.33.4 ## 0.33.4

View File

@ -17,15 +17,10 @@ interface EntitiesExplainedOutput : Explained {
interface ExplainedOutput : ParsableExplainedOutput, EntitiesExplainedOutput interface ExplainedOutput : ParsableExplainedOutput, EntitiesExplainedOutput
interface ExplainedInput : Explained { interface ExplainedInput : Explained {
val textSources: TextSourcesList
/** /**
* Full list of entities. This list WILL contain [TextPart]s with [dev.inmo.tgbotapi.types.MessageEntity.textsources.RegularTextSource] * Full list of entities. This list WILL contain [TextPart]s with [dev.inmo.tgbotapi.types.MessageEntity.textsources.RegularTextSource]
*/ */
val explanationEntities: List<TextPart> val explanationEntities: List<TextPart>
get() = textSources.toTextParts()
} }
/**
* @see ExplainedInput.explanationEntities
* @see justTextSources
*/
val ExplainedInput.textSources
get() = explanationEntities.justTextSources()

View File

@ -2,7 +2,6 @@ package dev.inmo.tgbotapi.CommonAbstracts
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourceSerializer import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourceSerializer
import dev.inmo.tgbotapi.types.MessageEntity.textsources.regular 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.captionLength
import dev.inmo.tgbotapi.types.textLength import dev.inmo.tgbotapi.types.textLength
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -44,14 +43,27 @@ interface MultilevelTextSource : TextSource {
} }
} }
@Deprecated("This class will be removed soon. Use TextSources instead")
data class TextPart( data class TextPart(
val range: IntRange, val range: IntRange,
val source: TextSource val source: TextSource
) )
@Deprecated("This method is no longer required to work with TextSources")
fun List<TextPart>.justTextSources() = map { it.source } fun List<TextPart>.justTextSources() = map { it.source }
internal fun List<TextSource>.toTextParts(preOffset: Int = 0): List<TextPart> {
var i = preOffset
return map {
TextPart(
i until (i + it.source.length),
it
).also {
i = it.range.last + 1
}
}
}
fun List<TextSource>.makeString() = joinToString("") { it.source } fun List<TextSource>.makeString() = joinToString("") { it.source }
internal fun MultilevelTextSource.textParts(offset: Int): List<TextPart> = subsources.toTextParts(offset)
fun List<TextSource>.separateForMessage(limit: IntRange, numberOfParts: Int? = null): List<List<TextSource>> { fun List<TextSource>.separateForMessage(limit: IntRange, numberOfParts: Int? = null): List<List<TextSource>> {
if (isEmpty()) { if (isEmpty()) {
return emptyList() return emptyList()

View File

@ -1,6 +1,5 @@
package dev.inmo.tgbotapi.CommonAbstracts package dev.inmo.tgbotapi.CommonAbstracts
import dev.inmo.tgbotapi.types.MessageEntity.toTextParts
import dev.inmo.tgbotapi.types.ParseMode.ParseMode import dev.inmo.tgbotapi.types.ParseMode.ParseMode
interface Texted { interface Texted {
@ -20,8 +19,6 @@ interface TextedOutput : ParsableOutput, EntitiesOutput
interface TextedInput : Texted { interface TextedInput : Texted {
/** /**
* Full list of [TextSource] built from source[TextedInput.textEntities] * Full list of [TextSource] built from source[TextedInput.textEntities]
*
* @see justTextSources
*/ */
val textSources: List<TextSource> val textSources: List<TextSource>
/** /**

View File

@ -59,7 +59,7 @@ data class EditChatMessageCaption internal constructor(
override val replyMarkup: InlineKeyboardMarkup? = null override val replyMarkup: InlineKeyboardMarkup? = null
) : EditChatMessage<MediaContent>, EditTextChatMessage, EditReplyMessage { ) : EditChatMessage<MediaContent>, EditTextChatMessage, EditReplyMessage {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text) ?.justTextSources() rawEntities ?.asTextSources(text)
} }
override fun method(): String = editMessageCaptionMethod override fun method(): String = editMessageCaptionMethod

View File

@ -48,7 +48,7 @@ data class EditInlineMessageCaption internal constructor(
override val replyMarkup: InlineKeyboardMarkup? = null override val replyMarkup: InlineKeyboardMarkup? = null
) : EditInlineMessage, EditTextChatMessage, EditReplyMessage { ) : EditInlineMessage, EditTextChatMessage, EditReplyMessage {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text) ?.justTextSources() rawEntities ?.asTextSources(text)
} }
override fun method(): String = editMessageCaptionMethod override fun method(): String = editMessageCaptionMethod

View File

@ -65,7 +65,7 @@ data class EditChatMessageText internal constructor(
override val replyMarkup: InlineKeyboardMarkup? = null override val replyMarkup: InlineKeyboardMarkup? = null
) : EditChatMessage<TextContent>, EditTextChatMessage, EditReplyMessage, EditDisableWebPagePreviewMessage { ) : EditChatMessage<TextContent>, EditTextChatMessage, EditReplyMessage, EditDisableWebPagePreviewMessage {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text) ?.justTextSources() rawEntities ?.asTextSources(text)
} }
override fun method(): String = editMessageTextMethod override fun method(): String = editMessageTextMethod

View File

@ -54,7 +54,7 @@ data class EditInlineMessageText internal constructor(
override val replyMarkup: InlineKeyboardMarkup? = null override val replyMarkup: InlineKeyboardMarkup? = null
) : EditInlineMessage, EditTextChatMessage, EditReplyMessage, EditDisableWebPagePreviewMessage { ) : EditInlineMessage, EditTextChatMessage, EditReplyMessage, EditDisableWebPagePreviewMessage {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text) ?.justTextSources() rawEntities ?.asTextSources(text)
} }
override fun method(): String = editMessageTextMethod override fun method(): String = editMessageTextMethod

View File

@ -63,7 +63,7 @@ data class CopyMessage internal constructor(
override val chatId: ChatIdentifier override val chatId: ChatIdentifier
get() = fromChatId get() = fromChatId
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
override fun method(): String = "copyMessage" override fun method(): String = "copyMessage"

View File

@ -84,7 +84,7 @@ data class SendTextMessage internal constructor(
DisableWebPagePreview DisableWebPagePreview
{ {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text) ?.justTextSources() rawEntities ?.asTextSources(text)
} }
init { init {

View File

@ -145,7 +145,7 @@ data class SendAnimationData internal constructor(
SizedSendMessageRequest<ContentMessage<AnimationContent>> SizedSendMessageRequest<ContentMessage<AnimationContent>>
{ {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -146,7 +146,7 @@ data class SendAudioData internal constructor(
Performerable Performerable
{ {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -158,7 +158,7 @@ data class SendDocumentData internal constructor(
ThumbedSendMessageRequest<ContentMessage<DocumentContent>> ThumbedSendMessageRequest<ContentMessage<DocumentContent>>
{ {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -101,7 +101,7 @@ data class SendPhotoData internal constructor(
TextableSendMessageRequest<ContentMessage<PhotoContent>> TextableSendMessageRequest<ContentMessage<PhotoContent>>
{ {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -151,7 +151,7 @@ data class SendVideoData internal constructor(
SizedSendMessageRequest<ContentMessage<VideoContent>> SizedSendMessageRequest<ContentMessage<VideoContent>>
{ {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -121,7 +121,7 @@ data class SendVoiceData internal constructor(
DuratedSendMessageRequest<ContentMessage<VoiceContent>> DuratedSendMessageRequest<ContentMessage<VoiceContent>>
{ {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -360,7 +360,7 @@ data class SendQuizPoll internal constructor(
override val requestSerializer: SerializationStrategy<*> override val requestSerializer: SerializationStrategy<*>
get() = serializer() get() = serializer()
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(explanation ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(explanation ?: return@lazy null)
} }
init { init {

View File

@ -49,6 +49,6 @@ data class InlineQueryResultAudioCachedImpl internal constructor(
) : InlineQueryResultAudioCached { ) : InlineQueryResultAudioCached {
override val type: String = inlineQueryResultAudioType override val type: String = inlineQueryResultAudioType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -60,6 +60,6 @@ data class InlineQueryResultAudioImpl internal constructor(
) : InlineQueryResultAudio { ) : InlineQueryResultAudio {
override val type: String = inlineQueryResultAudioType override val type: String = inlineQueryResultAudioType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -57,6 +57,6 @@ data class InlineQueryResultDocumentCachedImpl internal constructor(
) : InlineQueryResultDocumentCached { ) : InlineQueryResultDocumentCached {
override val type: String = inlineQueryResultDocumentType override val type: String = inlineQueryResultDocumentType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -74,6 +74,6 @@ data class InlineQueryResultDocumentImpl internal constructor(
) : InlineQueryResultDocument { ) : InlineQueryResultDocument {
override val type: String = inlineQueryResultDocumentType override val type: String = inlineQueryResultDocumentType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -53,6 +53,6 @@ data class InlineQueryResultGifCachedImpl internal constructor(
) : InlineQueryResultGifCached { ) : InlineQueryResultGifCached {
override val type: String = inlineQueryResultGifType override val type: String = inlineQueryResultGifType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -73,7 +73,7 @@ data class InlineQueryResultGifImpl internal constructor(
) : InlineQueryResultGif { ) : InlineQueryResultGif {
override val type: String = inlineQueryResultGifType override val type: String = inlineQueryResultGifType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -53,6 +53,6 @@ data class InlineQueryResultMpeg4GifCachedImpl internal constructor(
) : InlineQueryResultMpeg4GifCached { ) : InlineQueryResultMpeg4GifCached {
override val type: String = inlineQueryResultMpeg4GifType override val type: String = inlineQueryResultMpeg4GifType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -73,7 +73,7 @@ data class InlineQueryResultMpeg4GifImpl internal constructor(
) : InlineQueryResultMpeg4Gif { ) : InlineQueryResultMpeg4Gif {
override val type: String = inlineQueryResultMpeg4GifType override val type: String = inlineQueryResultMpeg4GifType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
init { init {

View File

@ -57,6 +57,6 @@ data class InlineQueryResultPhotoCachedImpl internal constructor(
) : InlineQueryResultPhotoCached { ) : InlineQueryResultPhotoCached {
override val type: String = inlineQueryResultPhotoType override val type: String = inlineQueryResultPhotoType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -68,6 +68,6 @@ data class InlineQueryResultPhotoImpl internal constructor(
) : InlineQueryResultPhoto { ) : InlineQueryResultPhoto {
override val type: String = inlineQueryResultPhotoType override val type: String = inlineQueryResultPhotoType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -57,6 +57,6 @@ data class InlineQueryResultVideoCachedImpl internal constructor(
) : InlineQueryResultVideoCached { ) : InlineQueryResultVideoCached {
override val type: String = inlineQueryResultVideoType override val type: String = inlineQueryResultVideoType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -78,6 +78,6 @@ data class InlineQueryResultVideoImpl internal constructor(
) : InlineQueryResultVideo { ) : InlineQueryResultVideo {
override val type: String = inlineQueryResultVideoType override val type: String = inlineQueryResultVideoType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -53,6 +53,6 @@ data class InlineQueryResultVoiceCachedImpl internal constructor(
) : InlineQueryResultVoiceCached { ) : InlineQueryResultVoiceCached {
override val type: String = inlineQueryResultVoiceType override val type: String = inlineQueryResultVoiceType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -66,6 +66,6 @@ data class InlineQueryResultVoiceImpl internal constructor(
) : InlineQueryResultVoice { ) : InlineQueryResultVoice {
override val type: String = inlineQueryResultVoiceType override val type: String = inlineQueryResultVoiceType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
} }

View File

@ -39,6 +39,6 @@ data class InputTextMessageContent internal constructor(
override val disableWebPagePreview: Boolean? = null override val disableWebPagePreview: Boolean? = null
) : TextedOutput, DisableWebPagePreview, InputMessageContent { ) : TextedOutput, DisableWebPagePreview, InputMessageContent {
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text) ?.justTextSources() rawEntities ?.asTextSources(text)
} }
} }

View File

@ -45,7 +45,7 @@ data class InputMediaAnimation internal constructor(
) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, TextedOutput { ) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, TextedOutput {
override val type: String = "animation" override val type: String = "animation"
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
@SerialName(mediaField) @SerialName(mediaField)

View File

@ -51,7 +51,7 @@ data class InputMediaAudio internal constructor(
) : InputMedia, AudioMediaGroupMemberInputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, Performerable { ) : InputMedia, AudioMediaGroupMemberInputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, Performerable {
override val type: String = audioInputMediaType override val type: String = audioInputMediaType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)

View File

@ -51,7 +51,7 @@ data class InputMediaDocument internal constructor(
) : InputMedia, DocumentMediaGroupMemberInputMedia, ThumbedInputMedia { ) : InputMedia, DocumentMediaGroupMemberInputMedia, ThumbedInputMedia {
override val type: String = documentInputMediaType override val type: String = documentInputMediaType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)

View File

@ -35,7 +35,7 @@ data class InputMediaPhoto internal constructor(
) : InputMedia, VisualMediaGroupMemberInputMedia { ) : InputMedia, VisualMediaGroupMemberInputMedia {
override val type: String = photoInputMediaType override val type: String = photoInputMediaType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)

View File

@ -46,7 +46,7 @@ data class InputMediaVideo internal constructor (
) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, VisualMediaGroupMemberInputMedia { ) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, VisualMediaGroupMemberInputMedia {
override val type: String = videoInputMediaType override val type: String = videoInputMediaType
override val entities: List<TextSource>? by lazy { override val entities: List<TextSource>? by lazy {
rawEntities ?.asTextParts(text ?: return@lazy null) ?.justTextSources() rawEntities ?.asTextSources(text ?: return@lazy null)
} }
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)

View File

@ -3,8 +3,6 @@ package dev.inmo.tgbotapi.types.MessageEntity
import dev.inmo.tgbotapi.CommonAbstracts.* import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.* import dev.inmo.tgbotapi.types.MessageEntity.textsources.*
import dev.inmo.tgbotapi.types.User import dev.inmo.tgbotapi.types.User
import dev.inmo.tgbotapi.utils.internal.fullListOfSubSource
import dev.inmo.tgbotapi.utils.internal.shiftSourcesToTheLeft
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@ -15,70 +13,115 @@ internal data class RawMessageEntity(
val url: String? = null, val url: String? = null,
val user: User? = null, val user: User? = null,
val language: String? = null val language: String? = null
) ) {
internal val range by lazy {
internal fun RawMessageEntity.asTextParts( offset until (offset + length)
source: String,
subParts: List<TextPart>
): List<TextPart> {
val sourceSubstring: String = source.substring(offset, offset + length)
val range = offset until (offset + length)
val shiftedSubSources = sourceSubstring.fullListOfSubSource(subParts.shiftSourcesToTheLeft(offset)).justTextSources()
return when (type) {
"mention" -> MentionTextSource(sourceSubstring, shiftedSubSources)
"hashtag" -> HashTagTextSource(sourceSubstring, shiftedSubSources)
"cashtag" -> CashTagTextSource(sourceSubstring, shiftedSubSources)
"bot_command" -> BotCommandTextSource(sourceSubstring)
"url" -> URLTextSource(sourceSubstring)
"email" -> EMailTextSource(sourceSubstring, shiftedSubSources)
"phone_number" -> PhoneNumberTextSource(sourceSubstring, shiftedSubSources)
"bold" -> BoldTextSource(sourceSubstring, shiftedSubSources)
"italic" -> ItalicTextSource(sourceSubstring, shiftedSubSources)
"code" -> CodeTextSource(sourceSubstring)
"pre" -> PreTextSource(sourceSubstring, language)
"text_link" -> TextLinkTextSource(sourceSubstring, url ?: throw IllegalStateException("URL must not be null for text link"))
"text_mention" -> TextMentionTextSource(sourceSubstring, user ?: throw IllegalStateException("User must not be null for text mention"), shiftedSubSources)
"underline" -> UnderlineTextSource(sourceSubstring, shiftedSubSources)
"strikethrough" -> StrikethroughTextSource(sourceSubstring, shiftedSubSources)
else -> RegularTextSource(sourceSubstring)
}.let {
val part = TextPart(range, it)
if (it !is MultilevelTextSource && subParts.isNotEmpty()) {
(subParts + part).sortedBy { currentPart -> currentPart.range.first }
} else {
listOf(part)
}
} }
} }
internal fun createTextPart(originalFullString: String, entities: RawMessageEntities): List<TextPart> { internal fun RawMessageEntity.asTextSource(
val mutableEntities = entities.toMutableList() source: String,
mutableEntities.sortBy { it.offset } subParts: List<TextSource>
val resultList = mutableListOf<TextPart>() ): TextSource {
val sourceSubstring: String = source.substring(range)
val subPartsWithRegulars by lazy {
subParts.fillWithRegulars(sourceSubstring)
}
return when (type) {
"mention" -> MentionTextSource(sourceSubstring, subPartsWithRegulars)
"hashtag" -> HashTagTextSource(sourceSubstring, subPartsWithRegulars)
"cashtag" -> CashTagTextSource(sourceSubstring, subPartsWithRegulars)
"bot_command" -> BotCommandTextSource(sourceSubstring)
"url" -> URLTextSource(sourceSubstring)
"email" -> EMailTextSource(sourceSubstring, subPartsWithRegulars)
"phone_number" -> PhoneNumberTextSource(sourceSubstring, subPartsWithRegulars)
"bold" -> BoldTextSource(sourceSubstring, subPartsWithRegulars)
"italic" -> ItalicTextSource(sourceSubstring, subPartsWithRegulars)
"code" -> CodeTextSource(sourceSubstring)
"pre" -> PreTextSource(sourceSubstring, language)
"text_link" -> TextLinkTextSource(sourceSubstring, url ?: throw IllegalStateException("URL must not be null for text link"))
"text_mention" -> TextMentionTextSource(sourceSubstring, user ?: throw IllegalStateException("User must not be null for text mention"), subPartsWithRegulars)
"underline" -> UnderlineTextSource(sourceSubstring, subPartsWithRegulars)
"strikethrough" -> StrikethroughTextSource(sourceSubstring, subPartsWithRegulars)
else -> RegularTextSource(sourceSubstring)
}
}
private inline operator fun <T : Comparable<T>> ClosedRange<T>.contains(other: ClosedRange<T>): Boolean {
return start <= other.start && endInclusive >= other.endInclusive
}
internal fun List<TextSource>.fillWithRegulars(source: String): List<TextSource> {
var index = 0
val result = mutableListOf<TextSource>()
for (i in 0 until size) {
val textSource = get(i)
val thisSourceInStart = source.startsWith(textSource.source, index)
if (!thisSourceInStart) {
val regularEndIndex = source.indexOf(textSource.source)
result.add(regular(source.substring(index, regularEndIndex)))
index = regularEndIndex
}
result.add(textSource)
index += textSource.source.length
}
if (index != source.length) {
result.add(regular(source.substring(index, source.length)))
}
return result
}
private fun createTextSources(originalFullString: String, entities: RawMessageEntities): List<TextSource> {
val mutableEntities = entities.toMutableList().apply { sortBy { it.offset } }
val resultList = mutableListOf<TextSource>()
while (mutableEntities.isNotEmpty()) { while (mutableEntities.isNotEmpty()) {
val currentFirst = mutableEntities.removeAt(0) var parent = mutableEntities.removeFirst()
val subEntities = if (mutableEntities.isNotEmpty()) { val subentities = mutableListOf<RawMessageEntity>()
val lastIndex = currentFirst.offset + currentFirst.length val toAddCutted = mutableListOf<RawMessageEntity>()
val subEntities = mutableListOf<RawMessageEntity>() while (mutableEntities.isNotEmpty()) {
while (mutableEntities.isNotEmpty()) { val potentialParent = mutableEntities.first()
val currentPossibleSubEntity = mutableEntities.first() when {
if (currentPossibleSubEntity.offset < lastIndex) { potentialParent.range.first > parent.range.last -> break
subEntities.add(currentPossibleSubEntity) potentialParent.range in parent.range -> {
mutableEntities.removeAt(0) subentities.add(potentialParent)
} else { }
break potentialParent.offset == parent.offset && potentialParent.length > parent.length -> {
subentities.add(parent)
parent = potentialParent
}
else -> { // need to cut
toAddCutted.add(potentialParent)
} }
} }
subEntities mutableEntities.remove(potentialParent)
} else {
emptyList<RawMessageEntity>()
} }
val subtextSources = if (subentities.isNotEmpty()) {
resultList.addAll( mutableEntities.removeAll(subentities)
currentFirst.asTextParts( if (toAddCutted.isNotEmpty()) {
val borderIndex = parent.range.last + 1
mutableEntities.addAll(
0,
toAddCutted.map {
val firstLength = borderIndex - it.offset
subentities.add(it.copy(length = firstLength))
it.copy(
offset = borderIndex,
length = it.length - firstLength
)
}
)
}
createTextSources(originalFullString, subentities)
} else {
emptyList()
}
resultList.add(
parent.asTextSource(
originalFullString, originalFullString,
createTextPart(originalFullString, subEntities) subtextSources
) )
) )
} }
@ -86,46 +129,41 @@ internal fun createTextPart(originalFullString: String, entities: RawMessageEnti
return resultList return resultList
} }
internal fun TextPart.asRawMessageEntities(): List<RawMessageEntity> { internal fun TextSource.toRawMessageEntities(offset: Int = 0): List<RawMessageEntity> {
val source = source val source = source
val length = range.last - range.first + 1 val length = source.length
return listOfNotNull( return listOfNotNull(
when (source) { when (this) {
is MentionTextSource -> RawMessageEntity("mention", range.first, length) is MentionTextSource -> RawMessageEntity("mention", offset, length)
is HashTagTextSource -> RawMessageEntity("hashtag", range.first, length) is HashTagTextSource -> RawMessageEntity("hashtag", offset, length)
is CashTagTextSource -> RawMessageEntity("cashtag", range.first, length) is CashTagTextSource -> RawMessageEntity("cashtag", offset, length)
is BotCommandTextSource -> RawMessageEntity("bot_command", range.first, length) is BotCommandTextSource -> RawMessageEntity("bot_command", offset, length)
is URLTextSource -> RawMessageEntity("url", range.first, length) is URLTextSource -> RawMessageEntity("url", offset, length)
is EMailTextSource -> RawMessageEntity("email", range.first, length) is EMailTextSource -> RawMessageEntity("email", offset, length)
is PhoneNumberTextSource -> RawMessageEntity("phone_number", range.first, length) is PhoneNumberTextSource -> RawMessageEntity("phone_number", offset, length)
is BoldTextSource -> RawMessageEntity("bold", range.first, length) is BoldTextSource -> RawMessageEntity("bold", offset, length)
is ItalicTextSource -> RawMessageEntity("italic", range.first, length) is ItalicTextSource -> RawMessageEntity("italic", offset, length)
is CodeTextSource -> RawMessageEntity("code", range.first, length) is CodeTextSource -> RawMessageEntity("code", offset, length)
is PreTextSource -> RawMessageEntity("pre", range.first, length, language = source.language) is PreTextSource -> RawMessageEntity("pre", offset, length, language = language)
is TextLinkTextSource -> RawMessageEntity("text_link", range.first, length, source.url) is TextLinkTextSource -> RawMessageEntity("text_link", offset, length, url)
is TextMentionTextSource -> RawMessageEntity("text_mention", range.first, length, user = source.user) is TextMentionTextSource -> RawMessageEntity("text_mention", offset, length, user = user)
is UnderlineTextSource -> RawMessageEntity("underline", range.first, length) is UnderlineTextSource -> RawMessageEntity("underline", offset, length)
is StrikethroughTextSource -> RawMessageEntity("strikethrough", range.first, length) is StrikethroughTextSource -> RawMessageEntity("strikethrough", offset, length)
else -> null else -> null
} }
) + if (source is MultilevelTextSource) { ) + if (this is MultilevelTextSource) {
source.textParts(range.first).asRawMessageEntities() subsources.toRawMessageEntities(offset)
} else { } else {
emptyList() emptyList()
} }
} }
internal fun List<TextPart>.asRawMessageEntities(): List<RawMessageEntity> = flatMap { it.asRawMessageEntities() }
internal fun List<TextSource>.toTextParts(preOffset: Int = 0): List<TextPart> { internal fun List<TextSource>.toRawMessageEntities(preOffset: Int = 0): List<RawMessageEntity> {
var i = preOffset var i = preOffset
return map { return flatMap {
TextPart( it.toRawMessageEntities(i).also {
i until (i + it.source.length), i += it.maxByOrNull { it.length }!!.length + 1
it
).also {
i = it.range.last + 1
} }
} }
} }
@ -136,10 +174,8 @@ fun String.removeLeading(word: String) = if (startsWith(word)){
this this
} }
internal fun List<TextSource>.toRawMessageEntities(): List<RawMessageEntity> = toTextParts().asRawMessageEntities() internal fun List<TextSource>.toRawMessageEntities(): List<RawMessageEntity> = toRawMessageEntities(0)
internal fun RawMessageEntities.asTextParts(sourceString: String): List<TextPart> = sourceString.fullListOfSubSource( internal fun RawMessageEntities.asTextSources(sourceString: String): List<TextSource> = createTextSources(sourceString, this).fillWithRegulars(sourceString)
createTextPart(sourceString, this)
)
internal typealias RawMessageEntities = List<RawMessageEntity> internal typealias RawMessageEntities = List<RawMessageEntity>

View File

@ -2,14 +2,7 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
import dev.inmo.tgbotapi.CommonAbstracts.TextSource 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.*
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
private val baseSerializers: Map<String, KSerializer<out TextSource>> = mapOf( private val baseSerializers: Map<String, KSerializer<out TextSource>> = mapOf(
"regular" to RegularTextSource.serializer(), "regular" to RegularTextSource.serializer(),

View File

@ -1,9 +1,8 @@
package dev.inmo.tgbotapi.types.games package dev.inmo.tgbotapi.types.games
import dev.inmo.tgbotapi.CommonAbstracts.justTextSources
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.MessageEntity.RawMessageEntities import dev.inmo.tgbotapi.types.MessageEntity.RawMessageEntities
import dev.inmo.tgbotapi.types.MessageEntity.asTextParts import dev.inmo.tgbotapi.types.MessageEntity.asTextSources
import dev.inmo.tgbotapi.types.files.* import dev.inmo.tgbotapi.types.files.*
import kotlinx.serialization.* import kotlinx.serialization.*
@ -29,7 +28,7 @@ internal data class RawGame(
description, description,
photo, photo,
text, text,
text ?.let { _ -> textEntities.asTextParts(text).justTextSources() } ?: emptyList(), text ?.let { _ -> textEntities.asTextSources(text) } ?: emptyList(),
animation animation
) )
} }

View File

@ -1,9 +1,8 @@
package dev.inmo.tgbotapi.types.message package dev.inmo.tgbotapi.types.message
import dev.inmo.tgbotapi.CommonAbstracts.justTextSources
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.MessageEntity.RawMessageEntities import dev.inmo.tgbotapi.types.MessageEntity.RawMessageEntities
import dev.inmo.tgbotapi.types.MessageEntity.asTextParts import dev.inmo.tgbotapi.types.MessageEntity.asTextSources
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.abstracts.* import dev.inmo.tgbotapi.types.chat.abstracts.*
import dev.inmo.tgbotapi.types.dice.Dice import dev.inmo.tgbotapi.types.dice.Dice
@ -103,11 +102,11 @@ internal data class RawMessage(
) { ) {
private val content: MessageContent? by lazy { private val content: MessageContent? by lazy {
val adaptedCaptionEntities = caption ?.let { val adaptedCaptionEntities = caption ?.let {
(caption_entities ?: emptyList()).asTextParts(caption).justTextSources() (caption_entities ?: emptyList()).asTextSources(caption)
} ?: emptyList() } ?: emptyList()
when { when {
text != null -> TextContent(text, (entities ?: emptyList()).asTextParts(text).justTextSources()) text != null -> TextContent(text, (entities ?: emptyList()).asTextSources(text))
audio != null -> AudioContent( audio != null -> AudioContent(
audio, audio,
caption, caption,

View File

@ -2,8 +2,7 @@ package dev.inmo.tgbotapi.types.polls
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import com.soywiz.klock.TimeSpan import com.soywiz.klock.TimeSpan
import dev.inmo.tgbotapi.CommonAbstracts.ExplainedInput import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.MessageEntity.* import dev.inmo.tgbotapi.types.MessageEntity.*
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
@ -136,7 +135,7 @@ data class QuizPoll(
*/ */
val correctOptionId: Int? = null, val correctOptionId: Int? = null,
override val explanation: String? = null, override val explanation: String? = null,
override val explanationEntities: List<TextPart> = emptyList(), override val textSources: List<TextSource> = emptyList(),
override val isClosed: Boolean = false, override val isClosed: Boolean = false,
override val isAnonymous: Boolean = false, override val isAnonymous: Boolean = false,
override val scheduledCloseInfo: ScheduledCloseInfo? = null override val scheduledCloseInfo: ScheduledCloseInfo? = null
@ -159,7 +158,7 @@ internal object PollSerializer : KSerializer<Poll> {
rawPoll.votesCount, rawPoll.votesCount,
rawPoll.correctOptionId, rawPoll.correctOptionId,
rawPoll.explanation, rawPoll.explanation,
rawPoll.explanation?.let { rawPoll.explanationEntities.asTextParts(it) } ?: emptyList(), rawPoll.explanation?.let { rawPoll.explanationEntities.asTextSources(it) } ?: emptyList(),
rawPoll.isClosed, rawPoll.isClosed,
rawPoll.isAnonymous, rawPoll.isAnonymous,
rawPoll.scheduledCloseInfo rawPoll.scheduledCloseInfo
@ -211,7 +210,7 @@ internal object PollSerializer : KSerializer<Poll> {
regularPollType, regularPollType,
correctOptionId = value.correctOptionId, correctOptionId = value.correctOptionId,
explanation = value.explanation, explanation = value.explanation,
explanationEntities = value.explanationEntities.asRawMessageEntities(), explanationEntities = value.textSources.toRawMessageEntities(),
openPeriod = (closeInfo as? ApproximateScheduledCloseInfo) ?.openDuration ?.seconds ?.toLong(), openPeriod = (closeInfo as? ApproximateScheduledCloseInfo) ?.openDuration ?.seconds ?.toLong(),
closeDate = (closeInfo as? ExactScheduledCloseInfo) ?.closeDateTime ?.unixMillisLong ?.div(1000L) closeDate = (closeInfo as? ExactScheduledCloseInfo) ?.closeDateTime ?.unixMillisLong ?.div(1000L)
) )

View File

@ -1,77 +1,12 @@
package dev.inmo.tgbotapi.utils.internal package dev.inmo.tgbotapi.utils.internal
import dev.inmo.tgbotapi.CommonAbstracts.* import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.RegularTextSource import dev.inmo.tgbotapi.types.MessageEntity.textsources.regular
import dev.inmo.tgbotapi.types.UserId import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.link import dev.inmo.tgbotapi.types.link
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Link import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Link
import dev.inmo.tgbotapi.utils.extensions.toHtml import dev.inmo.tgbotapi.utils.extensions.toHtml
internal fun String.fullListOfSubSource(sourceList: List<TextPart>): List<TextPart> {
val sortedSourceList = sourceList.sortedBy { it.range.first }.toMutableList()
var previousLastIndex = 0
val newSubSources = mutableListOf<TextPart>()
while (sortedSourceList.isNotEmpty()) {
val topSource = sortedSourceList.removeAt(0)
if (topSource.range.first - previousLastIndex > 0) {
val range = previousLastIndex until topSource.range.first
newSubSources.add(
TextPart(
range,
RegularTextSource(
substring(range)
)
)
)
}
newSubSources.add(topSource)
previousLastIndex = topSource.range.last + 1
}
if (length > previousLastIndex) {
val range = previousLastIndex until length
newSubSources.add(
TextPart(
range,
RegularTextSource(
substring(range)
)
)
)
}
return newSubSources
}
internal fun List<TextPart>.shiftSourcesToTheLeft(shiftCount: Int = 1): List<TextPart> {
return mapNotNull {
val first = (it.range.first - shiftCount).let { firstCalculated ->
if (firstCalculated < 0) {
0
} else {
firstCalculated
}
}
val last = (it.range.last - shiftCount).let { lastCalculated ->
if (lastCalculated < 0) {
0
} else {
lastCalculated
}
}
it.copy(range = first .. last).let { newSubSource ->
if (newSubSource.range.isEmpty()) {
null
} else {
newSubSource
}
}
}
}
private fun List<TextSource>.joinSubSourcesMarkdownV2() = joinToString("") { private fun List<TextSource>.joinSubSourcesMarkdownV2() = joinToString("") {
it.markdownV2 it.markdownV2
} }

View File

@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.types.MessageEntity package dev.inmo.tgbotapi.types.MessageEntity
import dev.inmo.tgbotapi.CommonAbstracts.TextPart import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.types.MessageEntity.textsources.* import dev.inmo.tgbotapi.types.MessageEntity.textsources.*
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -36,19 +36,19 @@ internal val testTextEntities = listOf(
RawMessageEntity( RawMessageEntity(
"mention", "mention",
39, 39,
6 8
) )
) )
fun List<TextPart>.testTextParts() { fun List<TextSource>.testTextSources() {
assertTrue (first().source is RegularTextSource) assertTrue (first() is RegularTextSource)
assertTrue (get(1).source is BoldTextSource) assertTrue (get(1) is BoldTextSource)
assertTrue (get(2).source is RegularTextSource) assertTrue (get(2) is RegularTextSource)
assertTrue (get(3).source is HashTagTextSource) assertTrue (get(3) is HashTagTextSource)
assertTrue (get(4).source is RegularTextSource) assertTrue (get(4) is RegularTextSource)
assertTrue (get(5).source is MentionTextSource) assertTrue (get(5) is MentionTextSource)
val boldSource = get(1).source as BoldTextSource val boldSource = get(1) as BoldTextSource
assertTrue (boldSource.subsources.first() is ItalicTextSource) assertTrue (boldSource.subsources.first() is ItalicTextSource)
assertTrue (boldSource.subsources[1] is RegularTextSource) assertTrue (boldSource.subsources[1] is RegularTextSource)
assertTrue (boldSource.subsources[2] is StrikethroughTextSource) assertTrue (boldSource.subsources[2] is StrikethroughTextSource)

View File

@ -48,7 +48,7 @@ class StringFormattingTests {
hashtag("tag") + hashtag("tag") +
" and " + " and " +
mention("mention") mention("mention")
sources.toTextParts().testTextParts() sources.testTextSources()
assertEquals(formattedV2Text, sources.toMarkdownV2Texts().first()) assertEquals(formattedV2Text, sources.toMarkdownV2Texts().first())
assertEquals(formattedHtmlText, sources.toHtmlTexts().first()) assertEquals(formattedHtmlText, sources.toHtmlTexts().first())

View File

@ -1,6 +1,5 @@
package dev.inmo.tgbotapi.types.MessageEntity package dev.inmo.tgbotapi.types.MessageEntity
import dev.inmo.tgbotapi.CommonAbstracts.justTextSources
import dev.inmo.tgbotapi.extensions.utils.formatting.toHtmlTexts import dev.inmo.tgbotapi.extensions.utils.formatting.toHtmlTexts
import dev.inmo.tgbotapi.extensions.utils.formatting.toMarkdownV2Texts import dev.inmo.tgbotapi.extensions.utils.formatting.toMarkdownV2Texts
import kotlin.test.Test import kotlin.test.Test
@ -9,23 +8,23 @@ import kotlin.test.assertEquals
class TextPartsCreatingTests { class TextPartsCreatingTests {
@Test @Test
fun testThatTextWithMultilevelPartsCorrectlyCreating() { fun testThatTextWithMultilevelPartsCorrectlyCreating() {
val textParts = testTextEntities.asTextParts(testText) val textSources = testTextEntities.asTextSources(testText)
textParts.testTextParts() textSources.testTextSources()
assertEquals( assertEquals(
formattedV2Text, formattedV2Text,
textParts.justTextSources().toMarkdownV2Texts().first() textSources.toMarkdownV2Texts().first()
) )
} }
@Test @Test
fun testThatTextWithMultilevelPartsCorrectlyCreatingInHtml() { fun testThatTextWithMultilevelPartsCorrectlyCreatingInHtml() {
val textParts = testTextEntities.asTextParts(testText) val textSources = testTextEntities.asTextSources(testText)
textParts.testTextParts() textSources.testTextSources()
assertEquals( assertEquals(
formattedHtmlText, formattedHtmlText,
textParts.justTextSources().toHtmlTexts().first() textSources.toHtmlTexts().first()
) )
} }
} }

View File

@ -1,6 +1,5 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.tgbotapi.CommonAbstracts.textSources
import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.utils.* import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage

View File

@ -1,7 +1,6 @@
package dev.inmo.tgbotapi.extensions.utils.shortcuts package dev.inmo.tgbotapi.extensions.utils.shortcuts
import dev.inmo.tgbotapi.CommonAbstracts.TextSource import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.CommonAbstracts.textSources
import dev.inmo.tgbotapi.extensions.utils.onlyTextContentMessages import dev.inmo.tgbotapi.extensions.utils.onlyTextContentMessages
import dev.inmo.tgbotapi.extensions.utils.updates.asContentMessagesFlow import dev.inmo.tgbotapi.extensions.utils.updates.asContentMessagesFlow
import dev.inmo.tgbotapi.types.MessageEntity.textsources.BotCommandTextSource import dev.inmo.tgbotapi.types.MessageEntity.textsources.BotCommandTextSource