diff options
| author | levlam <levlam@telegram.org> | 2026-06-30 19:57:42 +0300 |
|---|---|---|
| committer | levlam <levlam@telegram.org> | 2026-06-30 19:57:42 +0300 |
| commit | 669bcb5af6739acc617e992675feb8d6c670dd19 (patch) | |
| tree | 4add31bfcbb49e1fc3a22eff6762cb9e2eefc951 | |
| parent | bb705e0cb397187edc7932b4ab68870de12d702a (diff) | |
Add td_api::sendEphemeralMessage.
| -rw-r--r-- | td/generate/scheme/td_api.tl | 12 | ||||
| -rw-r--r-- | td/telegram/MessagesManager.cpp | 144 | ||||
| -rw-r--r-- | td/telegram/MessagesManager.h | 6 | ||||
| -rw-r--r-- | td/telegram/Requests.cpp | 12 | ||||
| -rw-r--r-- | td/telegram/Requests.h | 2 | ||||
| -rw-r--r-- | td/telegram/cli.cpp | 9 |
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_; |
