1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-26 03:58:44 +00:00

Merge pull request #214 from InsanusMokrassar/0.30.6

0.30.6
This commit is contained in:
InsanusMokrassar 2020-11-16 16:29:46 +06:00 committed by GitHub
commit 488158d8fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 160 additions and 95 deletions

View File

@ -1,5 +1,24 @@
# TelegramBotAPI changelog # TelegramBotAPI changelog
## 0.30.6
* `Core`
* `TextSource` properties has been renamed:
* `asMarkdownSource` -> `markdown`
* `asMarkdownV2Source` -> `markdownV2`
* `asHtmlSource` -> `html`
* `PrivateChat` override `id` property with type `UserId`
* Several new extensions and functions in links creation:
* New function `makeUsernameLink` with parameter `String`
* New extension `Username#link` and function `makeLink(Username)`
* Function `makeLinkToMessage` now able to get any type of chat
* New extension `Message#link`
* Old functions `makeLinkToAddStickerSet...` has been deprecated:
* `makeLinkToAddStickerSet`
* `makeLinkToAddStickerSetInMarkdownV2`
* `makeLinkToAddStickerSetInMarkdown`
* `makeLinkToAddStickerSetInHtml`
## 0.30.5 ## 0.30.5
* `Common`: * `Common`:

View File

@ -17,7 +17,7 @@ micro_utils_version=0.4.0
javax_activation_version=1.1.1 javax_activation_version=1.1.1
library_group=dev.inmo library_group=dev.inmo
library_version=0.30.5 library_version=0.30.6
gradle_bintray_plugin_version=1.8.5 gradle_bintray_plugin_version=1.8.5
github_release_plugin_version=2.2.12 github_release_plugin_version=2.2.12

View File

@ -14,13 +14,23 @@ typealias FullTextSourcesList = List<TextSource>
typealias FullTextPartsList = List<TextPart> typealias FullTextPartsList = List<TextPart>
interface TextSource { interface TextSource {
val asMarkdownSource: String val markdown: String
val asMarkdownV2Source: String val markdownV2: String
val asHtmlSource: String val html: String
val source: String val source: String
val asText: String val asText: String
get() = source get() = source
@Deprecated("Rename", ReplaceWith("markdown"))
val asMarkdownSource: String
get() = markdown
@Deprecated("Rename", ReplaceWith("markdownV2"))
val asMarkdownV2Source: String
get() = markdownV2
@Deprecated("Rename", ReplaceWith("html"))
val asHtmlSource: String
get() = html
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ data class BoldTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor)
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.boldMarkdown() } override val markdown: String by lazy { source.boldMarkdown() }
override val asMarkdownV2Source: String by lazy { boldMarkdownV2() } override val markdownV2: String by lazy { boldMarkdownV2() }
override val asHtmlSource: String by lazy { boldHTML() } override val html: String by lazy { boldHTML() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -18,9 +18,9 @@ data class BotCommandTextSource @RiskFeature(DirectInvocationOfTextSourceConstru
commandRegex.find(source) ?.value ?.substring(1) ?: source.substring(1)// skip first symbol like "/" or "!" commandRegex.find(source) ?.value ?.substring(1) ?: source.substring(1)// skip first symbol like "/" or "!"
} }
override val asMarkdownSource: String by lazy { source.commandMarkdown() } override val markdown: String by lazy { source.commandMarkdown() }
override val asMarkdownV2Source: String by lazy { source.commandMarkdownV2() } override val markdownV2: String by lazy { source.commandMarkdownV2() }
override val asHtmlSource: String by lazy { source.commandHTML() } override val html: String by lazy { source.commandHTML() }
} }
/** /**

View File

@ -13,9 +13,9 @@ data class CashTagTextSource @RiskFeature(DirectInvocationOfTextSourceConstructo
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.cashTagMarkdown() } override val markdown: String by lazy { source.cashTagMarkdown() }
override val asMarkdownV2Source: String by lazy { cashTagMarkdownV2() } override val markdownV2: String by lazy { cashTagMarkdownV2() }
override val asHtmlSource: String by lazy { cashTagHTML() } override val html: String by lazy { cashTagHTML() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ import dev.inmo.tgbotapi.utils.internal.codeMarkdownV2
data class CodeTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor ( data class CodeTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String override val source: String
) : TextSource { ) : TextSource {
override val asMarkdownSource: String by lazy { source.codeMarkdown() } override val markdown: String by lazy { source.codeMarkdown() }
override val asMarkdownV2Source: String by lazy { source.codeMarkdownV2() } override val markdownV2: String by lazy { source.codeMarkdownV2() }
override val asHtmlSource: String by lazy { source.codeHTML() } override val html: String by lazy { source.codeHTML() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ data class EMailTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor)
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.emailMarkdown() } override val markdown: String by lazy { source.emailMarkdown() }
override val asMarkdownV2Source: String by lazy { emailMarkdownV2(source) } override val markdownV2: String by lazy { emailMarkdownV2(source) }
override val asHtmlSource: String by lazy { emailHTML(source) } override val html: String by lazy { emailHTML(source) }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ data class HashTagTextSource @RiskFeature(DirectInvocationOfTextSourceConstructo
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.hashTagMarkdown() } override val markdown: String by lazy { source.hashTagMarkdown() }
override val asMarkdownV2Source: String by lazy { hashTagMarkdownV2() } override val markdownV2: String by lazy { hashTagMarkdownV2() }
override val asHtmlSource: String by lazy { hashTagHTML() } override val html: String by lazy { hashTagHTML() }
init { init {
if (!source.startsWith("#")) { if (!source.startsWith("#")) {

View File

@ -13,9 +13,9 @@ data class ItalicTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.italicMarkdown() } override val markdown: String by lazy { source.italicMarkdown() }
override val asMarkdownV2Source: String by lazy { italicMarkdownV2() } override val markdownV2: String by lazy { italicMarkdownV2() }
override val asHtmlSource: String by lazy { italicHTML() } override val html: String by lazy { italicHTML() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -20,9 +20,9 @@ data class MentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstructo
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.mentionMarkdown() } override val markdown: String by lazy { source.mentionMarkdown() }
override val asMarkdownV2Source: String by lazy { mentionMarkdownV2() } override val markdownV2: String by lazy { mentionMarkdownV2() }
override val asHtmlSource: String by lazy { mentionHTML() } override val html: String by lazy { mentionHTML() }
init { init {
if (!source.startsWith("@")) { if (!source.startsWith("@")) {

View File

@ -13,9 +13,9 @@ data class PhoneNumberTextSource @RiskFeature(DirectInvocationOfTextSourceConstr
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.phoneMarkdown() } override val markdown: String by lazy { source.phoneMarkdown() }
override val asMarkdownV2Source: String by lazy { phoneMarkdownV2() } override val markdownV2: String by lazy { phoneMarkdownV2() }
override val asHtmlSource: String by lazy { phoneHTML() } override val html: String by lazy { phoneHTML() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ data class PreTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) c
override val source: String, override val source: String,
val language: String? = null val language: String? = null
) : TextSource { ) : TextSource {
override val asMarkdownSource: String by lazy { source.preMarkdown(language) } override val markdown: String by lazy { source.preMarkdown(language) }
override val asMarkdownV2Source: String by lazy { source.preMarkdownV2(language) } override val markdownV2: String by lazy { source.preMarkdownV2(language) }
override val asHtmlSource: String by lazy { source.preHTML(language) } override val html: String by lazy { source.preHTML(language) }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -12,9 +12,9 @@ import dev.inmo.tgbotapi.utils.internal.regularMarkdownV2
data class RegularTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor ( data class RegularTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String override val source: String
) : TextSource { ) : TextSource {
override val asMarkdownSource: String by lazy { source.regularMarkdown() } override val markdown: String by lazy { source.regularMarkdown() }
override val asMarkdownV2Source: String by lazy { source.regularMarkdownV2() } override val markdownV2: String by lazy { source.regularMarkdownV2() }
override val asHtmlSource: String by lazy { source.regularHtml() } override val html: String by lazy { source.regularHtml() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ data class StrikethroughTextSource @RiskFeature(DirectInvocationOfTextSourceCons
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asHtmlSource: String by lazy { strikethroughHTML() } override val html: String by lazy { strikethroughHTML() }
override val asMarkdownV2Source: String by lazy { strikethroughMarkdownV2() } override val markdownV2: String by lazy { strikethroughMarkdownV2() }
override val asMarkdownSource: String by lazy { source.strikethroughMarkdown() } override val markdown: String by lazy { source.strikethroughMarkdown() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ data class TextLinkTextSource @RiskFeature(DirectInvocationOfTextSourceConstruct
override val source: String, override val source: String,
val url: String val url: String
) : TextSource { ) : TextSource {
override val asMarkdownSource: String by lazy { source.linkMarkdown(url) } override val markdown: String by lazy { source.linkMarkdown(url) }
override val asMarkdownV2Source: String by lazy { source.linkMarkdownV2(url) } override val markdownV2: String by lazy { source.linkMarkdownV2(url) }
override val asHtmlSource: String by lazy { source.linkHTML(url) } override val html: String by lazy { source.linkHTML(url) }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -15,9 +15,9 @@ data class TextMentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstr
val user: User, val user: User,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.textMentionMarkdown(user.id) } override val markdown: String by lazy { source.textMentionMarkdown(user.id) }
override val asMarkdownV2Source: String by lazy { textMentionMarkdownV2(user.id) } override val markdownV2: String by lazy { textMentionMarkdownV2(user.id) }
override val asHtmlSource: String by lazy { textMentionHTML(user.id) } override val html: String by lazy { textMentionHTML(user.id) }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -12,9 +12,9 @@ import dev.inmo.tgbotapi.utils.internal.linkMarkdownV2
data class URLTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor ( data class URLTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String override val source: String
) : TextSource { ) : TextSource {
override val asMarkdownSource: String by lazy { source.linkMarkdown(source) } override val markdown: String by lazy { source.linkMarkdown(source) }
override val asMarkdownV2Source: String by lazy { source.linkMarkdownV2(source) } override val markdownV2: String by lazy { source.linkMarkdownV2(source) }
override val asHtmlSource: String by lazy { source.linkHTML(source) } override val html: String by lazy { source.linkHTML(source) }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -13,9 +13,9 @@ data class UnderlineTextSource @RiskFeature(DirectInvocationOfTextSourceConstruc
override val source: String, override val source: String,
override val subsources: List<TextSource> override val subsources: List<TextSource>
) : MultilevelTextSource { ) : MultilevelTextSource {
override val asMarkdownSource: String by lazy { source.underlineMarkdown() } override val markdown: String by lazy { source.underlineMarkdown() }
override val asMarkdownV2Source: String by lazy { underlineMarkdownV2() } override val markdownV2: String by lazy { underlineMarkdownV2() }
override val asHtmlSource: String by lazy { underlineHTML() } override val html: String by lazy { underlineHTML() }
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")

View File

@ -10,9 +10,7 @@ import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
@Serializable(UserSerializer::class) @Serializable(UserSerializer::class)
sealed class User : PrivateChat { sealed class User : PrivateChat
abstract override val id: UserId
}
@Serializable @Serializable
data class CommonUser( data class CommonUser(

View File

@ -1,10 +1,13 @@
package dev.inmo.tgbotapi.types.chat.abstracts package dev.inmo.tgbotapi.types.chat.abstracts
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.chat.PreviewChatSerializer import dev.inmo.tgbotapi.types.chat.PreviewChatSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable(PreviewChatSerializer::class) @Serializable(PreviewChatSerializer::class)
interface PrivateChat : Chat, UsernameChat { interface PrivateChat : Chat, UsernameChat {
override val id: UserId
val firstName: String val firstName: String
val lastName: String val lastName: String
} }

View File

@ -15,9 +15,9 @@ internal fun createFormattedText(
val textBuilder = StringBuilder(partLength) val textBuilder = StringBuilder(partLength)
for (entity in entities) { for (entity in entities) {
val string = when (mode) { val string = when (mode) {
is MarkdownParseMode -> entity.asMarkdownSource is MarkdownParseMode -> entity.markdown
is MarkdownV2ParseMode -> entity.asMarkdownV2Source is MarkdownV2ParseMode -> entity.markdownV2
is HTMLParseMode -> entity.asHtmlSource is HTMLParseMode -> entity.html
} }
if (textBuilder.length + string.length > partLength) { if (textBuilder.length + string.length > partLength) {
if (textBuilder.isNotEmpty()) { if (textBuilder.isNotEmpty()) {

View File

@ -73,11 +73,11 @@ internal fun List<TextPart>.shiftSourcesToTheLeft(shiftCount: Int = 1): List<Tex
} }
private fun List<TextSource>.joinSubSourcesMarkdownV2() = joinToString("") { private fun List<TextSource>.joinSubSourcesMarkdownV2() = joinToString("") {
it.asMarkdownV2Source it.markdownV2
} }
private fun List<TextSource>.joinSubSourcesHtml() = joinToString("") { private fun List<TextSource>.joinSubSourcesHtml() = joinToString("") {
it.asHtmlSource it.html
} }
internal fun MultilevelTextSource.markdownV2Default( internal fun MultilevelTextSource.markdownV2Default(

View File

@ -1,13 +1,19 @@
package dev.inmo.tgbotapi.extensions.utils.formatting package dev.inmo.tgbotapi.extensions.utils.formatting
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.link
import dev.inmo.tgbotapi.types.ParseMode.* import dev.inmo.tgbotapi.types.ParseMode.*
import dev.inmo.tgbotapi.types.chat.abstracts.PrivateChat import dev.inmo.tgbotapi.types.chat.abstracts.*
import dev.inmo.tgbotapi.types.chat.abstracts.UsernameChat import dev.inmo.tgbotapi.types.chat.abstracts.extended.ExtendedPublicChat
import dev.inmo.tgbotapi.types.chat.abstracts.extended.ExtendedChat import dev.inmo.tgbotapi.types.message.abstracts.Message
private const val internalLinkBeginning = "https://t.me" private const val internalLinkBeginning = "https://t.me"
fun makeUsernameLink(username: String) = "$internalLinkBeginning/$username"
inline val Username.link
get() = makeUsernameLink(username)
inline fun makeLink(username: Username) = username.link
fun makeLinkToMessage( fun makeLinkToMessage(
username: String, username: String,
messageId: MessageIdentifier messageId: MessageIdentifier
@ -24,8 +30,12 @@ fun makeLinkToMessage(
private val linkIdRedundantPartRegex = Regex("^-100") private val linkIdRedundantPartRegex = Regex("^-100")
private val usernameBeginSymbolRegex = Regex("^@") private val usernameBeginSymbolRegex = Regex("^@")
/**
* Link which can be used as by any user to get access to [Message]. Returns null in case when there are no
* known way to build link (for [PrivateChat]s, for example)
*/
fun makeLinkToMessage( fun makeLinkToMessage(
chat: ExtendedChat, chat: Chat,
messageId: MessageIdentifier messageId: MessageIdentifier
): String? { ): String? {
return when { return when {
@ -43,8 +53,55 @@ fun makeLinkToMessage(
} }
} }
/**
* @see makeLinkToMessage
*/
val Message.link: String?
get() = makeLinkToMessage(
chat,
messageId
)
/**
* Link which can be used as by any user to get access to [Chat]. Returns null in case when there are no
* known way to build link
*/
val Chat.link: String?
get() {
if (this is UsernameChat) {
username ?.username ?.let { return it }
}
if (this is ExtendedPublicChat) {
inviteLink ?.let { return it }
}
if (this is PrivateChat) {
return id.link
}
return null
}
private const val stickerSetAddingLinkPrefix = "$internalLinkBeginning/addstickers" private const val stickerSetAddingLinkPrefix = "$internalLinkBeginning/addstickers"
val StickerSetName.stickerSetLink
get() = link(this, "$stickerSetAddingLinkPrefix/$this")
/**
* @return Link for adding of sticker set with name [stickerSetName] with formatting for [MarkdownV2]
*/
@Deprecated("Use extension `stickerSetLink` + getting of `asMarkdownV2Source` property")
fun makeLinkToAddStickerSetInMarkdownV2(
stickerSetName: StickerSetName
) = stickerSetName.stickerSetLink.markdownV2
/**
* @return Link for adding of sticker set with name [stickerSetName] with formatting for [Markdown]
*/
@Deprecated("Use extension `stickerSetLink` + getting of `asMarkdownSource` property")
fun makeLinkToAddStickerSetInMarkdown(stickerSetName: StickerSetName) = stickerSetName.stickerSetLink.markdown
/**
* @return Link for adding of sticker set with name [stickerSetName] with formatting for [HTML]
*/
@Deprecated("Use extension `stickerSetLink` + getting of `asHtmlSource` property")
fun makeLinkToAddStickerSetInHtml(stickerSetName: StickerSetName) = stickerSetName.stickerSetLink.html
/** /**
* Create a link for adding of sticker set with name [stickerSetName]. Was added thanks to user Djaler and based on * Create a link for adding of sticker set with name [stickerSetName]. Was added thanks to user Djaler and based on
* https://github.com/Djaler/evil-bot/blob/master/src/main/kotlin/com/github/djaler/evilbot/utils/StickerUtils.kt#L6-L8 * https://github.com/Djaler/evil-bot/blob/master/src/main/kotlin/com/github/djaler/evilbot/utils/StickerUtils.kt#L6-L8
@ -53,34 +110,12 @@ private const val stickerSetAddingLinkPrefix = "$internalLinkBeginning/addsticke
* @see [makeLinkToAddStickerSetInMarkdown] * @see [makeLinkToAddStickerSetInMarkdown]
* @see [makeLinkToAddStickerSetInHtml] * @see [makeLinkToAddStickerSetInHtml]
*/ */
@Deprecated("Use extension `stickerSetLink` + getting of required property")
fun makeLinkToAddStickerSet( fun makeLinkToAddStickerSet(
stickerSetName: StickerSetName, stickerSetName: StickerSetName,
parseMode: ParseMode parseMode: ParseMode
) = (stickerSetName to "$stickerSetAddingLinkPrefix/$stickerSetName").link( ) = when (parseMode) {
parseMode MarkdownParseMode -> makeLinkToAddStickerSetInMarkdown(stickerSetName)
) MarkdownV2ParseMode -> makeLinkToAddStickerSetInMarkdownV2(stickerSetName)
HTMLParseMode -> makeLinkToAddStickerSetInHtml(stickerSetName)
/** }
* @return Link for adding of sticker set with name [stickerSetName] with formatting for [MarkdownV2]
*/
fun makeLinkToAddStickerSetInMarkdownV2(stickerSetName: StickerSetName) =
makeLinkToAddStickerSet(
stickerSetName,
MarkdownV2
)
/**
* @return Link for adding of sticker set with name [stickerSetName] with formatting for [Markdown]
*/
fun makeLinkToAddStickerSetInMarkdown(stickerSetName: StickerSetName) =
makeLinkToAddStickerSet(
stickerSetName,
Markdown
)
/**
* @return Link for adding of sticker set with name [stickerSetName] with formatting for [HTML]
*/
fun makeLinkToAddStickerSetInHtml(stickerSetName: StickerSetName) =
makeLinkToAddStickerSet(
stickerSetName,
HTML
)

View File

@ -15,9 +15,9 @@ fun createFormattedText(
val textBuilder = StringBuilder(partLength) val textBuilder = StringBuilder(partLength)
for (entity in entities) { for (entity in entities) {
val string = when (mode) { val string = when (mode) {
is MarkdownParseMode -> entity.asMarkdownSource is MarkdownParseMode -> entity.markdown
is MarkdownV2ParseMode -> entity.asMarkdownV2Source is MarkdownV2ParseMode -> entity.markdownV2
is HTMLParseMode -> entity.asHtmlSource is HTMLParseMode -> entity.html
} }
if (textBuilder.length + string.length > partLength) { if (textBuilder.length + string.length > partLength) {
if (textBuilder.isNotEmpty()) { if (textBuilder.isNotEmpty()) {