mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2026-08-17 22:56:15 +00:00
Add Bot API 10.2 Rich Messages input blocks and media examples
Extend RichMessagesBot with the 36.0.0 rich-message input additions:
- rich_blocks: build a rich message from the typed InputRichBlocks DSL
(InputRichMessageBlocks { }) instead of an HTML/Markdown string
- rich_blocks_draft: stream a draft-only thinking() blocks sequence via
sendRichMessageDraft, then finalize with sendRichMessage
- onPhoto: embed a received photo into a rich message both via
InputRichMessageMedia (tg://photo?id=) referenced from HTML and as a
first-class photo() media block inside the blocks tree
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAn
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGuestRequestMessage
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPhoto
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onRichMessage
|
||||
import dev.inmo.tgbotapi.extensions.utils.baseSentMessageUpdateOrNull
|
||||
import dev.inmo.tgbotapi.extensions.utils.contentMessageOrNull
|
||||
@@ -25,8 +26,11 @@ import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputRichMessag
|
||||
import dev.inmo.tgbotapi.types.InlineQueryId
|
||||
import dev.inmo.tgbotapi.types.message.content.TextContent
|
||||
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
|
||||
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
|
||||
import dev.inmo.tgbotapi.types.rich.InputRichMessageBlocks
|
||||
import dev.inmo.tgbotapi.types.rich.InputRichMessageHTML
|
||||
import dev.inmo.tgbotapi.types.rich.InputRichMessageMarkdown
|
||||
import dev.inmo.tgbotapi.types.rich.InputRichMessageMedia
|
||||
import dev.inmo.tgbotapi.types.toChatId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -52,6 +56,14 @@ import kotlinx.coroutines.flow.mapNotNull
|
||||
* - [waitRichMessage] — expectation for a rich message
|
||||
* - [onlyRichMessageContentMessages] — flow filter keeping only rich message content
|
||||
* - [InputRichMessageContent] — usable as InputMessageContent in inline query results
|
||||
*
|
||||
* Telegram Bot API 10.2 additions demonstrated below:
|
||||
* - [InputRichMessageBlocks] — build a rich message from a typed [dev.inmo.tgbotapi.types.rich.InputRichBlock]
|
||||
* tree via the InputRichBlocks DSL instead of an HTML/Markdown string (exactly one of html/markdown/blocks)
|
||||
* - the draft-only `thinking()` block, streamed through [sendRichMessageDraft]
|
||||
* - [InputRichMessageMedia] — media referenced from the rich message via `tg://photo?id=` / `tg://video?id=` /
|
||||
* `tg://audio?id=`, plus first-class media blocks (photo/video/...) inside the blocks tree; new files are
|
||||
* uploaded as `attach://` automatically by [dev.inmo.tgbotapi.requests.send.SendRichMessage]
|
||||
*/
|
||||
suspend fun main(vararg args: String) {
|
||||
val botToken = args.first()
|
||||
@@ -444,6 +456,103 @@ suspend fun main(vararg args: String) {
|
||||
)
|
||||
}
|
||||
|
||||
// === Bots API 10.2 additions: InputRichBlocks DSL + rich message media ===
|
||||
|
||||
// InputRichMessageBlocks { } — build a rich message from a typed InputRichBlock tree instead
|
||||
// of an HTML/Markdown string (exactly one of html/markdown/blocks may be used). The lambda is an
|
||||
// InputRichBlocksBuilder; buildInputRichBlocks { } returns the raw List<InputRichBlock> the same way.
|
||||
onCommand("rich_blocks") {
|
||||
sendRichMessage(
|
||||
it.chat.id,
|
||||
InputRichMessageBlocks {
|
||||
heading("Rich blocks (Bots API 10.2)", level = 1)
|
||||
paragraph {
|
||||
plain("This message is built from ")
|
||||
bold("structured InputRichBlocks")
|
||||
plain(" — no HTML or Markdown string is involved.")
|
||||
}
|
||||
|
||||
heading("Lists", level = 2)
|
||||
list {
|
||||
item("A plain list item")
|
||||
item(hasCheckbox = true, isChecked = true) { paragraph("A completed task item") }
|
||||
item(hasCheckbox = true, isChecked = false) { paragraph("A pending task item") }
|
||||
item(value = 7, labelType = "1") { paragraph("An ordered item explicitly starting at 7") }
|
||||
}
|
||||
|
||||
divider()
|
||||
|
||||
heading("Code", level = 2)
|
||||
preformatted("val answer = 42", language = "kotlin")
|
||||
|
||||
heading("Quotation", level = 2)
|
||||
blockQuotation {
|
||||
paragraph {
|
||||
plain("Quotations are themselves made of nested blocks — ")
|
||||
italic("including inline formatting")
|
||||
plain(".")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// sendRichMessageDraft with blocks: the thinking() block is only valid inside a draft and is used
|
||||
// to stream a model's reasoning before the finalized rich message is sent via sendRichMessage.
|
||||
onCommand("rich_blocks_draft") {
|
||||
val chatId = it.chat.id.toChatId()
|
||||
val draftId = 2L
|
||||
listOf("Analyzing your request", "Composing a structured answer").forEach { step ->
|
||||
sendRichMessageDraft(
|
||||
chatId,
|
||||
draftId,
|
||||
InputRichMessageBlocks { thinking(step) }
|
||||
)
|
||||
delay(1000)
|
||||
}
|
||||
// finalize the streamed draft with the real (non-thinking) blocks
|
||||
sendRichMessage(
|
||||
chatId,
|
||||
InputRichMessageBlocks {
|
||||
heading("Answer", level = 2)
|
||||
paragraph("Here is the finalized, structured reply.")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Rich message media: send me a photo and it gets embedded into a rich message two ways.
|
||||
onPhoto { message ->
|
||||
// reuse the received file by its fileId (no upload). To upload a brand-new file instead,
|
||||
// build the TelegramMedia from file.asMultipartFile() — SendRichMessage collects any
|
||||
// MultipartFile inside the rich message and uploads it as attach://<id> automatically.
|
||||
val photoMedia = TelegramMediaPhoto(message.content.media.fileId)
|
||||
|
||||
// (1) referenced from HTML via tg://photo?id=<id>, resolved through InputRichMessage.media
|
||||
sendRichMessage(
|
||||
message.chat.id,
|
||||
InputRichMessageHTML(
|
||||
"""
|
||||
<h2>Your photo, referenced from HTML</h2>
|
||||
<p>Below is your photo, referenced via <code>tg://photo?id=userphoto</code>:</p>
|
||||
<img src="tg://photo?id=userphoto"/>
|
||||
""".trimIndent(),
|
||||
media = listOf(
|
||||
InputRichMessageMedia(id = "userphoto", media = photoMedia)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// (2) as a first-class media block inside an InputRichBlocks tree
|
||||
sendRichMessage(
|
||||
message.chat.id,
|
||||
InputRichMessageBlocks {
|
||||
heading("Your photo, as a media block", level = 2)
|
||||
paragraph("The same photo, this time a photo() block inside the blocks tree:")
|
||||
photo(photoMedia)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// waitRichMessage expectation: wait for the user to send a rich message
|
||||
onCommand("wait_rich") {
|
||||
reply(it, "Send me a rich message now")
|
||||
@@ -528,7 +637,9 @@ suspend fun main(vararg args: String) {
|
||||
setMyCommands(
|
||||
BotCommand("rich_html", "Send a rich message described with HTML"),
|
||||
BotCommand("rich_markdown", "Send a rich message described with Markdown"),
|
||||
BotCommand("rich_blocks", "Send a rich message built from the InputRichBlocks DSL"),
|
||||
BotCommand("rich_draft", "Stream a rich message draft, then finalize it"),
|
||||
BotCommand("rich_blocks_draft", "Stream a blocks draft with thinking(), then finalize it"),
|
||||
BotCommand("rich_edit", "Send a rich message and edit it with new rich content"),
|
||||
BotCommand("wait_rich", "Wait for you to send a rich message"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user