1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-25 19:48:43 +00:00

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 { val priority by lazy {
when (type) { when (type) {
// Types with potential subsources should have priority // Types with potential subsources should have priority
"mention" -> 0 "mention" -> 1
"hashtag" -> 0 "hashtag" -> 1
"cashtag" -> 0 "cashtag" -> 1
"email" -> 0 "email" -> 1
"phone_number" -> 0 "phone_number" -> 1
"bold" -> 0 "bold" -> 1
"italic" -> 0 "blockquote" -> 0
"text_mention" -> 0 "italic" -> 1
"strikethrough" -> 0 "text_mention" -> 1
"underline" -> 0 "strikethrough" -> 1
"spoiler" -> 0 "underline" -> 1
"custom_emoji" -> 0 "spoiler" -> 1
"bot_command" -> 1 "custom_emoji" -> 1
"url" -> 1 "bot_command" -> 2
"code" -> 1 "url" -> 2
"pre" -> 1 "code" -> 2
"text_link" -> 1 "pre" -> 2
else -> 1 "text_link" -> 2
else -> 2
} }
} }
} }
@ -61,6 +62,7 @@ internal fun RawMessageEntity.asTextSource(
"email" -> EMailTextSource(sourceSubstring, subPartsWithRegulars) "email" -> EMailTextSource(sourceSubstring, subPartsWithRegulars)
"phone_number" -> PhoneNumberTextSource(sourceSubstring, subPartsWithRegulars) "phone_number" -> PhoneNumberTextSource(sourceSubstring, subPartsWithRegulars)
"bold" -> BoldTextSource(sourceSubstring, subPartsWithRegulars) "bold" -> BoldTextSource(sourceSubstring, subPartsWithRegulars)
"blockquote" -> BlockquoteTextSource(sourceSubstring, subPartsWithRegulars)
"italic" -> ItalicTextSource(sourceSubstring, subPartsWithRegulars) "italic" -> ItalicTextSource(sourceSubstring, subPartsWithRegulars)
"code" -> CodeTextSource(sourceSubstring) "code" -> CodeTextSource(sourceSubstring)
"pre" -> PreTextSource(sourceSubstring, language) "pre" -> PreTextSource(sourceSubstring, language)
@ -180,6 +182,7 @@ internal fun TextSource.toRawMessageEntities(offset: Int = 0): List<RawMessageEn
is EMailTextSource -> RawMessageEntity("email", offset, length) is EMailTextSource -> RawMessageEntity("email", offset, length)
is PhoneNumberTextSource -> RawMessageEntity("phone_number", offset, length) is PhoneNumberTextSource -> RawMessageEntity("phone_number", offset, length)
is BoldTextSource -> RawMessageEntity("bold", offset, length) is BoldTextSource -> RawMessageEntity("bold", offset, length)
is BlockquoteTextSource -> RawMessageEntity("blockquote", offset, length)
is ItalicTextSource -> RawMessageEntity("italic", offset, length) is ItalicTextSource -> RawMessageEntity("italic", offset, length)
is CodeTextSource -> RawMessageEntity("code", offset, length) is CodeTextSource -> RawMessageEntity("code", offset, length)
is PreTextSource -> RawMessageEntity("pre", offset, length, language = language) 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 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] * 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.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.message.* import dev.inmo.tgbotapi.types.message.*
val eachLineRegex = Regex("^[^\n]")
inline fun TextSourcesList.makeString( inline fun TextSourcesList.makeString(
parseMode: ParseMode? = null parseMode: ParseMode? = null
) = when (parseMode) { ) = when (parseMode) {
@ -21,8 +23,23 @@ inline fun TextSourcesList.makeHtmlString() = joinToString("") {
it.html it.html
} }
inline fun TextSourcesList.makeMarkdownV2String() = joinToString("") { inline fun TextSourcesList.makeMarkdownV2String(eachLineSeparator: String? = null) = joinToString("") {
it.markdownV2 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("") { inline fun TextSourcesList.makeMarkdownString() = joinToString("") {

View File

@ -6,8 +6,9 @@ import dev.inmo.tgbotapi.utils.extensions.*
internal fun MultilevelTextSource.markdownV2Default( internal fun MultilevelTextSource.markdownV2Default(
openControlSymbol: String, openControlSymbol: String,
closeControlSymbol: String = openControlSymbol closeControlSymbol: String = openControlSymbol,
) = "$openControlSymbol${subsources.makeMarkdownV2String()}$closeControlSymbol" eachLineSeparator: String? = null
) = "$openControlSymbol${subsources.makeMarkdownV2String(eachLineSeparator)}$closeControlSymbol"
internal fun MultilevelTextSource.htmlDefault( internal fun MultilevelTextSource.htmlDefault(
openControlSymbol: String, openControlSymbol: String,
closeControlSymbol: String = openControlSymbol closeControlSymbol: String = openControlSymbol
@ -40,6 +41,10 @@ internal fun MultilevelTextSource.boldMarkdownV2(): String = markdownV2Default(m
internal fun MultilevelTextSource.boldHTML(): String = htmlDefault(htmlBoldControl) 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.cashTagMarkdownV2(): String = subsources.makeMarkdownV2String()
internal fun MultilevelTextSource.cashTagHTML(): String = subsources.makeHtmlString() 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.* import dev.inmo.tgbotapi.utils.extensions.*
const val markdownBoldControl = "*" const val markdownBoldControl = "*"
const val markdownBlockquoteControl = ">"
const val markdownItalicControl = "_" const val markdownItalicControl = "_"
const val markdownSpoilerControl = "||" const val markdownSpoilerControl = "||"
const val markdownCodeControl = "`" const val markdownCodeControl = "`"
@ -17,6 +18,7 @@ const val markdownV2UnderlineEndControl = "$markdownV2UnderlineControl$markdownV
const val markdownV2ItalicEndControl = "$markdownItalicControl$markdownV2ItalicUnderlineDelimiter" const val markdownV2ItalicEndControl = "$markdownItalicControl$markdownV2ItalicUnderlineDelimiter"
const val htmlBoldControl = "b" const val htmlBoldControl = "b"
const val htmlBlockquoteControl = "blockquote"
const val htmlItalicControl = "i" const val htmlItalicControl = "i"
const val htmlSpoilerControl = "span class=\"tg-spoiler\"" const val htmlSpoilerControl = "span class=\"tg-spoiler\""
const val htmlSpoilerClosingControl = "span" 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.boldMarkdown(): String = markdownDefault(markdownBoldControl)
internal fun String.blockquoteMarkdown(): String = regularMarkdown()
internal fun String.italicMarkdown(): String = markdownDefault(markdownItalicControl) internal fun String.italicMarkdown(): String = markdownDefault(markdownItalicControl)
internal fun String.spoilerMarkdown(): String = regularMarkdown() 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 dev.inmo.tgbotapi.types.message.textsources.*
import kotlin.test.assertTrue import kotlin.test.assertTrue
const val testText = "It (is?) is simple hello world with #tag and @mention" const val testText = "It (is?) is simple hello world with #tag and @mention. Start of blockquote: Block quotation started\n" +
const val formattedV2Text = "It \\(is?\\) *_is_ ~__simple__~* ||hello world|| with \\#tag and @mention" "Block quotation continued\n" +
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" "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( internal val testTextEntities = listOf(
RawMessageEntity( RawMessageEntity(
"bold", "bold",
@ -42,6 +44,11 @@ internal val testTextEntities = listOf(
"mention", "mention",
45, 45,
8 8
),
RawMessageEntity(
"blockquote",
76,
86
) )
) )
@ -54,10 +61,15 @@ fun TextSourcesList.testTextSources() {
assertTrue (get(5) is HashTagTextSource) assertTrue (get(5) is HashTagTextSource)
assertTrue (get(6) is RegularTextSource) assertTrue (get(6) is RegularTextSource)
assertTrue (get(7) is MentionTextSource) assertTrue (get(7) is MentionTextSource)
assertTrue (get(8) is RegularTextSource)
assertTrue (get(9) is BlockquoteTextSource)
val boldSource = get(1) 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)
assertTrue ((boldSource.subsources[2] as StrikethroughTextSource).subsources.first() is UnderlineTextSource) 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 " + " with " +
hashtag("tag") + hashtag("tag") +
" and " + " 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() sources.testTextSources()
assertEquals(formattedV2Text, sources.toMarkdownV2Texts().first()) assertEquals(formattedV2Text, sources.toMarkdownV2Texts().first())