add blockquote

This commit is contained in:
InsanusMokrassar 2024-01-02 23:26:47 +06:00
parent a01a9910b5
commit db2101d85c
8 changed files with 135 additions and 25 deletions

View File

@ -22,24 +22,25 @@ internal data class RawMessageEntity(
val priority by lazy {
when (type) {
// Types with potential subsources should have priority
"mention" -> 0
"hashtag" -> 0
"cashtag" -> 0
"email" -> 0
"phone_number" -> 0
"bold" -> 0
"italic" -> 0
"text_mention" -> 0
"strikethrough" -> 0
"underline" -> 0
"spoiler" -> 0
"custom_emoji" -> 0
"bot_command" -> 1
"url" -> 1
"code" -> 1
"pre" -> 1
"text_link" -> 1
else -> 1
"mention" -> 1
"hashtag" -> 1
"cashtag" -> 1
"email" -> 1
"phone_number" -> 1
"bold" -> 1
"blockquote" -> 0
"italic" -> 1
"text_mention" -> 1
"strikethrough" -> 1
"underline" -> 1
"spoiler" -> 1
"custom_emoji" -> 1
"bot_command" -> 2
"url" -> 2
"code" -> 2
"pre" -> 2
"text_link" -> 2
else -> 2
}
}
}
@ -61,6 +62,7 @@ internal fun RawMessageEntity.asTextSource(
"email" -> EMailTextSource(sourceSubstring, subPartsWithRegulars)
"phone_number" -> PhoneNumberTextSource(sourceSubstring, subPartsWithRegulars)
"bold" -> BoldTextSource(sourceSubstring, subPartsWithRegulars)
"blockquote" -> BlockquoteTextSource(sourceSubstring, subPartsWithRegulars)
"italic" -> ItalicTextSource(sourceSubstring, subPartsWithRegulars)
"code" -> CodeTextSource(sourceSubstring)
"pre" -> PreTextSource(sourceSubstring, language)
@ -180,6 +182,7 @@ internal fun TextSource.toRawMessageEntities(offset: Int = 0): List<RawMessageEn
is EMailTextSource -> RawMessageEntity("email", offset, length)
is PhoneNumberTextSource -> RawMessageEntity("phone_number", offset, length)
is BoldTextSource -> RawMessageEntity("bold", offset, length)
is BlockquoteTextSource -> RawMessageEntity("blockquote", offset, length)
is ItalicTextSource -> RawMessageEntity("italic", offset, length)
is CodeTextSource -> RawMessageEntity("code", offset, length)
is PreTextSource -> RawMessageEntity("pre", offset, length, language = language)

View File

@ -0,0 +1,26 @@
package dev.inmo.tgbotapi.types.message.textsources
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.extensions.makeString
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see blockquote
*/
@Serializable
data class BlockquoteTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: TextSourcesList
) : MultilevelTextSource {
override val markdown: String by lazy { source.blockquoteMarkdown() }
override val markdownV2: String by lazy { blockquoteMarkdownV2() }
override val html: String by lazy { blockquoteHTML() }
}
@Suppress("NOTHING_TO_INLINE")
inline fun blockquote(parts: TextSourcesList) = BlockquoteTextSource(parts.makeString(), parts)
@Suppress("NOTHING_TO_INLINE")
inline fun blockquote(vararg parts: TextSource) = blockquote(parts.toList())
@Suppress("NOTHING_TO_INLINE")
inline fun blockquote(text: String) = blockquote(regular(text))

View File

@ -103,6 +103,43 @@ inline fun EntitiesBuilder.bold(text: String) = add(dev.inmo.tgbotapi.types.mess
*/
inline fun EntitiesBuilder.boldln(text: String) = bold(text) + newLine
/**
* Add blockquote using [EntitiesBuilder.add] with [dev.inmo.tgbotapi.types.message.textsources.blockquote]
*/
inline fun EntitiesBuilder.blockquote(parts: TextSourcesList) = add(dev.inmo.tgbotapi.types.message.textsources.blockquote(parts))
/**
* Version of [EntitiesBuilder.blockquote] with new line at the end
*/
inline fun EntitiesBuilder.blockquoteln(parts: TextSourcesList) = blockquote(parts) + newLine
/**
* Add blockquote using [EntitiesBuilder.add] with [dev.inmo.tgbotapi.types.message.textsources.blockquote].
* Will reuse separator config from [buildEntities]
*/
inline fun EntitiesBuilder.blockquote(noinline init: EntitiesBuilderBody) = add(dev.inmo.tgbotapi.types.message.textsources.blockquote(
buildEntities(separator, init)
))
/**
* Version of [EntitiesBuilder.blockquote] with new line at the end.
* Will reuse separator config from [buildEntities]
*/
inline fun EntitiesBuilder.blockquoteln(noinline init: EntitiesBuilderBody) = blockquote(init) + newLine
/**
* Add blockquote using [EntitiesBuilder.add] with [dev.inmo.tgbotapi.types.message.textsources.blockquote]
*/
inline fun EntitiesBuilder.blockquote(vararg parts: TextSource) = add(dev.inmo.tgbotapi.types.message.textsources.blockquote(*parts))
/**
* Version of [EntitiesBuilder.blockquote] with new line at the end
*/
inline fun EntitiesBuilder.blockquoteln(vararg parts: TextSource) = blockquote(*parts) + newLine
/**
* Add blockquote using [EntitiesBuilder.add] with [dev.inmo.tgbotapi.types.message.textsources.blockquote]
*/
inline fun EntitiesBuilder.blockquote(text: String) = add(dev.inmo.tgbotapi.types.message.textsources.blockquote(text))
/**
* Version of [EntitiesBuilder.blockquote] with new line at the end
*/
inline fun EntitiesBuilder.blockquoteln(text: String) = blockquote(text) + newLine
/**
* Add spoiler using [EntitiesBuilder.add] with [dev.inmo.tgbotapi.types.message.textsources.spoiler]
*/

View File

@ -3,6 +3,8 @@ package dev.inmo.tgbotapi.utils.extensions
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.message.*
val eachLineRegex = Regex("^[^\n]")
inline fun TextSourcesList.makeString(
parseMode: ParseMode? = null
) = when (parseMode) {
@ -21,8 +23,23 @@ inline fun TextSourcesList.makeHtmlString() = joinToString("") {
it.html
}
inline fun TextSourcesList.makeMarkdownV2String() = joinToString("") {
inline fun TextSourcesList.makeMarkdownV2String(eachLineSeparator: String? = null) = joinToString("") {
it.markdownV2
}.let {
if (eachLineSeparator == null) {
it
} else {
it.let {
if (it.startsWith("\n")) {
it
} else {
"$eachLineSeparator$it"
}
}.replace(
"\n",
"\n$eachLineSeparator"
)
}
}
inline fun TextSourcesList.makeMarkdownString() = joinToString("") {

View File

@ -6,8 +6,9 @@ import dev.inmo.tgbotapi.utils.extensions.*
internal fun MultilevelTextSource.markdownV2Default(
openControlSymbol: String,
closeControlSymbol: String = openControlSymbol
) = "$openControlSymbol${subsources.makeMarkdownV2String()}$closeControlSymbol"
closeControlSymbol: String = openControlSymbol,
eachLineSeparator: String? = null
) = "$openControlSymbol${subsources.makeMarkdownV2String(eachLineSeparator)}$closeControlSymbol"
internal fun MultilevelTextSource.htmlDefault(
openControlSymbol: String,
closeControlSymbol: String = openControlSymbol
@ -40,6 +41,10 @@ internal fun MultilevelTextSource.boldMarkdownV2(): String = markdownV2Default(m
internal fun MultilevelTextSource.boldHTML(): String = htmlDefault(htmlBoldControl)
internal fun MultilevelTextSource.blockquoteMarkdownV2(): String = markdownV2Default("", eachLineSeparator = markdownBlockquoteControl)
internal fun MultilevelTextSource.blockquoteHTML(): String = htmlDefault(htmlBlockquoteControl)
internal fun MultilevelTextSource.cashTagMarkdownV2(): String = subsources.makeMarkdownV2String()
internal fun MultilevelTextSource.cashTagHTML(): String = subsources.makeHtmlString()

View File

@ -5,6 +5,7 @@ import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.utils.extensions.*
const val markdownBoldControl = "*"
const val markdownBlockquoteControl = ">"
const val markdownItalicControl = "_"
const val markdownSpoilerControl = "||"
const val markdownCodeControl = "`"
@ -17,6 +18,7 @@ const val markdownV2UnderlineEndControl = "$markdownV2UnderlineControl$markdownV
const val markdownV2ItalicEndControl = "$markdownItalicControl$markdownV2ItalicUnderlineDelimiter"
const val htmlBoldControl = "b"
const val htmlBlockquoteControl = "blockquote"
const val htmlItalicControl = "i"
const val htmlSpoilerControl = "span class=\"tg-spoiler\""
const val htmlSpoilerClosingControl = "span"
@ -47,6 +49,8 @@ internal fun String.linkHTML(link: String): String = "<a href=\"$link\">${toHtml
internal fun String.boldMarkdown(): String = markdownDefault(markdownBoldControl)
internal fun String.blockquoteMarkdown(): String = regularMarkdown()
internal fun String.italicMarkdown(): String = markdownDefault(markdownItalicControl)
internal fun String.spoilerMarkdown(): String = regularMarkdown()

View File

@ -4,9 +4,11 @@ import dev.inmo.tgbotapi.types.message.RawMessageEntity
import dev.inmo.tgbotapi.types.message.textsources.*
import kotlin.test.assertTrue
const val testText = "It (is?) is simple hello world with #tag and @mention"
const val formattedV2Text = "It \\(is?\\) *_is_ ~__simple__~* ||hello world|| with \\#tag and @mention"
const val formattedHtmlText = "It (is?) <b><i>is</i> <s><u>simple</u></s></b> <span class=\"tg-spoiler\">hello world</span> with #tag and @mention"
const val testText = "It (is?) is simple hello world with #tag and @mention. Start of blockquote: Block quotation started\n" +
"Block quotation continued\n" +
"The last line of the block quotation"
const val formattedV2Text = "It \\(is?\\) *_is_ ~__simple__~* ||hello world|| with \\#tag and @mention\\. Start of blockquote: >Block quotation started\n>Block quotation continued\n>The last line of the block quotation"
const val formattedHtmlText = "It (is?) <b><i>is</i> <s><u>simple</u></s></b> <span class=\"tg-spoiler\">hello world</span> with #tag and @mention. Start of blockquote: <blockquote>Block quotation started\nBlock quotation continued\nThe last line of the block quotation</blockquote>"
internal val testTextEntities = listOf(
RawMessageEntity(
"bold",
@ -42,6 +44,11 @@ internal val testTextEntities = listOf(
"mention",
45,
8
),
RawMessageEntity(
"blockquote",
76,
86
)
)
@ -54,10 +61,15 @@ fun TextSourcesList.testTextSources() {
assertTrue (get(5) is HashTagTextSource)
assertTrue (get(6) is RegularTextSource)
assertTrue (get(7) is MentionTextSource)
assertTrue (get(8) is RegularTextSource)
assertTrue (get(9) is BlockquoteTextSource)
val boldSource = get(1) as BoldTextSource
assertTrue (boldSource.subsources.first() is ItalicTextSource)
assertTrue (boldSource.subsources[1] is RegularTextSource)
assertTrue (boldSource.subsources[2] is StrikethroughTextSource)
assertTrue ((boldSource.subsources[2] as StrikethroughTextSource).subsources.first() is UnderlineTextSource)
val blockquoteSource = get(9) as BlockquoteTextSource
assertTrue (blockquoteSource.subsources.first() is RegularTextSource)
}

View File

@ -48,7 +48,13 @@ class StringFormattingTests {
" with " +
hashtag("tag") +
" and " +
mention("mention")
mention("mention") +
". Start of blockquote: " +
blockquote(
"Block quotation started\n" +
"Block quotation continued\n" +
"The last line of the block quotation"
)
sources.testTextSources()
assertEquals(formattedV2Text, sources.toMarkdownV2Texts().first())