aboutsummaryrefslogtreecommitdiffhomepage
path: root/td
diff options
context:
space:
mode:
authorlevlam <levlam@telegram.org>2026-06-30 19:57:42 +0300
committerlevlam <levlam@telegram.org>2026-06-30 19:57:42 +0300
commit669bcb5af6739acc617e992675feb8d6c670dd19 (patch)
tree4add31bfcbb49e1fc3a22eff6762cb9e2eefc951 /td
parentbb705e0cb397187edc7932b4ab68870de12d702a (diff)
Add td_api::sendEphemeralMessage.
Diffstat (limited to 'td')
-rw-r--r--td/generate/scheme/td_api.tl12
-rw-r--r--td/telegram/MessagesManager.cpp144
-rw-r--r--td/telegram/MessagesManager.h6
-rw-r--r--td/telegram/Requests.cpp12
-rw-r--r--td/telegram/Requests.h2
-rw-r--r--td/telegram/cli.cpp9
6 files changed, 173 insertions, 12 deletions
diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl
index e9591a66f..9032aa6b9 100644
--- a/td/generate/scheme/td_api.tl
+++ b/td/generate/scheme/td_api.tl
@@ -12043,6 +12043,18 @@ sendQuickReplyShortcutMessages chat_id:int53 shortcut_id:int32 sending_id:int32
//@paid_message_star_count The number of Telegram Stars the user agreed to pay to send the messages. Ignored if messageSendingStateFailed.required_paid_message_star_count == 0
resendMessages chat_id:int53 message_ids:vector<int53> quote:inputTextQuote paid_message_star_count:int53 = Messages;
+//@description Sends an ephemeral message which will be received only by one bot in a chat. Currently, only ephemeral bot commands and replies to bot ephemeral messages can be sent using the method.
+//-The message is persistent across application restarts only if the message database is used. Returns the sent message
+//@chat_id Target chat
+//@topic_id Topic in which the message will be sent; pass null if none
+//@receiver_user_id Identifier of the user who will receive the message
+//@reply_to Information about the message to be replied; pass null if none. The message can be an incoming ephemeral message
+//@sending_id Non-persistent identifier, which will be returned back in messageSendingStatePending object and can be used to match sent messages and corresponding updateNewMessage updates
+//@only_preview Pass true to get a fake message instead of actually sending them
+//@reply_markup Markup for replying to the message; pass null if none; for bots only
+//@input_message_content The content of the message to be added. Must be one of the following types: inputMessageText, inputMessageRichMessage, inputMessageAnimation, inputMessageAudio, inputMessageDocument, inputMessagePhoto, inputMessageSticker, inputMessageVideo, inputMessageVideoNote, inputMessageVoiceNote, inputMessageLocation, inputMessageVenue, inputMessageContact
+sendEphemeralMessage chat_id:int53 topic_id:MessageTopic receiver_user_id:int53 reply_to:InputMessageReplyTo sending_id:int32 only_preview:Bool reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message;
+
//@description Adds a local message to a chat. The message is persistent across application restarts only if the message database is used. Returns the added message
//@chat_id Target chat; channel direct messages chats aren't supported
//@sender_id Identifier of the sender of the message
diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp
index eaffb3106..b1659bec9 100644
--- a/td/telegram/MessagesManager.cpp
+++ b/td/telegram/MessagesManager.cpp
@@ -1530,11 +1530,12 @@ class SendMediaQuery final : public Td::ResultHandler {
public:
void send(vector<FileUploadId> file_upload_ids, vector<FileUploadId> thumbnail_file_upload_ids,
vector<FileId> cover_file_ids, int32 flags, DialogId dialog_id,
- tl_object_ptr<telegram_api::InputPeer> as_input_peer, const MessageInputReplyTo &input_reply_to,
- const MessageTopic &message_topic, int32 schedule_date, int32 schedule_repeat_period,
- MessageEffectId effect_id, int64 paid_message_star_count, const SuggestedPost *suggested_post,
- const unique_ptr<ReplyMarkup> &reply_markup, const FormattedText *text, InputMedia &&input_media,
- MessageContentType content_type, bool is_copy, int64 random_id, NetQueryRef *send_query_ref) {
+ tl_object_ptr<telegram_api::InputPeer> as_input_peer, UserId receiver_user_id,
+ const MessageInputReplyTo &input_reply_to, const MessageTopic &message_topic, int32 schedule_date,
+ int32 schedule_repeat_period, MessageEffectId effect_id, int64 paid_message_star_count,
+ const SuggestedPost *suggested_post, const unique_ptr<ReplyMarkup> &reply_markup, const FormattedText *text,
+ InputMedia &&input_media, MessageContentType content_type, bool is_copy, int64 random_id,
+ NetQueryRef *send_query_ref) {
random_id_ = random_id;
file_upload_ids_ = std::move(file_upload_ids);
thumbnail_file_upload_ids_ = std::move(thumbnail_file_upload_ids);
@@ -1551,6 +1552,41 @@ class SendMediaQuery final : public Td::ResultHandler {
}
auto reply_to = input_reply_to.get_input_reply_to(td_, message_topic, false, dialog_id, flags);
+ if (receiver_user_id != UserId()) {
+ auto r_input_user = td_->user_manager_->get_input_user(receiver_user_id);
+ if (r_input_user.is_error()) {
+ return on_error(r_input_user.move_as_error());
+ }
+ flags = 0;
+ if (reply_to != nullptr) {
+ flags |= telegram_api::ephemeral_sendMessage::REPLY_TO_MASK;
+ }
+ auto entities = get_input_message_entities(td_->user_manager_.get(), text, "SendMediaQuery");
+ if (!entities.empty()) {
+ flags |= telegram_api::ephemeral_sendMessage::ENTITIES_MASK;
+ }
+ if (input_media.rich_message_ != nullptr) {
+ flags |= telegram_api::ephemeral_sendMessage::RICH_MESSAGE_MASK;
+ }
+ if (input_media.media_ != nullptr) {
+ flags |= telegram_api::ephemeral_sendMessage::MEDIA_MASK;
+ }
+ auto input_reply_markup = get_input_reply_markup(td_->user_manager_.get(), reply_markup);
+ if (input_reply_markup != nullptr) {
+ flags |= telegram_api::ephemeral_sendMessage::REPLY_MARKUP_MASK;
+ }
+
+ auto query = G()->net_query_creator().create(
+ telegram_api::ephemeral_sendMessage(flags, std::move(input_peer), r_input_user.move_as_ok(), 0,
+ text == nullptr ? string() : text->text, std::move(entities),
+ std::move(input_media.media_), std::move(input_reply_markup),
+ std::move(input_media.rich_message_), random_id, std::move(reply_to)),
+ {{dialog_id, MessageContentType::Text},
+ {dialog_id, is_copy ? MessageContentType::Photo : MessageContentType::Text}});
+ *send_query_ref = query.get_weak();
+ send_query(std::move(query));
+ return;
+ }
if (input_media.rich_message_ != nullptr) {
CHECK(text == nullptr);
if (reply_to != nullptr) {
@@ -1599,7 +1635,7 @@ class SendMediaQuery final : public Td::ResultHandler {
auto query = G()->net_query_creator().create(
telegram_api::messages_sendMedia(
flags, false, false, false, false, false, false, false, std::move(input_peer), std::move(reply_to),
- std::move(input_media.media_), text == nullptr ? "" : text->text, random_id,
+ std::move(input_media.media_), text == nullptr ? string() : text->text, random_id,
get_input_reply_markup(td_->user_manager_.get(), reply_markup), std::move(entities), schedule_date,
schedule_repeat_period, std::move(as_input_peer), nullptr, effect_id.get(), paid_message_star_count,
std::move(post)),
@@ -22015,10 +22051,10 @@ void MessagesManager::do_send_message_media(DialogId dialog_id, const Message *m
td_->create_handler<SendMediaQuery>()->send(
m->file_upload_ids, m->thumbnail_file_upload_ids,
get_message_content_cover_any_file_ids(td_, m->content.get()), get_message_flags(m), dialog_id,
- get_send_message_as_input_peer(m), *get_message_input_reply_to(m), get_send_message_topic(dialog_id, m),
- get_message_schedule_date(m), get_message_schedule_repeat_period(m), m->effect_id,
- m->paid_message_star_count, m->suggested_post.get(), m->reply_markup, caption, std::move(input_media),
- m->content->get_type(), m->is_copy, random_id, &m->send_query_ref);
+ get_send_message_as_input_peer(m), m->receiver_user_id, *get_message_input_reply_to(m),
+ get_send_message_topic(dialog_id, m), get_message_schedule_date(m), get_message_schedule_repeat_period(m),
+ m->effect_id, m->paid_message_star_count, m->suggested_post.get(), m->reply_markup, caption,
+ std::move(input_media), m->content->get_type(), m->is_copy, random_id, &m->send_query_ref);
}));
}
@@ -22515,7 +22551,7 @@ void MessagesManager::on_text_message_ready_to_send(DialogId dialog_id, MessageI
CHECK(message_text != nullptr);
int64 random_id = begin_send_message(dialog_id, m);
auto input_media = get_message_content_input_media_web_page(td_, content);
- if (input_media == nullptr) {
+ if (input_media == nullptr && m->receiver_user_id == UserId()) {
td_->create_handler<SendMessageQuery>()->send(
get_message_flags(m), dialog_id, get_send_message_as_input_peer(m), *get_message_input_reply_to(m),
get_send_message_topic(dialog_id, m), get_message_schedule_date(m), get_message_schedule_repeat_period(m),
@@ -22523,7 +22559,7 @@ void MessagesManager::on_text_message_ready_to_send(DialogId dialog_id, MessageI
message_text == nullptr ? FormattedText() : *message_text, m->is_copy, random_id, &m->send_query_ref);
} else {
td_->create_handler<SendMediaQuery>()->send(
- {}, {}, {}, get_message_flags(m), dialog_id, get_send_message_as_input_peer(m),
+ {}, {}, {}, get_message_flags(m), dialog_id, get_send_message_as_input_peer(m), m->receiver_user_id,
*get_message_input_reply_to(m), get_send_message_topic(dialog_id, m), get_message_schedule_date(m),
get_message_schedule_repeat_period(m), m->effect_id, m->paid_message_star_count, m->suggested_post.get(),
m->reply_markup, message_text, std::move(input_media), MessageContentType::Text, m->is_copy, random_id,
@@ -25119,6 +25155,90 @@ void MessagesManager::process_suggested_post(MessageFullId message_full_id, bool
std::move(promise));
}
+Result<td_api::object_ptr<td_api::message>> MessagesManager::send_ephemeral_message(
+ DialogId dialog_id, const td_api::object_ptr<td_api::MessageTopic> &topic_id, UserId receiver_user_id,
+ td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to, int32 sending_id, bool only_preview,
+ td_api::object_ptr<td_api::ReplyMarkup> &&reply_markup,
+ td_api::object_ptr<td_api::InputMessageContent> &&input_message_content) {
+ if (input_message_content == nullptr) {
+ return Status::Error(400, "Can't send message without content");
+ }
+ switch (input_message_content->get_id()) {
+ case td_api::inputMessageText::ID:
+ case td_api::inputMessageRichMessage::ID:
+ case td_api::inputMessageAnimation::ID:
+ case td_api::inputMessageAudio::ID:
+ case td_api::inputMessageDocument::ID:
+ case td_api::inputMessagePhoto::ID:
+ case td_api::inputMessageSticker::ID:
+ case td_api::inputMessageVideo::ID:
+ case td_api::inputMessageVideoNote::ID:
+ case td_api::inputMessageVoiceNote::ID:
+ case td_api::inputMessageLocation::ID:
+ case td_api::inputMessageVenue::ID:
+ case td_api::inputMessageContact::ID:
+ // ok
+ break;
+ default:
+ return Status::Error(400, "Unallowed message content");
+ }
+
+ Dialog *d = get_dialog_force(dialog_id, "send_ephemeral_message");
+ if (d == nullptr) {
+ return Status::Error(400, "Chat not found");
+ }
+ TRY_STATUS(td_->user_manager_->get_input_user(receiver_user_id));
+ TRY_RESULT(message_topic, MessageTopic::get_send_message_topic(td_, dialog_id, topic_id));
+ auto input_reply_to = create_message_input_reply_to(d, message_topic, std::move(reply_to), false, true);
+ TRY_RESULT(message_reply_markup, get_dialog_reply_markup(dialog_id, std::move(reply_markup)));
+ TRY_RESULT(message_content, process_input_message_content(dialog_id, std::move(input_message_content), false));
+
+ // there must be no errors after get_message_to_send call
+
+ auto content = dup_message_content(td_, dialog_id, message_content.content.get(), MessageContentDupType::Send,
+ MessageCopyOptions());
+ bool need_update_dialog_pos = false;
+ unique_ptr<Message> message;
+ Message *m;
+ if (only_preview) {
+ message =
+ create_message_to_send(d, message_topic, std::move(input_reply_to), MessageSendOptions(), std::move(content),
+ message_content.invert_media, false, nullptr, DialogId(), false, DialogId(), false);
+ m = message.get();
+ } else {
+ m = get_message_to_send(d, message_topic, std::move(input_reply_to), MessageSendOptions(), std::move(content),
+ message_content.invert_media, &need_update_dialog_pos);
+ }
+ m->receiver_user_id = receiver_user_id;
+ m->sending_id = sending_id;
+ m->reply_markup = std::move(message_reply_markup);
+ m->disable_web_page_preview = message_content.disable_web_page_preview;
+ m->clear_draft = message_content.clear_draft;
+ if (message_content.ttl.is_valid()) {
+ m->ttl = message_content.ttl;
+ m->is_content_secret = m->ttl.is_secret_message_content(m->content->get_type());
+ }
+ m->send_emoji = std::move(message_content.emoji);
+
+ if (only_preview) {
+ return get_message_object(dialog_id, m, "send_ephemeral_message");
+ }
+
+ clear_dialog_draft_by_sent_message(d, m, !need_update_dialog_pos);
+
+ save_send_message_log_event(dialog_id, m);
+ do_send_message(dialog_id, m);
+
+ if (!td_->auth_manager_->is_bot()) {
+ send_update_new_message(d, m);
+ if (need_update_dialog_pos) {
+ send_update_chat_last_message(d, "send_ephemeral_message");
+ }
+ }
+
+ return get_message_object(dialog_id, m, "send_ephemeral_message");
+}
+
Result<MessageId> MessagesManager::add_local_message(
DialogId dialog_id, td_api::object_ptr<td_api::MessageSender> &&sender,
td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to, bool disable_notification,
diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h
index 096050db6..07e0e840d 100644
--- a/td/telegram/MessagesManager.h
+++ b/td/telegram/MessagesManager.h
@@ -493,6 +493,12 @@ class MessagesManager final : public Actor {
void process_suggested_post(MessageFullId message_full_id, bool is_rejected, int32 schedule_date,
const string &comment, Promise<Unit> &&promise);
+ Result<td_api::object_ptr<td_api::message>> send_ephemeral_message(
+ DialogId dialog_id, const td_api::object_ptr<td_api::MessageTopic> &topic_id, UserId receiver_user_id,
+ td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to, int32 sending_id, bool only_preview,
+ tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
+ tl_object_ptr<td_api::InputMessageContent> &&input_message_content) TD_WARN_UNUSED_RESULT;
+
Result<MessageId> add_local_message(
DialogId dialog_id, td_api::object_ptr<td_api::MessageSender> &&sender,
td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to, bool disable_notification,
diff --git a/td/telegram/Requests.cpp b/td/telegram/Requests.cpp
index 3b06ad7da..1fcc7b7e1 100644
--- a/td/telegram/Requests.cpp
+++ b/td/telegram/Requests.cpp
@@ -4242,6 +4242,18 @@ void Requests::on_request(uint64 id, td_api::sendInlineQueryResultMessage &reque
}
}
+void Requests::on_request(uint64 id, td_api::sendEphemeralMessage &request) {
+ auto r_sent_message = td_->messages_manager_->send_ephemeral_message(
+ DialogId(request.chat_id_), request.topic_id_, UserId(request.receiver_user_id_), std::move(request.reply_to_),
+ request.sending_id_, request.only_preview_, std::move(request.reply_markup_),
+ std::move(request.input_message_content_));
+ if (r_sent_message.is_error()) {
+ send_closure(td_actor_, &Td::send_error, id, r_sent_message.move_as_error());
+ } else {
+ send_closure(td_actor_, &Td::send_result, id, r_sent_message.move_as_ok());
+ }
+}
+
void Requests::on_request(uint64 id, td_api::addLocalMessage &request) {
CHECK_IS_USER();
diff --git a/td/telegram/Requests.h b/td/telegram/Requests.h
index a2933b8fa..037d3a912 100644
--- a/td/telegram/Requests.h
+++ b/td/telegram/Requests.h
@@ -676,6 +676,8 @@ class Requests {
void on_request(uint64 id, td_api::sendInlineQueryResultMessage &request);
+ void on_request(uint64 id, td_api::sendEphemeralMessage &request);
+
void on_request(uint64 id, td_api::addLocalMessage &request);
void on_request(uint64 id, td_api::editMessageText &request);
diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp
index 258bee06b..3966fcbf1 100644
--- a/td/telegram/cli.cpp
+++ b/td/telegram/cli.cpp
@@ -2945,6 +2945,12 @@ class CliClient final : public Actor {
void send_message(int64 chat_id, td_api::object_ptr<td_api::InputMessageContent> &&input_message_content,
bool disable_notification = false, bool from_background = false) {
+ if (ephemeral_message_receiver_id_ != 0) {
+ send_request(td_api::make_object<td_api::sendEphemeralMessage>(
+ chat_id, get_message_topic_id(), ephemeral_message_receiver_id_, get_input_message_reply_to(), 123,
+ only_preview_, nullptr, std::move(input_message_content)));
+ return;
+ }
if (!business_connection_id_.empty()) {
send_request(td_api::make_object<td_api::sendBusinessMessage>(
business_connection_id_, chat_id, get_input_message_reply_to(), disable_notification, rand_bool(),
@@ -6463,6 +6469,8 @@ class CliClient final : public Actor {
saved_messages_topic_id_ = as_chat_id(args);
} else if (op == "sqrs") {
quick_reply_shortcut_name_ = args;
+ } else if (op == "smer") {
+ get_args(args, ephemeral_message_receiver_id_);
} else if (op == "smas") {
added_sticker_file_ids_ = as_file_ids(args);
} else if (op == "smc") {
@@ -9025,6 +9033,7 @@ class CliClient final : public Actor {
GiftCollectionId gift_collection_id_;
int64 saved_messages_topic_id_ = 0;
string quick_reply_shortcut_name_;
+ int64 ephemeral_message_receiver_id_ = 0;
vector<int32> added_sticker_file_ids_;
string caption_;
string rich_message_media_;