mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2026-09-29 11:55:01 +00:00
update examples for ktgbotapi 37.0.0
This commit is contained in:
@@ -1,22 +1,31 @@
|
||||
# EphemeralMessagesBot
|
||||
|
||||
Demonstrates ephemeral messages: group messages that Telegram shows only to one receiver.
|
||||
Demonstrates Telegram Bot API 10.2 and 10.3 ephemeral messages: group messages that Telegram shows only to one receiver.
|
||||
|
||||
## Behavior
|
||||
|
||||
- `/ephemeral` replies with a **Reveal a secret** inline button. The command is registered with Telegram as
|
||||
an ephemeral command.
|
||||
- Pressing the button (`reveal` callback data) sends a personal message visible only to the user who
|
||||
pressed it. After three seconds the bot edits the message, then deletes it three seconds later.
|
||||
- Pressing the button (`reveal` callback data) uses `EphemeralMessageParameters` to replace the callback-query
|
||||
message with a personal rich message visible only to the user who pressed it. After three seconds the bot replaces
|
||||
its text with typed rich blocks, then deletes it three seconds later.
|
||||
- `/ephemeral_photo` waits for a photo from the same user and chat, sends it back ephemerally by its Telegram file ID,
|
||||
downloads it, and edits the ephemeral media using a new multipart upload. A second edit sets
|
||||
`showCaptionAboveMedia = true`.
|
||||
- `/ephemeral_live_photo` waits for a Live Photo, sends it ephemerally by existing file IDs, then downloads and
|
||||
re-uploads both its main file and secondary `photo` file in one `editEphemeralMessageMedia` request. This exercises
|
||||
ktgbotapi 37.0.0's secondary multipart attachment collection.
|
||||
- When the bot receives an ephemeral content message, it sends two ephemeral replies: one through the
|
||||
general `reply` API and one through the explicit `replyToEphemeral` API.
|
||||
general `reply` API and one through the explicit `replyToEphemeral` API. The explicit form also uses
|
||||
`EphemeralMessageParameters`.
|
||||
- Updates and basic bot information are printed to standard output.
|
||||
|
||||
## Setup
|
||||
|
||||
Create a bot token, keep it secret, and add the bot to a group. The bot must be allowed to send messages
|
||||
there; this example does not request or validate group permissions itself. Use a Telegram environment that
|
||||
supports ephemeral messages.
|
||||
supports ephemeral messages. The photo demonstrations download all selected media into memory before uploading it
|
||||
again.
|
||||
|
||||
## Run
|
||||
|
||||
|
||||
@@ -6,29 +6,47 @@ import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
||||
import dev.inmo.tgbotapi.extensions.api.deleteEphemeralMessage
|
||||
import dev.inmo.tgbotapi.extensions.api.edit.text.editEphemeralMessageText
|
||||
import dev.inmo.tgbotapi.extensions.api.edit.caption.editEphemeralMessageCaption
|
||||
import dev.inmo.tgbotapi.extensions.api.edit.media.editEphemeralMessageMedia
|
||||
import dev.inmo.tgbotapi.extensions.api.edit.text.editEphemeralMessageRichText
|
||||
import dev.inmo.tgbotapi.extensions.api.files.downloadFile
|
||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||
import dev.inmo.tgbotapi.extensions.api.send.replyToEphemeral
|
||||
import dev.inmo.tgbotapi.extensions.api.send.sendTextMessage
|
||||
import dev.inmo.tgbotapi.extensions.api.send.sendRichMessage
|
||||
import dev.inmo.tgbotapi.extensions.api.send.media.sendLivePhoto
|
||||
import dev.inmo.tgbotapi.extensions.api.send.media.sendPhoto
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitLivePhotoMessage
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitPhotoMessage
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery
|
||||
import dev.inmo.tgbotapi.extensions.utils.fromUserMessageOrNull
|
||||
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
|
||||
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
|
||||
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
|
||||
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||
import dev.inmo.tgbotapi.types.BotCommand
|
||||
import dev.inmo.tgbotapi.types.EphemeralMessageParameters
|
||||
import dev.inmo.tgbotapi.types.ephemeralReplyReceiverUserIdOrNull
|
||||
import dev.inmo.tgbotapi.types.media.TelegramMediaLivePhoto
|
||||
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.PossiblyEphemeralMessage
|
||||
import dev.inmo.tgbotapi.types.rich.InputRichMessageBlocks
|
||||
import korlibs.time.seconds
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
||||
/**
|
||||
* Runs the ephemeral-messages example bot using long polling.
|
||||
*
|
||||
* `/ephemeral` posts an inline button whose callback sends, edits, and deletes a message visible only to
|
||||
* the user who pressed it. Incoming [PossiblyEphemeralMessage] instances receive both an automatic
|
||||
* `/ephemeral` posts an inline button whose callback replaces it with a rich message visible only to
|
||||
* the user who pressed it, edits that message, and deletes it. `/ephemeral_photo` demonstrates uploading new media
|
||||
* to an ephemeral edit and moving its caption above the media. `/ephemeral_live_photo` demonstrates collecting both
|
||||
* multipart files of a Live Photo edit. Incoming [PossiblyEphemeralMessage] instances receive both an automatic
|
||||
* ephemeral [reply] and an explicit [replyToEphemeral].
|
||||
*
|
||||
* [args] must start with the bot token. The optional exact values `debug` and `testServer` respectively
|
||||
@@ -68,18 +86,25 @@ suspend fun main(vararg args: String) {
|
||||
)
|
||||
}
|
||||
|
||||
// Send an ephemeral message in response to a callback query. `receiverUserId` + `callbackQueryId`
|
||||
// make the outgoing message ephemeral — Telegram shows it only to the querying user (and this also
|
||||
// serves as the answer to the callback query).
|
||||
// Bot API 10.3 groups the recipient/callback fields in EphemeralMessageParameters. Setting
|
||||
// replaceCallbackQueryMessage replaces the button message only for the user who pressed it.
|
||||
onMessageDataCallbackQuery(Regex("reveal")) { query ->
|
||||
val chatId = query.message.chat.id
|
||||
val receiverUserId = query.from.id
|
||||
|
||||
val sent = sendTextMessage(
|
||||
val sent = sendRichMessage(
|
||||
chatId,
|
||||
"🔒 ${query.from.firstName}, here is your personal secret: 42",
|
||||
receiverUserId = receiverUserId,
|
||||
callbackQueryId = query.id,
|
||||
InputRichMessageBlocks {
|
||||
paragraph {
|
||||
plain("🔒 ${query.from.firstName}, here is your personal secret: ")
|
||||
code("42")
|
||||
}
|
||||
},
|
||||
ephemeralMessageParameters = EphemeralMessageParameters(
|
||||
receiverUserId = receiverUserId,
|
||||
callbackQueryId = query.id,
|
||||
replaceCallbackQueryMessage = true,
|
||||
),
|
||||
)
|
||||
|
||||
// Only the group-family Common*ContentMessage types implement PossiblyEphemeralMessage, so the
|
||||
@@ -87,14 +112,91 @@ suspend fun main(vararg args: String) {
|
||||
val ephemeralMessageId = (sent as? PossiblyEphemeralMessage)?.ephemeralMessageId
|
||||
if (ephemeralMessageId != null) {
|
||||
delay(3.seconds)
|
||||
// editEphemeralMessageText: address the ephemeral message by chatId + receiverUserId + ephemeralMessageId
|
||||
editEphemeralMessageText(chatId, receiverUserId, ephemeralMessageId, "🔓 Revealed: the answer is 42")
|
||||
// editEphemeralMessageText now accepts rich_message; the typed extension exposes that as
|
||||
// editEphemeralMessageRichText.
|
||||
editEphemeralMessageRichText(
|
||||
chatId,
|
||||
receiverUserId,
|
||||
ephemeralMessageId,
|
||||
InputRichMessageBlocks {
|
||||
h2("Secret revealed")
|
||||
paragraph { plain("The answer is "); code("42") }
|
||||
},
|
||||
)
|
||||
delay(3.seconds)
|
||||
// deleteEphemeralMessage: same addressing (there is also a PossiblyEphemeralMessage overload)
|
||||
deleteEphemeralMessage(chatId, receiverUserId, ephemeralMessageId)
|
||||
}
|
||||
}
|
||||
|
||||
// Upload a received photo again as a brand-new multipart file while editing an ephemeral media message.
|
||||
onCommand("ephemeral_photo") { origin ->
|
||||
val receiverUserId = origin.fromUserMessageOrNull()?.user?.id ?: return@onCommand
|
||||
reply(origin, "Send a photo. I will return it as ephemeral media and then re-upload it in an edit.")
|
||||
val photoMessage = waitPhotoMessage().filter {
|
||||
it.sameChat(origin) && it.fromUserMessageOrNull()?.user?.id == receiverUserId
|
||||
}.first()
|
||||
|
||||
val sent = sendPhoto(
|
||||
photoMessage.chat.id,
|
||||
photoMessage.content.media.fileId,
|
||||
text = "Ephemeral photo using its existing Telegram file ID",
|
||||
ephemeralMessageParameters = EphemeralMessageParameters(receiverUserId),
|
||||
)
|
||||
val ephemeralMessageId = (sent as? PossiblyEphemeralMessage)?.ephemeralMessageId
|
||||
?: return@onCommand
|
||||
|
||||
val photoBytes = downloadFile(photoMessage.content)
|
||||
editEphemeralMessageMedia(
|
||||
photoMessage.chat.id,
|
||||
receiverUserId,
|
||||
ephemeralMessageId,
|
||||
TelegramMediaPhoto(photoBytes.asMultipartFile("ephemeral-photo.jpg")),
|
||||
)
|
||||
editEphemeralMessageCaption(
|
||||
photoMessage.chat.id,
|
||||
receiverUserId,
|
||||
ephemeralMessageId,
|
||||
caption = "This caption is above newly uploaded media",
|
||||
showCaptionAboveMedia = true,
|
||||
)
|
||||
}
|
||||
|
||||
// A Live Photo has a main file plus a secondary `photo` file. ktgbotapi 37.0.0 includes both multipart
|
||||
// attachments when EditEphemeralMessageMedia builds its request.
|
||||
onCommand("ephemeral_live_photo") { origin ->
|
||||
val receiverUserId = origin.fromUserMessageOrNull()?.user?.id ?: return@onCommand
|
||||
reply(origin, "Send a Live Photo. I will send it ephemerally and edit it using two new uploads.")
|
||||
val livePhotoMessage = waitLivePhotoMessage().filter {
|
||||
it.sameChat(origin) && it.fromUserMessageOrNull()?.user?.id == receiverUserId
|
||||
}.first()
|
||||
val livePhoto = livePhotoMessage.content.media
|
||||
|
||||
val sent = sendLivePhoto(
|
||||
chatId = livePhotoMessage.chat.id,
|
||||
livePhoto = livePhoto,
|
||||
text = "Ephemeral Live Photo using existing Telegram file IDs",
|
||||
ephemeralMessageParameters = EphemeralMessageParameters(receiverUserId),
|
||||
)
|
||||
val ephemeralMessageId = (sent as? PossiblyEphemeralMessage)?.ephemeralMessageId
|
||||
?: return@onCommand
|
||||
|
||||
val livePhotoBytes = downloadFile(livePhoto)
|
||||
val coverPhotoBytes = livePhoto.photo?.let { downloadFile(it) }
|
||||
editEphemeralMessageMedia(
|
||||
livePhotoMessage.chat.id,
|
||||
receiverUserId,
|
||||
ephemeralMessageId,
|
||||
TelegramMediaLivePhoto(
|
||||
file = livePhotoBytes.asMultipartFile("ephemeral-live-photo.mp4"),
|
||||
photo = coverPhotoBytes?.asMultipartFile("ephemeral-live-photo-cover.jpg")
|
||||
?: livePhoto.photo?.fileId
|
||||
?: livePhoto.fileId,
|
||||
text = "Edited with newly uploaded main and cover files",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Incoming ephemeral messages: detect them via PossiblyEphemeralMessage, then answer them.
|
||||
onContentMessage { message ->
|
||||
val ephemeral = (message as? PossiblyEphemeralMessage)?.takeIf { it.ephemeralMessageId != null }
|
||||
@@ -109,7 +211,7 @@ suspend fun main(vararg args: String) {
|
||||
if (receiverUserId != null) {
|
||||
replyToEphemeral(
|
||||
message.chat.id,
|
||||
receiverUserId,
|
||||
EphemeralMessageParameters(receiverUserId),
|
||||
ephemeral.ephemeralMessageId!!,
|
||||
"Explicit ephemeral reply via replyToEphemeral",
|
||||
)
|
||||
@@ -119,6 +221,8 @@ suspend fun main(vararg args: String) {
|
||||
setMyCommands(
|
||||
// isEphemeral marks a command whose response is an ephemeral (personal) message
|
||||
BotCommand("ephemeral", "Post a button that reveals an ephemeral (personal) message", isEphemeral = true),
|
||||
BotCommand("ephemeral_photo", "Re-upload a photo through an ephemeral media edit", isEphemeral = true),
|
||||
BotCommand("ephemeral_live_photo", "Re-upload both files of an ephemeral Live Photo", isEphemeral = true),
|
||||
)
|
||||
|
||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||
|
||||
Reference in New Issue
Block a user