mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2026-06-30 07:05:03 +00:00
Compare commits
1 Commits
35.0.0
...
renovate/k
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6449fc79f8 |
@@ -1,21 +0,0 @@
|
|||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
|
||||||
apply plugin: 'application'
|
|
||||||
|
|
||||||
mainClassName="JoinRequestQueriesBotKt"
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
||||||
|
|
||||||
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import dev.inmo.kslog.common.KSLog
|
|
||||||
import dev.inmo.kslog.common.LogLevel
|
|
||||||
import dev.inmo.kslog.common.defaultMessageFormatter
|
|
||||||
import dev.inmo.kslog.common.setDefaultKSLog
|
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.chat.invite_links.answerChatJoinRequestQuery
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.chat.invite_links.sendChatJoinRequestWebApp
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatJoinRequest
|
|
||||||
import dev.inmo.tgbotapi.requests.chat.invite_links.ChatJoinRequestQueryResult
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This bot demonstrates Join Request Queries support introduced in Telegram Bot API 10.1.
|
|
||||||
*
|
|
||||||
* A "guard bot" of a chat receives chat join requests as queries and must process them with
|
|
||||||
* [answerChatJoinRequestQuery] or hand the user a Web App via [sendChatJoinRequestWebApp]
|
|
||||||
* (for example, to run a captcha / verification flow before deciding).
|
|
||||||
*
|
|
||||||
* Your bot must be set as the guard bot of the chat and must have `can_invite_users` rights to
|
|
||||||
* receive these requests.
|
|
||||||
*
|
|
||||||
* Key concepts demonstrated:
|
|
||||||
* - [dev.inmo.tgbotapi.types.chat.ExtendedBot.supportsJoinRequestQueries] — whether the bot itself
|
|
||||||
* supports join request queries (from getMe(), maps `User.supports_join_request_queries`)
|
|
||||||
* - [dev.inmo.tgbotapi.types.chat.ExtendedChat.guardBot] — the bot that processes join request
|
|
||||||
* queries in a chat (from getChat(), maps `ChatFullInfo.guard_bot`)
|
|
||||||
* - [dev.inmo.tgbotapi.types.chat.ChatJoinRequest.queryId] — the [dev.inmo.tgbotapi.types.ChatJoinRequestQueryId]
|
|
||||||
* present when the request arrives as a query to the guard bot
|
|
||||||
* - [answerChatJoinRequestQuery] with [ChatJoinRequestQueryResult] (Approve / Decline / Queue / Unknown)
|
|
||||||
* - [sendChatJoinRequestWebApp] — open a Web App to process the request
|
|
||||||
*/
|
|
||||||
suspend fun main(vararg args: String) {
|
|
||||||
val botToken = args.first()
|
|
||||||
val isDebug = args.any { it == "debug" }
|
|
||||||
val isTestServer = args.any { it == "testServer" }
|
|
||||||
// pass a https url as the second argument to demonstrate sendChatJoinRequestWebApp
|
|
||||||
val webAppUrl = args.getOrNull(1) ?.takeIf { it.startsWith("https://") }
|
|
||||||
|
|
||||||
if (isDebug) {
|
|
||||||
setDefaultKSLog(
|
|
||||||
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
|
||||||
println(defaultMessageFormatter(level, tag, message, throwable))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
telegramBotWithBehaviourAndLongPolling(
|
|
||||||
botToken,
|
|
||||||
CoroutineScope(Dispatchers.IO),
|
|
||||||
testServer = isTestServer
|
|
||||||
) {
|
|
||||||
val me = getMe()
|
|
||||||
println("Bot info: $me")
|
|
||||||
// supportsJoinRequestQueries reflects the supports_join_request_queries field from the Telegram API
|
|
||||||
println("Supports join request queries: ${me.supportsJoinRequestQueries}")
|
|
||||||
|
|
||||||
onChatJoinRequest { request ->
|
|
||||||
println("=== Chat join request received ===")
|
|
||||||
println(" from: ${request.from}")
|
|
||||||
println(" chat: ${request.chat}")
|
|
||||||
println(" bio: ${request.bio}")
|
|
||||||
// queryId is non-null only when the request arrives as a query to this bot as the guard bot
|
|
||||||
println(" queryId: ${request.queryId}")
|
|
||||||
|
|
||||||
// guardBot is the bot processing join request queries in this chat (admins-only field)
|
|
||||||
val guardBot = runCatching { getChat(request.chat).guardBot }.getOrNull()
|
|
||||||
println(" guardBot: $guardBot")
|
|
||||||
|
|
||||||
val queryId = request.queryId
|
|
||||||
if (queryId == null) {
|
|
||||||
println(" -> request has no queryId, this bot is not the guard bot here")
|
|
||||||
return@onChatJoinRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
if (webAppUrl != null) {
|
|
||||||
// sendChatJoinRequestWebApp: hand the user a Web App (e.g. captcha) instead of deciding now
|
|
||||||
sendChatJoinRequestWebApp(request, webAppUrl)
|
|
||||||
println(" -> sent join request Web App: $webAppUrl")
|
|
||||||
return@onChatJoinRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// answerChatJoinRequestQuery with one of the ChatJoinRequestQueryResult variants:
|
|
||||||
// Approve — allow the user to join
|
|
||||||
// Decline — disallow the user to join
|
|
||||||
// Queue — leave the decision to other administrators
|
|
||||||
// Unknown — any future result not yet known to the library
|
|
||||||
val result = if (request.bio.isNullOrBlank()) {
|
|
||||||
// no bio -> let other admins decide
|
|
||||||
ChatJoinRequestQueryResult.Queue
|
|
||||||
} else {
|
|
||||||
// has a bio -> approve
|
|
||||||
ChatJoinRequestQueryResult.Approve
|
|
||||||
}
|
|
||||||
answerChatJoinRequestQuery(request, result)
|
|
||||||
println(" -> answered with: ${result.name}")
|
|
||||||
}
|
|
||||||
|
|
||||||
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
|
||||||
println(it)
|
|
||||||
}
|
|
||||||
}.second.join()
|
|
||||||
}
|
|
||||||
@@ -25,7 +25,6 @@ import dev.inmo.tgbotapi.types.BotCommand
|
|||||||
import dev.inmo.tgbotapi.types.IdChatIdentifier
|
import dev.inmo.tgbotapi.types.IdChatIdentifier
|
||||||
import dev.inmo.tgbotapi.types.PollId
|
import dev.inmo.tgbotapi.types.PollId
|
||||||
import dev.inmo.tgbotapi.types.ReplyParameters
|
import dev.inmo.tgbotapi.types.ReplyParameters
|
||||||
import dev.inmo.tgbotapi.types.media.TelegramMediaLink
|
|
||||||
import dev.inmo.tgbotapi.types.media.TelegramMediaLocation
|
import dev.inmo.tgbotapi.types.media.TelegramMediaLocation
|
||||||
import dev.inmo.tgbotapi.types.media.TelegramMediaSticker
|
import dev.inmo.tgbotapi.types.media.TelegramMediaSticker
|
||||||
import dev.inmo.tgbotapi.types.media.TelegramMediaVenue
|
import dev.inmo.tgbotapi.types.media.TelegramMediaVenue
|
||||||
@@ -56,8 +55,6 @@ import kotlin.random.Random
|
|||||||
* * `/members_only` — poll with `membersOnly = true` (new [dev.inmo.tgbotapi.types.polls.Poll.membersOnly] field)
|
* * `/members_only` — poll with `membersOnly = true` (new [dev.inmo.tgbotapi.types.polls.Poll.membersOnly] field)
|
||||||
* * `/country_codes` — poll with `countryCodes` (new [dev.inmo.tgbotapi.types.polls.Poll.countryCodes] field)
|
* * `/country_codes` — poll with `countryCodes` (new [dev.inmo.tgbotapi.types.polls.Poll.countryCodes] field)
|
||||||
* * `/single_option` — poll with just 1 option (minimum options count decreased from 2 to 1)
|
* * `/single_option` — poll with just 1 option (minimum options count decreased from 2 to 1)
|
||||||
* * `/link_poll` — poll whose options carry a [TelegramMediaLink] (InputMediaLink / Bot API 10.1
|
|
||||||
* [dev.inmo.tgbotapi.types.Link]) as [dev.inmo.tgbotapi.types.media.InputPollOptionMedia]
|
|
||||||
*
|
*
|
||||||
* [onPollUpdates] prints [dev.inmo.tgbotapi.types.polls.Poll.media], [dev.inmo.tgbotapi.types.polls.Poll.membersOnly],
|
* [onPollUpdates] prints [dev.inmo.tgbotapi.types.polls.Poll.media], [dev.inmo.tgbotapi.types.polls.Poll.membersOnly],
|
||||||
* [dev.inmo.tgbotapi.types.polls.Poll.countryCodes], [QuizPoll.explanationMedia], and
|
* [dev.inmo.tgbotapi.types.polls.Poll.countryCodes], [QuizPoll.explanationMedia], and
|
||||||
@@ -315,30 +312,6 @@ suspend fun main(vararg args: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demonstrates TelegramMediaLink (InputMediaLink, Bot API 10.1) as poll option media.
|
|
||||||
// Link is the only new poll media type in 10.1 and is allowed only as InputPollOptionMedia.
|
|
||||||
onCommand("link_poll") {
|
|
||||||
val sentPoll = sendRegularPoll(
|
|
||||||
it.chat.id,
|
|
||||||
buildEntities { regular("Pick your favourite resource") },
|
|
||||||
listOf(
|
|
||||||
// InputPollOptionMedia via TelegramMediaLink (InputMediaLink)
|
|
||||||
InputPollOption(
|
|
||||||
media = TelegramMediaLink("https://core.telegram.org/bots/api")
|
|
||||||
) { regular("Bot API docs") },
|
|
||||||
InputPollOption(
|
|
||||||
media = TelegramMediaLink("https://github.com/InsanusMokrassar/ktgbotapi")
|
|
||||||
) { regular("ktgbotapi") },
|
|
||||||
InputPollOption { regular("None of these") },
|
|
||||||
),
|
|
||||||
isAnonymous = false,
|
|
||||||
replyParameters = ReplyParameters(it)
|
|
||||||
)
|
|
||||||
pollToChatMutex.withLock {
|
|
||||||
pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPollAnswer {
|
onPollAnswer {
|
||||||
val chatId = pollToChat[it.pollId] ?: return@onPollAnswer
|
val chatId = pollToChat[it.pollId] ?: return@onPollAnswer
|
||||||
|
|
||||||
@@ -407,7 +380,6 @@ suspend fun main(vararg args: String) {
|
|||||||
BotCommand("members_only", "Poll restricted to channel members only (membersOnly)"),
|
BotCommand("members_only", "Poll restricted to channel members only (membersOnly)"),
|
||||||
BotCommand("country_codes", "Poll targeted to US, DE, JP users (countryCodes)"),
|
BotCommand("country_codes", "Poll targeted to US, DE, JP users (countryCodes)"),
|
||||||
BotCommand("single_option", "Poll with 1 option (minimum is now 1, not 2)"),
|
BotCommand("single_option", "Poll with 1 option (minimum is now 1, not 2)"),
|
||||||
BotCommand("link_poll", "Poll with link media (TelegramMediaLink) on options"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
|
||||||
apply plugin: 'application'
|
|
||||||
|
|
||||||
mainClassName="RichMessagesBotKt"
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
||||||
|
|
||||||
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
|
||||||
}
|
|
||||||
@@ -1,408 +0,0 @@
|
|||||||
import dev.inmo.kslog.common.KSLog
|
|
||||||
import dev.inmo.kslog.common.LogLevel
|
|
||||||
import dev.inmo.kslog.common.defaultMessageFormatter
|
|
||||||
import dev.inmo.kslog.common.setDefaultKSLog
|
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.answers.answer
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.send
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.sendRichMessage
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.sendRichMessageDraft
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitRichMessage
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
|
||||||
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.onRichMessage
|
|
||||||
import dev.inmo.tgbotapi.extensions.utils.baseSentMessageUpdateOrNull
|
|
||||||
import dev.inmo.tgbotapi.extensions.utils.contentMessageOrNull
|
|
||||||
import dev.inmo.tgbotapi.extensions.utils.onlyRichMessageContentMessages
|
|
||||||
import dev.inmo.tgbotapi.requests.edit.text.EditChatMessageRichText
|
|
||||||
import dev.inmo.tgbotapi.types.BotCommand
|
|
||||||
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
|
|
||||||
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputRichMessageContent
|
|
||||||
import dev.inmo.tgbotapi.types.InlineQueryId
|
|
||||||
import dev.inmo.tgbotapi.types.rich.InputRichMessageHTML
|
|
||||||
import dev.inmo.tgbotapi.types.rich.InputRichMessageMarkdown
|
|
||||||
import dev.inmo.tgbotapi.types.toChatId
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.flow.mapNotNull
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This bot demonstrates Rich Messages support introduced in Telegram Bot API 10.1.
|
|
||||||
*
|
|
||||||
* Rich messages allow bots to send highly structured text (and to stream AI-generated replies
|
|
||||||
* with seamless rich formatting). Telegram parses the provided HTML/Markdown into a structured
|
|
||||||
* [dev.inmo.tgbotapi.types.rich.RichMessage] made of [dev.inmo.tgbotapi.types.rich.RichBlock]s.
|
|
||||||
*
|
|
||||||
* Key concepts demonstrated:
|
|
||||||
* - [dev.inmo.tgbotapi.types.rich.InputRichMessage] — describes a rich message to send. Built only via
|
|
||||||
* the [InputRichMessageHTML] / [InputRichMessageMarkdown] factories (exactly one format must be used)
|
|
||||||
* - [sendRichMessage] — sendRichMessage method
|
|
||||||
* - [sendRichMessageDraft] — sendRichMessageDraft method: stream partial rich messages by draftId
|
|
||||||
* - [EditChatMessageRichText] — editMessageText with the new `rich_message` parameter
|
|
||||||
* - [onRichMessage] — trigger for incoming [dev.inmo.tgbotapi.types.message.content.RichMessageContent]
|
|
||||||
* (the new `rich_message` field of Message)
|
|
||||||
* - [waitRichMessage] — expectation for a rich message
|
|
||||||
* - [onlyRichMessageContentMessages] — flow filter keeping only rich message content
|
|
||||||
* - [InputRichMessageContent] — usable as InputMessageContent in inline query results
|
|
||||||
*/
|
|
||||||
suspend fun main(vararg args: String) {
|
|
||||||
val botToken = args.first()
|
|
||||||
val isDebug = args.any { it == "debug" }
|
|
||||||
val isTestServer = args.any { it == "testServer" }
|
|
||||||
|
|
||||||
if (isDebug) {
|
|
||||||
setDefaultKSLog(
|
|
||||||
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
|
||||||
println(defaultMessageFormatter(level, tag, message, throwable))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
telegramBotWithBehaviourAndLongPolling(
|
|
||||||
botToken,
|
|
||||||
CoroutineScope(Dispatchers.IO),
|
|
||||||
testServer = isTestServer
|
|
||||||
) {
|
|
||||||
// sendRichMessage with HTML-formatted content
|
|
||||||
onCommand("rich_html") {
|
|
||||||
sendRichMessage(
|
|
||||||
it.chat.id,
|
|
||||||
// InputRichMessageHTML factory — content described using HTML formatting
|
|
||||||
InputRichMessageHTML(
|
|
||||||
"""
|
|
||||||
<a name="chapter-0"></a>
|
|
||||||
<b>bold text</b>, <strong>bold text</strong>
|
|
||||||
<i>italic text</i>, <em>italic text</em>
|
|
||||||
<u>underlined text</u>, <ins>underlined text</ins>
|
|
||||||
<s>strikethrough text</s>, <strike>strikethrough text</strike>, <del>strikethrough text</del>
|
|
||||||
<code>inline fixed-width code</code>
|
|
||||||
<mark>marked text</mark>
|
|
||||||
<sub>subscript text</sub>
|
|
||||||
<sup>superscript text</sup>
|
|
||||||
<tg-spoiler>spoiler</tg-spoiler>
|
|
||||||
|
|
||||||
<a href="#note-1">Reference</a>
|
|
||||||
<a href="https://t.me/">inline URL</a>
|
|
||||||
<a href="mailto:user@example.com">inline e-mail</a>
|
|
||||||
<a href="tel:+123456789">inline phone number</a>
|
|
||||||
<a href="tg://user?id=123456789">inline mention of a user</a>
|
|
||||||
<a href="#chapter-1">in-document link</a>
|
|
||||||
<a name="chapter-1"></a>
|
|
||||||
|
|
||||||
<tg-reference name="note-1">Referenced text</tg-reference>
|
|
||||||
<tg-emoji emoji-id="5368324170671202286">👍</tg-emoji>
|
|
||||||
<img src="tg://emoji?id=5368324170671202286" alt="👍"/>
|
|
||||||
<tg-time unix="1647531900" format="wDT">22:45 tomorrow</tg-time>
|
|
||||||
<tg-math>x^2 + y^2</tg-math>
|
|
||||||
|
|
||||||
#hashtag ${'$'}USD +12345678901, card: 4242 4242 4242 4242, https://t.me t.me a@t.me /command @username
|
|
||||||
|
|
||||||
all the text above was on the same line
|
|
||||||
|
|
||||||
<h1>Heading 1</h1>
|
|
||||||
<h2>Heading 2</h2>
|
|
||||||
<h3>Heading 3</h3>
|
|
||||||
<h4>Heading 4</h4>
|
|
||||||
<h5>Heading 5</h5>
|
|
||||||
<h6>Heading 6</h6>
|
|
||||||
|
|
||||||
<a name="chapter-2"></a>
|
|
||||||
|
|
||||||
<p>Paragraph text</p>
|
|
||||||
<pre>pre-formatted fixed-width code block</pre>
|
|
||||||
<pre><code class="language-python"> print('pre-formatted fixed-width code block written in the Python programming language')</code></pre>
|
|
||||||
<footer>Footer text</footer>
|
|
||||||
<hr/>
|
|
||||||
<ul><li>unordered list item</li></ul>
|
|
||||||
<ol><li>ordered list item</li></ol>
|
|
||||||
<ol start="3" type="a" reversed><li>ordered list item</li></ol>
|
|
||||||
<ol><li value="7" type="i">ordered list item with explicit number</li></ol>
|
|
||||||
<ul>
|
|
||||||
<li><input type="checkbox" checked>Checked checkbox</li>
|
|
||||||
<li><input type="checkbox">Unchecked checkbox</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<blockquote>Block quotation started<br>Block quotation continued<br>The last line of the block quotation<cite>The Author</cite></blockquote>
|
|
||||||
<aside>Pull quote<cite>The Author</cite></aside>
|
|
||||||
|
|
||||||
<img src="https://telegram.org/example/photo.jpg"/>
|
|
||||||
<video src="https://telegram.org/example/video.mp4"></video>
|
|
||||||
<audio src="https://telegram.org/example/audio.mp3"></audio>
|
|
||||||
<audio src="https://telegram.org/example/audio.ogg"></audio>
|
|
||||||
<video src="https://telegram.org/example/animation.gif"></video>
|
|
||||||
|
|
||||||
<figure><img src="https://telegram.org/example/photo.jpg" tg-spoiler/><figcaption>Photo caption<cite>Photo credit</cite></figcaption></figure>
|
|
||||||
<figure><video src="https://telegram.org/example/video.mp4" tg-spoiler></video><figcaption>Video caption</figcaption></figure>
|
|
||||||
<figure><audio src="https://telegram.org/example/audio.mp3"></audio><figcaption>Audio caption</figcaption></figure>
|
|
||||||
<figure><audio src="https://telegram.org/example/audio.ogg"></audio><figcaption>Voice note caption</figcaption></figure>
|
|
||||||
<figure><video src="https://telegram.org/example/animation.gif" tg-spoiler></video><figcaption>Animation caption</figcaption></figure>
|
|
||||||
|
|
||||||
<tg-map lat="41.9" long="12.5" zoom="14"/>
|
|
||||||
<figure><tg-map lat="41.9" long="12.5" zoom="14"/><figcaption>Map caption</figcaption></figure>
|
|
||||||
|
|
||||||
<tg-collage><img src="https://telegram.org/example/photo.jpg"/><video src="https://telegram.org/example/video.mp4"/></tg-collage>
|
|
||||||
<tg-collage><video src="https://telegram.org/example/video.mp4"/><img src="https://telegram.org/example/photo.jpg"/><figcaption>Collage caption</figcaption></tg-collage>
|
|
||||||
<tg-slideshow><img src="https://telegram.org/example/photo.jpg"/><video src="https://telegram.org/example/video.mp4"/></tg-slideshow>
|
|
||||||
<tg-slideshow><video src="https://telegram.org/example/video.mp4"/><img src="https://telegram.org/example/photo.jpg"/><figcaption>Slideshow caption</figcaption></tg-slideshow>
|
|
||||||
|
|
||||||
<table><tr><th>Header 1</th><th>Header 2</th></tr><tr><td>Value 1</td><td>Value 2</td></tr></table>
|
|
||||||
<table bordered striped><caption>Table caption</caption>
|
|
||||||
<tr><td colspan="2" rowspan="2" align="left">Value</td><td align="center">Value2</td><td align="right">Value3</td></tr>
|
|
||||||
<tr><td valign="top">Value4</td><td valign="middle">Value5</td><td valign="bottom">Value6</td></tr>
|
|
||||||
<tr><td>Value7</td></tr></table>
|
|
||||||
|
|
||||||
<details><summary>Title</summary>Content</details>
|
|
||||||
<details open><summary>Title</summary>Content</details>
|
|
||||||
<tg-math-block>E = mc^2</tg-math-block>
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendRichMessage with Markdown-formatted content
|
|
||||||
onCommand("rich_markdown") {
|
|
||||||
val sent = sendRichMessage(
|
|
||||||
it.chat.id,
|
|
||||||
// InputRichMessageMarkdown factory — content described using Markdown formatting
|
|
||||||
InputRichMessageMarkdown(
|
|
||||||
"""
|
|
||||||
**bold text**
|
|
||||||
__bold text__
|
|
||||||
*italic text*
|
|
||||||
_italic text_
|
|
||||||
~~strikethrough text~~
|
|
||||||
`inline fixed-width code`
|
|
||||||
==marked text==
|
|
||||||
||spoiler||
|
|
||||||
|
|
||||||
[inline URL](https://t.me/)
|
|
||||||
[inline e-mail](mailto:user@example.com)
|
|
||||||
[inline phone number](tel:+123456789)
|
|
||||||
[inline mention of a user](tg://user?id=123456789)
|
|
||||||

|
|
||||||

|
|
||||||
${'$'}x^2 + y^2$
|
|
||||||
\#hashtag ${'$'}USD +12345678901, card: 4242 4242 4242 4242, https://t.me t.me a@t.me /command @username
|
|
||||||
all the text above was on the same line
|
|
||||||
|
|
||||||
# Heading 1
|
|
||||||
## Heading 2
|
|
||||||
### Heading 3
|
|
||||||
#### Heading 4
|
|
||||||
##### Heading 5
|
|
||||||
###### Heading 6
|
|
||||||
|
|
||||||
Paragraph text
|
|
||||||
|
|
||||||
```python
|
|
||||||
print('pre-formatted fixed-width code block written in the Python programming language')
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
- unordered list item
|
|
||||||
* unordered list item
|
|
||||||
+ unordered list item
|
|
||||||
|
|
||||||
1. ordered list item
|
|
||||||
2. ordered list item
|
|
||||||
|
|
||||||
- [ ] task list item
|
|
||||||
- [x] completed task list item
|
|
||||||
|
|
||||||
>Block quotation started
|
|
||||||
>
|
|
||||||
>Block quotation continued on the next line
|
|
||||||
>Block quotation continued on the same line
|
|
||||||
>
|
|
||||||
>The last line of the block quotation
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
| Header 1 | Header 2 |
|
|
||||||
|:---------|:--------:|
|
|
||||||
| left | center |
|
|
||||||
|
|
||||||
Text with a reference[^id1] and another one[^id2].
|
|
||||||
|
|
||||||
[^id1]: Definition of the first footnote.
|
|
||||||
[^id2]: Definition of the second footnote.
|
|
||||||
|
|
||||||
$${'$'}E = mc^2$$
|
|
||||||
|
|
||||||
```math
|
|
||||||
E = mc^2
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example Nested Syntax Report for _Q1_
|
|
||||||
Intro with <u>underlined text</u>, ==marked text==, and ${'$'}x^2 + y^2$.
|
|
||||||
**Bold _italic <u>underlined italic bold</u> italic_ bold**
|
|
||||||
<u>In inline tags, nested **markdown** is parsed</u>
|
|
||||||
>Quote with **bold text, ~~strikethrough, and <tg-spoiler>spoiler</tg-spoiler>~~**, plus [a link](https://t.me/).
|
|
||||||
|
|
||||||
- List item with `code`, <sup>superscript</sup>, <sub>subscript</sub>, and a footnote[^note]
|
|
||||||
- Another item with **bold <tg-spoiler><code>spoiler code</code></tg-spoiler>**
|
|
||||||
- Another item with ~~strikethrough and <ins>inserted text</ins>~~
|
|
||||||
|
|
||||||
| Metric | Value |
|
|
||||||
|:-------|------:|
|
|
||||||
| Speed | **42** <sup>ms</sup> |
|
|
||||||
| Status | <tg-spoiler>ready</tg-spoiler> |
|
|
||||||
|
|
||||||
[^note]: Footnote with _italic text_ and <u>HTML underline</u>.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Details blocks can contain Markdown content:
|
|
||||||
|
|
||||||
<details open><summary>Summary with **bold text**</summary>
|
|
||||||
|
|
||||||
### Details heading
|
|
||||||
- List item with _italic text_
|
|
||||||
- List item with <tg-spoiler>spoiler</tg-spoiler>
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
# Collages and slideshows can contain Markdown media blocks:
|
|
||||||
|
|
||||||
<tg-collage>
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
</tg-collage>
|
|
||||||
|
|
||||||
<tg-slideshow>
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
</tg-slideshow>
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
println(sent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendRichMessageDraft: stream partial rich messages sharing one draftId, then finalize
|
|
||||||
// with a full sendRichMessage. Emulates streaming of an AI-generated reply.
|
|
||||||
onCommand("rich_draft") {
|
|
||||||
val chatId = it.chat.id.toChatId()
|
|
||||||
val draftId = 1L
|
|
||||||
val parts = listOf(
|
|
||||||
"Thinking",
|
|
||||||
"Thinking about *rich* messages",
|
|
||||||
"Thinking about *rich* messages and how to _stream_ them"
|
|
||||||
)
|
|
||||||
parts.forEach { part ->
|
|
||||||
sendRichMessageDraft(chatId, draftId, InputRichMessageMarkdown(part))
|
|
||||||
delay(1000)
|
|
||||||
}
|
|
||||||
// finalize the streamed draft with the real message
|
|
||||||
sendRichMessage(chatId, InputRichMessageMarkdown("Done! Here is the *final* rich message."))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EditChatMessageRichText: send a rich message, then edit it with new rich content
|
|
||||||
onCommand("rich_edit") {
|
|
||||||
val sent = sendRichMessage(it.chat.id, InputRichMessageMarkdown("*Original* rich message"))
|
|
||||||
delay(2000)
|
|
||||||
execute(
|
|
||||||
EditChatMessageRichText(
|
|
||||||
chatId = sent.chat.id,
|
|
||||||
messageId = sent.messageId,
|
|
||||||
// the new rich_message parameter of editMessageText
|
|
||||||
richMessage = InputRichMessageMarkdown("*Edited* rich message — now _updated_")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitRichMessage expectation: wait for the user to send a rich message
|
|
||||||
onCommand("wait_rich") {
|
|
||||||
reply(it, "Send me a rich message now")
|
|
||||||
val richMessageContent = waitRichMessage().first()
|
|
||||||
reply(
|
|
||||||
it,
|
|
||||||
"Got rich message with ${richMessageContent.richMessage.blocks.size} block(s)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// onRichMessage trigger: incoming messages carrying the new rich_message field
|
|
||||||
onRichMessage { message ->
|
|
||||||
val richMessage = message.content.richMessage
|
|
||||||
println("=== Rich message received ===")
|
|
||||||
println(" isRtl: ${richMessage.isRtl}")
|
|
||||||
println(" blocks: ${richMessage.blocks.size}")
|
|
||||||
richMessage.blocks.forEachIndexed { index, block ->
|
|
||||||
println(" [$index] $block")
|
|
||||||
}
|
|
||||||
reply(message, "Received a rich message with ${richMessage.blocks.size} block(s)")
|
|
||||||
execute(
|
|
||||||
message.content.createResend(
|
|
||||||
message.chat.id,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputRichMessageContent as InputMessageContent of inline query results
|
|
||||||
onBaseInlineQuery { query ->
|
|
||||||
answer(
|
|
||||||
query,
|
|
||||||
results = listOf(
|
|
||||||
InlineQueryResultArticle(
|
|
||||||
InlineQueryId("rich_html"),
|
|
||||||
"Rich message (HTML)",
|
|
||||||
// InputRichMessageContent wraps an InputRichMessage and is a valid InputMessageContent
|
|
||||||
InputRichMessageContent(
|
|
||||||
InputRichMessageHTML("<b>Bold</b> rich message sent via inline query")
|
|
||||||
),
|
|
||||||
description = "InputRichMessageContent built from HTML"
|
|
||||||
),
|
|
||||||
InlineQueryResultArticle(
|
|
||||||
InlineQueryId("rich_markdown"),
|
|
||||||
"Rich message (Markdown)",
|
|
||||||
InputRichMessageContent(
|
|
||||||
InputRichMessageMarkdown("*Bold* rich message sent via inline query")
|
|
||||||
),
|
|
||||||
description = "InputRichMessageContent built from Markdown"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
cachedTime = 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// onlyRichMessageContentMessages: filter a Flow<ContentMessage<*>> down to rich message content
|
|
||||||
allUpdatesFlow
|
|
||||||
.mapNotNull { it.baseSentMessageUpdateOrNull() ?.data ?.contentMessageOrNull() }
|
|
||||||
.onlyRichMessageContentMessages()
|
|
||||||
.subscribeLoggingDropExceptions(scope = this) { richMessageContentMessage ->
|
|
||||||
println("[onlyRichMessageContentMessages] ${richMessageContentMessage.content.richMessage.blocks.size} blocks")
|
|
||||||
}
|
|
||||||
|
|
||||||
setMyCommands(
|
|
||||||
BotCommand("rich_html", "Send a rich message described with HTML"),
|
|
||||||
BotCommand("rich_markdown", "Send a rich message described with Markdown"),
|
|
||||||
BotCommand("rich_draft", "Stream a rich message draft, 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"),
|
|
||||||
)
|
|
||||||
|
|
||||||
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
|
||||||
println(it)
|
|
||||||
}
|
|
||||||
}.second.join()
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
|
// maven { url "https://proxy.nexus.inmo.dev/repository/maven-releases/" }
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ org.gradle.jvmargs=-Xmx3148m
|
|||||||
kotlin.daemon.jvmargs=-Xmx3g -Xms500m
|
kotlin.daemon.jvmargs=-Xmx3g -Xms500m
|
||||||
|
|
||||||
|
|
||||||
kotlin_version=2.3.20
|
kotlin_version=2.4.0
|
||||||
telegram_bot_api_version=35.0.0
|
telegram_bot_api_version=34.0.0
|
||||||
micro_utils_version=0.29.1
|
micro_utils_version=0.29.1
|
||||||
serialization_version=1.10.0
|
serialization_version=1.10.0
|
||||||
ktor_version=3.4.1
|
ktor_version=3.4.1
|
||||||
|
|||||||
@@ -77,7 +77,3 @@ include ":GuestQueryBot"
|
|||||||
include ":LivePhotosBot"
|
include ":LivePhotosBot"
|
||||||
|
|
||||||
include ":ChatManagementBot"
|
include ":ChatManagementBot"
|
||||||
|
|
||||||
include ":RichMessagesBot"
|
|
||||||
|
|
||||||
include ":JoinRequestQueriesBot"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user