Merge pull request #295 from InsanusMokrassar/0.32.5

0.32.5
This commit is contained in:
InsanusMokrassar 2021-02-09 19:13:42 +06:00 committed by GitHub
commit 2e27b8b64d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 243 additions and 199 deletions

View File

@ -1,5 +1,16 @@
# TelegramBotAPI changelog
## 0.32.5
* `Core`:
* Add `mention` variants for user ids and receiver variants ([#294](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/294))
* Now `AbstractRequestCallFactory` will set up one-second delay for zero timeouts in `GetUpdate` requests
* Several extensions for `TelegramBotAPI` like `retrieveAccumulatedUpdates` have been added as a solution for
[#293](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/293)
* Links for `tg://user?id=<user_id>` have been updated ([#292](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/292))
* All usages of captions or texts in resends and same things have been replaced with `textSources`
* Global `defaultParseMode` has been added ([#291](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/291))
## 0.32.4
* `Common`:

View File

@ -17,6 +17,6 @@ micro_utils_version=0.4.24
javax_activation_version=1.1.1
library_group=dev.inmo
library_version=0.32.4
library_version=0.32.5
github_release_plugin_version=2.2.12

View File

@ -60,6 +60,7 @@ kotlin {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
implementation project(":tgbotapi.extensions.utils")
}
}

View File

@ -16,6 +16,8 @@ import io.ktor.http.ContentType
import kotlinx.serialization.json.Json
import kotlin.collections.set
var defaultUpdateTimeoutForZeroDelay = 1000L
abstract class AbstractRequestCallFactory : KtorCallFactory {
private val methodsCache: MutableMap<String, String> = mutableMapOf()
override suspend fun <T : Any> makeCall(
@ -41,6 +43,11 @@ abstract class AbstractRequestCallFactory : KtorCallFactory {
requestTimeoutMillis = customTimeoutMillis
socketTimeoutMillis = customTimeoutMillis
}
} else {
timeout {
requestTimeoutMillis = defaultUpdateTimeoutForZeroDelay
socketTimeoutMillis = defaultUpdateTimeoutForZeroDelay
}
}
}
}

View File

@ -1,5 +1,7 @@
package dev.inmo.tgbotapi.types
import dev.inmo.micro_utils.common.Warning
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import kotlinx.serialization.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@ -17,9 +19,20 @@ data class ChatId(
val chatId: Identifier
) : ChatIdentifier()
val ChatId.link: String
get() = "tg://user?id=$chatId"
/**
* https://core.telegram.org/bots/api#formatting-options
*/
@Warning("This API have restrictions in Telegram System")
val Identifier.link: String
get() = "tg://user?id=$this"
/**
* https://core.telegram.org/bots/api#formatting-options
*/
@Warning("This API have restrictions in Telegram System")
val UserId.link: String
get() = chatId.link
val User.link: String
get() = id.link
typealias UserId = ChatId

View File

@ -74,3 +74,15 @@ fun AudioFile.toInputMediaAudio(
title,
thumb ?.fileId
)
fun AudioFile.toInputMediaAudio(
textSources: TextSourcesList = emptyList(),
title: String? = this.title
): InputMediaAudio = InputMediaAudio(
fileId,
textSources,
duration,
performer,
title,
thumb ?.fileId
)

View File

@ -70,3 +70,11 @@ fun DocumentFile.toInputMediaDocument(
parseMode,
thumb ?.fileId
)
fun DocumentFile.toInputMediaDocument(
textSources: TextSourcesList = emptyList()
) = InputMediaDocument(
fileId,
textSources,
thumb ?.fileId
)

View File

@ -53,3 +53,10 @@ fun PhotoSize.toInputMediaPhoto(
caption,
parseMode
)
fun PhotoSize.toInputMediaPhoto(
textSources: TextSourcesList = emptyList()
): InputMediaPhoto = InputMediaPhoto(
fileId,
textSources
)

View File

@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.types.User
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
@ -21,6 +21,26 @@ data class TextMentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstr
@Suppress("NOTHING_TO_INLINE")
inline fun mention(parts: List<TextSource>, user: User) = TextMentionTextSource(parts.makeString(), user, parts)
@Suppress("NOTHING_TO_INLINE")
inline fun User.mention(parts: List<TextSource>) = mention(parts, this)
@Suppress("NOTHING_TO_INLINE")
inline fun mention(parts: List<TextSource>, userId: UserId) = mention(parts, CommonUser(userId, ""))
@Suppress("NOTHING_TO_INLINE")
inline fun UserId.mention(parts: List<TextSource>) = mention(parts, this)
@Suppress("NOTHING_TO_INLINE")
inline fun mention(parts: List<TextSource>, id: Identifier) = mention(parts, UserId(id))
@Suppress("NOTHING_TO_INLINE")
inline fun Identifier.mention(parts: List<TextSource>) = mention(parts, this)
@Suppress("NOTHING_TO_INLINE")
inline fun mention(user: User, vararg parts: TextSource) = mention(parts.toList(), user)
@Suppress("NOTHING_TO_INLINE")
inline fun mention(text: String, user: User) = mention(user, regular(text))
@Suppress("NOTHING_TO_INLINE")
inline fun User.mention(text: String) = mention(this, regular(text))
@Suppress("NOTHING_TO_INLINE")
inline fun mention(text: String, userId: UserId) = mention(text, CommonUser(userId, ""))
@Suppress("NOTHING_TO_INLINE")
inline fun UserId.mention(text: String) = mention(text, this)
@Suppress("NOTHING_TO_INLINE")
inline fun mention(text: String, id: Identifier) = mention(text, UserId(id))
@Suppress("NOTHING_TO_INLINE")
inline fun Identifier.mention(text: String) = mention(text, this)

View File

@ -35,12 +35,21 @@ typealias Markdown = MarkdownParseMode
typealias MarkdownV2 = MarkdownV2ParseMode
typealias HTML = HTMLParseMode
/**
* This variable respects to default parse mode used in places like next:
*
* * [dev.inmo.tgbotapi.types.message.content.TextContent.createResends]
* *
*/
var defaultParseMode: ParseMode = HTML
@Serializer(ParseMode::class)
internal object ParseModeSerializerObject : KSerializer<ParseMode> {
override fun deserialize(decoder: Decoder): ParseMode {
return when (decoder.decodeString()) {
MarkdownParseMode.parseModeName -> MarkdownParseMode
HTMLParseMode.parseModeName -> HTMLParseMode
Markdown.parseModeName -> Markdown
MarkdownV2.parseModeName -> MarkdownV2
HTML.parseModeName -> HTML
else -> throw IllegalArgumentException("Unknown parse mode")
}
}

View File

@ -1,5 +1,6 @@
package dev.inmo.tgbotapi.types.files
import dev.inmo.tgbotapi.CommonAbstracts.TextSourcesList
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.InputMedia.InputMediaVideo
@ -44,3 +45,15 @@ inline fun VideoFile.toInputMediaVideo(
duration,
thumb ?.fileId
)
@Suppress("NOTHING_TO_INLINE")
inline fun VideoFile.toInputMediaVideo(
textSources: TextSourcesList
) = InputMediaVideo(
fileId,
textSources,
width,
height,
duration,
thumb ?.fileId
)

View File

@ -1,7 +1,6 @@
package dev.inmo.tgbotapi.types.message.content
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.CommonAbstracts.TextedInput
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.SendTextMessage
import dev.inmo.tgbotapi.types.ChatIdentifier
@ -10,7 +9,6 @@ import dev.inmo.tgbotapi.types.ParseMode.*
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent
import dev.inmo.tgbotapi.utils.internal.*
data class TextContent(
override val text: String,
@ -24,8 +22,7 @@ data class TextContent(
replyMarkup: KeyboardMarkup?
): Request<ContentMessage<TextContent>> = SendTextMessage(
chatId,
toHtmlTexts().first(),
HTMLParseMode,
textSources,
false,
disableNotification,
replyToMessageId,
@ -33,42 +30,36 @@ data class TextContent(
replyMarkup
)
@Deprecated(
"Useless due to fact that createResend currently use textSource and that will guarantee correct sending of message",
ReplaceWith("createResend")
)
override fun createResends(
chatId: ChatIdentifier,
disableNotification: Boolean,
replyToMessageId: MessageIdentifier?,
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?
): List<Request<ContentMessage<TextContent>>> = createResends(
chatId,
disableNotification,
replyToMessageId,
allowSendingWithoutReply,
replyMarkup,
HTMLParseMode
): List<Request<ContentMessage<TextContent>>> = listOf(
createResend(
chatId,
disableNotification,
replyToMessageId,
allowSendingWithoutReply,
replyMarkup
)
)
@Deprecated(
"Useless due to fact that createResend currently use textSource and that will guarantee correct sending of message",
ReplaceWith("createResend")
)
fun createResends(
chatId: ChatIdentifier,
disableNotification: Boolean,
replyToMessageId: MessageIdentifier?,
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?,
parseMode: ParseMode = HTMLParseMode
): List<Request<ContentMessage<TextContent>>> = when (parseMode) {
is MarkdownParseMode -> toMarkdownTexts()
is MarkdownV2ParseMode -> toMarkdownV2Texts()
is HTMLParseMode -> toHtmlTexts()
}.map {
SendTextMessage(
chatId,
it,
parseMode,
false,
disableNotification,
replyToMessageId,
allowSendingWithoutReply,
replyMarkup
)
}
parseMode: ParseMode = defaultParseMode
): List<Request<ContentMessage<TextContent>>> = createResends(chatId, disableNotification, replyToMessageId, allowSendingWithoutReply, replyMarkup)
}

View File

@ -1,21 +1,16 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.CaptionedInput
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendAnimation
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaAnimation
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.AnimationFile
import dev.inmo.tgbotapi.types.files.DocumentFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
import dev.inmo.tgbotapi.utils.internal.toHtmlCaptions
import dev.inmo.tgbotapi.utils.internal.toMarkdownV2Captions
data class AnimationContent(
override val media: AnimationFile,
@ -33,8 +28,7 @@ data class AnimationContent(
chatId,
media.fileId,
media.thumb ?.fileId,
toHtmlCaptions().firstOrNull(),
HTMLParseMode,
textSources,
media.duration,
media.width,
media.height,
@ -46,8 +40,7 @@ data class AnimationContent(
override fun asInputMedia(): InputMediaAnimation = InputMediaAnimation(
media.fileId,
toMarkdownV2Captions().firstOrNull(),
MarkdownV2,
textSources,
media.width,
media.height,
media.duration,

View File

@ -1,18 +1,17 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.CommonAbstracts.textSources
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendAudio
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaAudio
import dev.inmo.tgbotapi.types.InputMedia.toInputMediaAudio
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.AudioFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.AudioMediaGroupContent
import dev.inmo.tgbotapi.utils.internal.toHtmlCaptions
data class AudioContent(
override val media: AudioFile,
@ -29,8 +28,7 @@ data class AudioContent(
chatId,
media.fileId,
media.thumb ?.fileId,
toHtmlCaptions().firstOrNull(),
HTMLParseMode,
textSources,
media.duration,
media.performer,
media.title,
@ -42,8 +40,5 @@ data class AudioContent(
override fun toMediaGroupMemberInputMedia(): InputMediaAudio = asInputMedia()
override fun asInputMedia(): InputMediaAudio = media.toInputMediaAudio(
toHtmlCaptions().firstOrNull(),
HTMLParseMode
)
override fun asInputMedia(): InputMediaAudio = media.toInputMediaAudio(textSources)
}

View File

@ -1,21 +1,18 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.CaptionedInput
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendDocument
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaDocument
import dev.inmo.tgbotapi.types.InputMedia.toInputMediaDocument
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.DocumentFile
import dev.inmo.tgbotapi.types.files.asDocumentFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
import dev.inmo.tgbotapi.utils.internal.toHtmlCaptions
data class DocumentContent(
override val media: DocumentFile,
@ -32,8 +29,7 @@ data class DocumentContent(
chatId,
media.fileId,
media.thumb ?.fileId,
toHtmlCaptions().firstOrNull(),
HTMLParseMode,
textSources,
disableNotification,
replyToMessageId,
allowSendingWithoutReply,
@ -42,10 +38,7 @@ data class DocumentContent(
override fun toMediaGroupMemberInputMedia(): InputMediaDocument = asInputMedia()
override fun asInputMedia(): InputMediaDocument = media.toInputMediaDocument(
toHtmlCaptions().firstOrNull(),
HTMLParseMode
)
override fun asInputMedia(): InputMediaDocument = media.toInputMediaDocument(textSources)
}
@Suppress("NOTHING_TO_INLINE")

View File

@ -1,19 +1,18 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.CommonAbstracts.textSources
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendPhoto
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaPhoto
import dev.inmo.tgbotapi.types.InputMedia.toInputMediaPhoto
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaCollectionContent
import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent
import dev.inmo.tgbotapi.utils.internal.toHtmlCaptions
data class PhotoContent(
override val mediaCollection: Photo,
@ -31,8 +30,7 @@ data class PhotoContent(
): Request<ContentMessage<PhotoContent>> = SendPhoto(
chatId,
media.fileId,
toHtmlCaptions().firstOrNull(),
HTMLParseMode,
textSources,
disableNotification,
replyToMessageId,
allowSendingWithoutReply,
@ -41,8 +39,5 @@ data class PhotoContent(
override fun toMediaGroupMemberInputMedia(): InputMediaPhoto = asInputMedia()
override fun asInputMedia(): InputMediaPhoto = media.toInputMediaPhoto(
toHtmlCaptions().firstOrNull(),
HTMLParseMode
)
override fun asInputMedia(): InputMediaPhoto = media.toInputMediaPhoto(textSources)
}

View File

@ -1,18 +1,17 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.CommonAbstracts.textSources
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendVideo
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaVideo
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.VideoFile
import dev.inmo.tgbotapi.types.files.toInputMediaVideo
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent
import dev.inmo.tgbotapi.utils.internal.toHtmlCaptions
data class VideoContent(
override val media: VideoFile,
@ -29,8 +28,7 @@ data class VideoContent(
chatId,
media.fileId,
media.thumb ?.fileId,
toHtmlCaptions().firstOrNull(),
HTMLParseMode,
textSources,
media.duration,
media.width,
media.height,
@ -43,8 +41,5 @@ data class VideoContent(
override fun toMediaGroupMemberInputMedia(): InputMediaVideo = asInputMedia()
override fun asInputMedia(): InputMediaVideo = media.toInputMediaVideo(
toHtmlCaptions().firstOrNull(),
HTMLParseMode
)
override fun asInputMedia(): InputMediaVideo = media.toInputMediaVideo(textSources)
}

View File

@ -1,20 +1,15 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.CaptionedInput
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendVoice
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaAudio
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.VoiceFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
import dev.inmo.tgbotapi.utils.internal.toHtmlCaptions
import dev.inmo.tgbotapi.utils.internal.toMarkdownV2Captions
data class VoiceContent(
override val media: VoiceFile,
@ -30,8 +25,7 @@ data class VoiceContent(
): Request<ContentMessage<VoiceContent>> = SendVoice(
chatId,
media.fileId,
toHtmlCaptions().firstOrNull(),
HTMLParseMode,
textSources,
media.duration,
disableNotification,
replyToMessageId,
@ -41,8 +35,7 @@ data class VoiceContent(
override fun asInputMedia(): InputMediaAudio = InputMediaAudio(
media.fileId,
toMarkdownV2Captions().firstOrNull(),
MarkdownV2,
textSources,
media.duration
)
}

View File

@ -1,97 +0,0 @@
package dev.inmo.tgbotapi.utils.internal
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.types.ParseMode.*
import dev.inmo.tgbotapi.types.captionLength
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.textLength
internal fun createFormattedText(
entities: TextSourcesList,
partLength: Int = textLength.last,
mode: ParseMode = MarkdownParseMode
): List<String> {
val texts = mutableListOf<String>()
val textBuilder = StringBuilder(partLength)
for (entity in entities) {
val string = when (mode) {
is MarkdownParseMode -> entity.markdown
is MarkdownV2ParseMode -> entity.markdownV2
is HTMLParseMode -> entity.html
}
if (textBuilder.length + string.length > partLength) {
if (textBuilder.isNotEmpty()) {
texts.add(textBuilder.toString())
textBuilder.clear()
}
val chunked = string.chunked(partLength)
val last = chunked.last()
textBuilder.append(last)
val listToAdd = if (chunked.size > 1) {
chunked.subList(0, chunked.size - 1)
} else {
emptyList()
}
listToAdd.forEach {
texts.add(it)
}
} else {
textBuilder.append(string)
}
}
if (textBuilder.isNotEmpty()) {
texts.add(textBuilder.toString())
textBuilder.clear()
}
return texts
}
internal fun createMarkdownText(
entities: TextSourcesList,
partLength: Int = textLength.last
): List<String> = createFormattedText(entities, partLength, MarkdownParseMode)
internal fun TextSourcesList.toMarkdownTexts(): List<String> = createMarkdownText(
this,
textLength.last
)
internal fun TextContent.toMarkdownTexts(): List<String> = textSources.toMarkdownTexts()
internal fun createMarkdownV2Text(
entities: TextSourcesList,
partLength: Int = textLength.last
): List<String> = createFormattedText(entities, partLength, MarkdownV2ParseMode)
internal fun TextSourcesList.toMarkdownV2Captions(): List<String> = createMarkdownV2Text(
this,
captionLength.last
)
internal fun CaptionedInput.toMarkdownV2Captions(): List<String> = textSources.toMarkdownV2Captions()
internal fun TextSourcesList.toMarkdownV2Texts(): List<String> = createMarkdownV2Text(
this,
textLength.last
)
internal fun TextContent.toMarkdownV2Texts(): List<String> = textSources.toMarkdownV2Texts()
internal fun createHtmlText(
entities: TextSourcesList,
partLength: Int = textLength.last
): List<String> = createFormattedText(entities, partLength, HTMLParseMode)
internal fun TextSourcesList.toHtmlCaptions(): List<String> = createHtmlText(
this,
captionLength.last
)
internal fun CaptionedInput.toHtmlCaptions(): List<String> = textSources.toHtmlCaptions()
internal fun TextSourcesList.toHtmlTexts(): List<String> = createHtmlText(
this,
textLength.last
)
internal fun TextContent.toHtmlTexts(): List<String> = textSources.toHtmlTexts()

View File

@ -2,8 +2,8 @@ package dev.inmo.tgbotapi.types.MessageEntity
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.CommonAbstracts.plus
import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.*
import dev.inmo.tgbotapi.utils.internal.*
import kotlin.test.Test
import kotlin.test.assertEquals

View File

@ -1,8 +1,8 @@
package dev.inmo.tgbotapi.types.MessageEntity
import dev.inmo.tgbotapi.CommonAbstracts.justTextSources
import dev.inmo.tgbotapi.utils.internal.toHtmlTexts
import dev.inmo.tgbotapi.utils.internal.toMarkdownV2Texts
import dev.inmo.tgbotapi.extensions.utils.formatting.toHtmlTexts
import dev.inmo.tgbotapi.extensions.utils.formatting.toMarkdownV2Texts
import kotlin.test.Test
import kotlin.test.assertEquals

View File

@ -44,5 +44,23 @@ kotlin {
api project(":tgbotapi.core")
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
}
}
jsTest {
dependencies {
implementation kotlin('test-junit')
implementation kotlin('test-js')
}
}
}
}

View File

@ -8,15 +8,15 @@ import dev.inmo.tgbotapi.types.message.content.TextContent
fun createFormattedText(
entities: TextSourcesList,
partLength: Int = textLength.last,
mode: ParseMode = MarkdownParseMode
mode: ParseMode = defaultParseMode
): List<String> {
val texts = mutableListOf<String>()
val textBuilder = StringBuilder(partLength)
for (entity in entities) {
val string = when (mode) {
is MarkdownParseMode -> entity.markdown
is MarkdownV2ParseMode -> entity.markdownV2
is HTMLParseMode -> entity.html
is Markdown -> entity.markdown
is MarkdownV2 -> entity.markdownV2
is HTML -> entity.html
}
if (textBuilder.length + string.length > partLength) {
if (textBuilder.isNotEmpty()) {
@ -49,7 +49,7 @@ fun createFormattedText(
fun createMarkdownText(
entities: TextSourcesList,
partLength: Int = textLength.last
): List<String> = createFormattedText(entities, partLength, MarkdownParseMode)
): List<String> = createFormattedText(entities, partLength, Markdown)
fun TextSourcesList.toMarkdownCaptions(): List<String> = createMarkdownText(
this,
@ -73,7 +73,7 @@ fun ExplainedInput.toMarkdownExplanations(): List<String> = textSources.toMarkdo
fun createMarkdownV2Text(
entities: TextSourcesList,
partLength: Int = textLength.last
): List<String> = createFormattedText(entities, partLength, MarkdownV2ParseMode)
): List<String> = createFormattedText(entities, partLength, MarkdownV2)
fun TextSourcesList.toMarkdownV2Captions(): List<String> = createMarkdownV2Text(
this,
@ -97,7 +97,7 @@ fun ExplainedInput.toMarkdownV2Explanations(): List<String> = textSources.toMark
fun createHtmlText(
entities: TextSourcesList,
partLength: Int = textLength.last
): List<String> = createFormattedText(entities, partLength, HTMLParseMode)
): List<String> = createFormattedText(entities, partLength, HTML)
fun TextSourcesList.toHtmlCaptions(): List<String> = createHtmlText(
this,

View File

@ -1,7 +1,6 @@
package dev.inmo.tgbotapi.extensions.utils.updates.retrieving
import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.coroutines.*
import dev.inmo.tgbotapi.bot.RequestsExecutor
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.exceptions.RequestException
@ -14,7 +13,11 @@ import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.*
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.*
import dev.inmo.tgbotapi.utils.*
import io.ktor.client.features.HttpRequestTimeoutException
import io.ktor.utils.io.core.use
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlin.coroutines.coroutineContext
fun TelegramBot.startGettingOfUpdatesByLongPolling(
timeoutSeconds: Seconds = 30,
@ -66,6 +69,70 @@ fun TelegramBot.startGettingOfUpdatesByLongPolling(
}
}
fun TelegramBot.retrieveAccumulatedUpdates(
avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = null,
updatesReceiver: UpdateReceiver<Update>
): Job = scope.launch {
safelyWithoutExceptions {
startGettingOfUpdatesByLongPolling(
0,
CoroutineScope(coroutineContext + SupervisorJob()),
{
if (it is HttpRequestTimeoutException) {
throw CancellationException("Cancel due to absence of new updates")
} else {
exceptionsHandler ?.invoke(it)
}
},
allowedUpdates
) {
when {
it is InlineQueryUpdate && avoidInlineQueries ||
it is CallbackQueryUpdate && avoidCallbackQueries -> return@startGettingOfUpdatesByLongPolling
else -> updatesReceiver(it)
}
}.join()
}
}
/**
* @return [kotlinx.coroutines.flow.Flow] which will emit updates to the collector while they will be accumulated. Works
* the same as [retrieveAccumulatedUpdates], but pass [kotlinx.coroutines.flow.FlowCollector.emit] as a callback
*/
fun TelegramBot.createAccumulatedUpdatesRetrieverFlow(
avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false,
exceptionsHandler: ExceptionHandler<Unit>? = null,
allowedUpdates: List<String>? = null
): Flow<Update> = channelFlow {
val parentContext = kotlin.coroutines.coroutineContext
channel.apply {
retrieveAccumulatedUpdates(
avoidInlineQueries,
avoidCallbackQueries,
CoroutineScope(parentContext),
exceptionsHandler,
allowedUpdates,
::send
).join()
close()
}
}
fun TelegramBot.retrieveAccumulatedUpdates(
flowsUpdatesFilter: FlowsUpdatesFilter,
avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
exceptionsHandler: ExceptionHandler<Unit>? = null
) = flowsUpdatesFilter.run {
retrieveAccumulatedUpdates(avoidInlineQueries, avoidCallbackQueries, scope, exceptionsHandler, allowedUpdates, asUpdateReceiver)
}
/**
* Will [startGettingOfUpdatesByLongPolling] using incoming [flowsUpdatesFilter]. It is assumed that you ALREADY CONFIGURE
* all updates receivers, because this method will trigger getting of updates and.