aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlevlam <levlam@telegram.org>2026-07-01 15:51:44 +0300
committerlevlam <levlam@telegram.org>2026-07-01 15:51:44 +0300
commit406d07ba55c7f763de39803fa1a042d85d1a2850 (patch)
tree7a96beb9ade8899cdda7b3d69c1910f14f0d1f8c
parent0f090ec07c81b2cfe5a75339243c2a5700c063ca (diff)
Support deletion of ephemeral messages.
-rw-r--r--td/telegram/MessageQueryManager.cpp108
-rw-r--r--td/telegram/MessageQueryManager.h9
-rw-r--r--td/telegram/MessagesManager.cpp19
-rw-r--r--td/telegram/TdDb.cpp1
-rw-r--r--td/telegram/logevent/LogEvent.h1
5 files changed, 133 insertions, 5 deletions
diff --git a/td/telegram/MessageQueryManager.cpp b/td/telegram/MessageQueryManager.cpp
index ef44bbfa4..74b29ff96 100644
--- a/td/telegram/MessageQueryManager.cpp
+++ b/td/telegram/MessageQueryManager.cpp
@@ -1662,6 +1662,45 @@ class DeleteScheduledMessagesQuery final : public Td::ResultHandler {
}
};
+class DeleteEphemeralMessageQuery final : public Td::ResultHandler {
+ Promise<Unit> promise_;
+ DialogId dialog_id_;
+
+ public:
+ explicit DeleteEphemeralMessageQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
+ }
+
+ void send(DialogId dialog_id, DialogId receiver_dialog_id, EphemeralMessageId ephemeral_message_id) {
+ dialog_id_ = dialog_id;
+
+ auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read);
+ if (input_peer == nullptr || receiver_dialog_id.get_type() != DialogType::User) {
+ return on_error(Status::Error(400, "Can't access the chat"));
+ }
+ auto r_input_user = td_->user_manager_->get_input_user(receiver_dialog_id.get_user_id());
+ if (r_input_user.is_error()) {
+ return on_error(Status::Error(400, "Can't access the user"));
+ }
+ send_query(G()->net_query_creator().create(telegram_api::ephemeral_deleteMessage(
+ std::move(input_peer), r_input_user.move_as_ok(), ephemeral_message_id.get())));
+ }
+
+ void on_result(BufferSlice packet) final {
+ auto result_ptr = fetch_result<telegram_api::ephemeral_deleteMessage>(packet);
+ if (result_ptr.is_error()) {
+ return on_error(result_ptr.move_as_error());
+ }
+ promise_.set_value(Unit());
+ }
+
+ void on_error(Status status) final {
+ if (!td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "DeleteEphemeralMessageQuery")) {
+ LOG(ERROR) << "Receive error for delete ephemeral messages: " << status;
+ }
+ promise_.set_error(std::move(status));
+ }
+};
+
class DeleteTopicHistoryQuery final : public Td::ResultHandler {
Promise<AffectedHistory> promise_;
DialogId dialog_id_;
@@ -3684,6 +3723,53 @@ void MessageQueryManager::delete_scheduled_messages_on_server(DialogId dialog_id
td_->create_handler<DeleteScheduledMessagesQuery>(std::move(promise))->send(dialog_id, std::move(message_ids));
}
+class MessageQueryManager::DeleteEphemeralMessageOnServerLogEvent {
+ public:
+ DialogId dialog_id_;
+ DialogId receiver_dialog_id_;
+ EphemeralMessageId ephemeral_message_id_;
+
+ template <class StorerT>
+ void store(StorerT &storer) const {
+ td::store(dialog_id_, storer);
+ td::store(receiver_dialog_id_, storer);
+ td::store(ephemeral_message_id_, storer);
+ }
+
+ template <class ParserT>
+ void parse(ParserT &parser) {
+ td::parse(dialog_id_, parser);
+ td::parse(receiver_dialog_id_, parser);
+ td::parse(ephemeral_message_id_, parser);
+ }
+};
+
+uint64 MessageQueryManager::save_delete_ephemeral_message_on_server_log_event(DialogId dialog_id,
+ DialogId receiver_dialog_id,
+ EphemeralMessageId ephemeral_message_id) {
+ DeleteEphemeralMessageOnServerLogEvent log_event{dialog_id, receiver_dialog_id, ephemeral_message_id};
+ return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteEphemeralMessageOnServer,
+ get_log_event_storer(log_event));
+}
+
+void MessageQueryManager::delete_ephemeral_message_on_server(DialogId dialog_id, DialogId receiver_dialog_id,
+ EphemeralMessageId ephemeral_message_id,
+ uint64 log_event_id, Promise<Unit> &&promise) {
+ LOG(INFO) << "Delete " << ephemeral_message_id << " from " << receiver_dialog_id << " in " << dialog_id
+ << " from server";
+
+ if (log_event_id == 0 && G()->use_message_database()) {
+ log_event_id =
+ save_delete_ephemeral_message_on_server_log_event(dialog_id, receiver_dialog_id, ephemeral_message_id);
+ }
+
+ auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise));
+ promise = std::move(new_promise); // to prevent self-move
+
+ td_->create_handler<DeleteEphemeralMessageQuery>(std::move(promise))
+ ->send(dialog_id, receiver_dialog_id, ephemeral_message_id);
+}
+
void MessageQueryManager::delete_topic_history(DialogId dialog_id, ForumTopicId forum_topic_id,
Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(
@@ -4261,6 +4347,28 @@ void MessageQueryManager::on_binlog_events(vector<BinlogEvent> &&events) {
delete_scheduled_messages_on_server(dialog_id, std::move(log_event.message_ids_), event.id_, Auto());
break;
}
+ case LogEvent::HandlerType::DeleteEphemeralMessageOnServer: {
+ if (!have_old_message_database) {
+ binlog_erase(G()->td_db()->get_binlog(), event.id_);
+ break;
+ }
+
+ DeleteEphemeralMessageOnServerLogEvent log_event;
+ log_event_parse(log_event, event.get_data()).ensure();
+
+ auto dialog_id = log_event.dialog_id_;
+ if (!td_->dialog_manager_->have_dialog_force(dialog_id, "DeleteEphemeralMessageOnServerLogEvent") ||
+ !td_->dialog_manager_->have_input_peer(dialog_id, true, AccessRights::Read)) {
+ binlog_erase(G()->td_db()->get_binlog(), event.id_);
+ break;
+ }
+
+ td_->messages_manager_->on_delete_ephemeral_messages(dialog_id, {log_event.ephemeral_message_id_});
+
+ delete_ephemeral_message_on_server(dialog_id, log_event.receiver_dialog_id_, log_event.ephemeral_message_id_,
+ event.id_, Auto());
+ break;
+ }
case LogEvent::HandlerType::DeleteTopicHistoryOnServer: {
if (!have_old_message_database) {
binlog_erase(G()->td_db()->get_binlog(), event.id_);
diff --git a/td/telegram/MessageQueryManager.h b/td/telegram/MessageQueryManager.h
index 7f0c04502..777b5c50f 100644
--- a/td/telegram/MessageQueryManager.h
+++ b/td/telegram/MessageQueryManager.h
@@ -12,6 +12,7 @@
#include "td/telegram/DialogId.h"
#include "td/telegram/DialogListId.h"
#include "td/telegram/EmojiGameInfo.h"
+#include "td/telegram/EphemeralMessageId.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/files/FileUploadId.h"
@@ -211,6 +212,10 @@ class MessageQueryManager final : public Actor {
void delete_scheduled_messages_on_server(DialogId dialog_id, vector<MessageId> message_ids, uint64 log_event_id,
Promise<Unit> &&promise);
+ void delete_ephemeral_message_on_server(DialogId dialog_id, DialogId receiver_dialog_id,
+ EphemeralMessageId ephemeral_message_id, uint64 log_event_id,
+ Promise<Unit> &&promise);
+
void delete_topic_history(DialogId dialog_id, ForumTopicId forum_topic_id, Promise<Unit> &&promise);
void delete_reactions_by_sender(DialogId dialog_id, DialogId sender_dialog_id, Promise<Unit> &&promise);
@@ -261,6 +266,7 @@ class MessageQueryManager final : public Actor {
class DeleteDialogMessagesByDateOnServerLogEvent;
class DeleteMessagesOnServerLogEvent;
class DeleteScheduledMessagesOnServerLogEvent;
+ class DeleteEphemeralMessageOnServerLogEvent;
class DeleteTopicHistoryOnServerLogEvent;
class ReadAllDialogMentionsOnServerLogEvent;
class ReadAllDialogReactionsOnServerLogEvent;
@@ -352,6 +358,9 @@ class MessageQueryManager final : public Actor {
static uint64 save_delete_scheduled_messages_on_server_log_event(DialogId dialog_id,
const vector<MessageId> &message_ids);
+ static uint64 save_delete_ephemeral_message_on_server_log_event(DialogId dialog_id, DialogId receiver_dialog_id,
+ EphemeralMessageId ephemeral_message_id);
+
static uint64 save_delete_topic_history_on_server_log_event(DialogId dialog_id, ForumTopicId forum_topic_id);
static uint64 save_read_all_dialog_mentions_on_server_log_event(DialogId dialog_id);
diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp
index 3a8383bf0..20350179f 100644
--- a/td/telegram/MessagesManager.cpp
+++ b/td/telegram/MessagesManager.cpp
@@ -8370,8 +8370,9 @@ bool MessagesManager::can_revoke_message(DialogId dialog_id, const Message *m) c
if (m == nullptr) {
return true;
}
- if (m->message_id.is_local()) {
- return false;
+ auto dialog_type = dialog_id.get_type();
+ if (m->message_id.is_local() && dialog_type != DialogType::SecretChat) {
+ return m->ephemeral_message_id.is_valid();
}
if (dialog_id == td_->dialog_manager_->get_my_dialog_id()) {
return false;
@@ -8386,7 +8387,7 @@ bool MessagesManager::can_revoke_message(DialogId dialog_id, const Message *m) c
const int32 DEFAULT_REVOKE_TIME_LIMIT = td_->auth_manager_->is_bot() ? 2 * 86400 : std::numeric_limits<int32>::max();
auto content_type = m->content->get_type();
- switch (dialog_id.get_type()) {
+ switch (dialog_type) {
case DialogType::User: {
bool can_revoke_incoming = td_->option_manager_->get_option_boolean("revoke_pm_inbox", true);
int64 revoke_time_limit =
@@ -8435,8 +8436,8 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector<MessageId
vector<MessageId> message_ids;
message_ids.reserve(input_message_ids.size());
vector<MessageId> deleted_server_message_ids;
-
vector<MessageId> deleted_scheduled_server_message_ids;
+ vector<std::pair<DialogId, EphemeralMessageId>> deleted_ephemeral_message_ids;
for (auto message_id : input_message_ids) {
if (!message_id.is_valid() && !message_id.is_valid_scheduled()) {
return promise.set_error(400, "Invalid message identifier");
@@ -8450,6 +8451,10 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector<MessageId
if (m->message_id.is_scheduled_server()) {
deleted_scheduled_server_message_ids.push_back(m->message_id);
}
+ } else if (is_ephemeral_message(m)) {
+ if (m->ephemeral_message_id.is_valid() && m->receiver_user_id.is_valid()) {
+ deleted_ephemeral_message_ids.emplace_back(DialogId(m->receiver_user_id), m->ephemeral_message_id);
+ }
} else {
if (m->message_id.is_server() || is_secret) {
deleted_server_message_ids.push_back(m->message_id);
@@ -8464,7 +8469,7 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector<MessageId
if (!can_delete_message(dialog_id, m)) {
return promise.set_error(400, "Message can't be deleted");
}
- if (is_bot && !message_id.is_scheduled() && message_id.is_server() && !can_revoke_message(dialog_id, m)) {
+ if (is_bot && message_id.is_server() && !can_revoke_message(dialog_id, m)) {
return promise.set_error(400, "Message can't be deleted for everyone");
}
}
@@ -8477,6 +8482,10 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector<MessageId
mpas.get_promise());
td_->message_query_manager_->delete_scheduled_messages_on_server(
dialog_id, std::move(deleted_scheduled_server_message_ids), 0, mpas.get_promise());
+ for (auto &it : deleted_ephemeral_message_ids) {
+ td_->message_query_manager_->delete_ephemeral_message_on_server(dialog_id, it.first, it.second, 0,
+ mpas.get_promise());
+ }
lock.set_value(Unit());
do_delete_dialog_messages(d, message_ids, false, DELETE_MESSAGE_USER_REQUEST_SOURCE);
diff --git a/td/telegram/TdDb.cpp b/td/telegram/TdDb.cpp
index 839f627b0..c49ad458b 100644
--- a/td/telegram/TdDb.cpp
+++ b/td/telegram/TdDb.cpp
@@ -114,6 +114,7 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue<Binlog> &binlog_p
case LogEvent::HandlerType::ReadMessageContentsOnServer:
case LogEvent::HandlerType::UnpinAllDialogMessagesOnServer:
case LogEvent::HandlerType::ReadAllPollVotesOnServer:
+ case LogEvent::HandlerType::DeleteEphemeralMessageOnServer:
events.to_message_query_manager.push_back(event.clone());
break;
case LogEvent::HandlerType::SendMessage:
diff --git a/td/telegram/logevent/LogEvent.h b/td/telegram/logevent/LogEvent.h
index 2afe70aad..ebb53adc3 100644
--- a/td/telegram/logevent/LogEvent.h
+++ b/td/telegram/logevent/LogEvent.h
@@ -106,6 +106,7 @@ class LogEvent {
SendQuickReplyShortcutMessages = 0x128,
UpdateReactionNotificationSettingsOnServer = 0x129,
ReadAllPollVotesOnServer = 0x12a,
+ DeleteEphemeralMessageOnServer = 0x12b,
GetChannelDifference = 0x140,
AddMessagePushNotification = 0x200,
EditMessagePushNotification = 0x201,