From 6a6e355af4a4f6ebcc7fbfdeb8a4ee51ba4fe1ea Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Feb 2021 20:38:30 +0400 Subject: [PATCH] Handle ttl_period locally. --- Telegram/SourceFiles/api/api_updates.cpp | 18 +-- .../boxes/peers/edit_peer_info_box.cpp | 117 +++++++++++++++++- Telegram/SourceFiles/data/data_channel.cpp | 3 + Telegram/SourceFiles/data/data_chat.cpp | 8 ++ Telegram/SourceFiles/data/data_chat.h | 21 ++-- Telegram/SourceFiles/data/data_peer.cpp | 29 +++++ Telegram/SourceFiles/data/data_peer.h | 34 +++-- Telegram/SourceFiles/data/data_session.cpp | 28 +---- Telegram/SourceFiles/data/data_user.cpp | 3 + Telegram/SourceFiles/history/history_item.cpp | 51 +++++++- Telegram/SourceFiles/history/history_item.h | 6 + .../SourceFiles/history/history_message.cpp | 27 +++- .../SourceFiles/history/history_message.h | 6 + 13 files changed, 282 insertions(+), 69 deletions(-) diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index f7012632e..d4a950d4b 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -1331,14 +1331,7 @@ void Updates::applyUpdates( item->id, ApiWrap::RequestMessageDataCallback()); } - item->updateSentContent({ - sent.text, - Api::EntitiesFromMTP(&session(), list.value_or_empty()) - }, d.vmedia()); - item->contributeToSlowmode(d.vdate().v); - if (!wasAlready) { - item->indexAsNewItem(); - } + item->applySentMessage(sent.text, d, wasAlready); } } @@ -1829,7 +1822,14 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updatePeerHistoryTTL: { const auto &d = update.c_updatePeerHistoryTTL(); - // #TODO ttl + const auto peerId = peerFromMTP(d.vpeer()); + if (const auto peer = session().data().peerLoaded(peerId)) { + if (const auto ttl = d.vttl()) { + peer->applyMessagesTTL(*ttl); + } else { + peer->setMessagesTTL(0, 0, false); + } + } } break; case mtpc_updateNewEncryptedMessage: { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index de3b9e09d..44c8ab463 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "boxes/add_contact_box.h" #include "boxes/confirm_box.h" +#include "boxes/single_choice_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_type_box.h" @@ -307,13 +308,14 @@ private: Ui::UserpicButton *photo = nullptr; rpl::lifetime initialPhotoImageWaiting; Ui::VerticalLayout *buttonsLayout = nullptr; - Ui::SlideWrap *historyVisibilityWrap = nullptr; + Ui::SlideWrap<> *historyVisibilityWrap = nullptr; }; struct Saving { std::optional username; std::optional title; std::optional description; std::optional hiddenPreHistory; + std::optional messagesTTL; std::optional signatures; std::optional linkedChat; }; @@ -335,6 +337,7 @@ private: //void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); + void fillSetMessagesTTLButton(); void fillManageSection(); void submitTitle(); @@ -348,6 +351,7 @@ private: bool validateTitle(Saving &to) const; bool validateDescription(Saving &to) const; bool validateHistoryVisibility(Saving &to) const; + bool validateMessagesTTL(Saving &to) const; bool validateSignatures(Saving &to) const; void save(); @@ -356,6 +360,7 @@ private: void saveTitle(); void saveDescription(); void saveHistoryVisibility(); + void saveMessagesTTL(); void saveSignatures(); void savePhoto(); void pushSaveStage(FnMut &&lambda); @@ -372,6 +377,7 @@ private: void migrate(not_null channel); std::optional _privacySavedValue; + std::optional _ttlSavedValue; std::optional _linkedChatSavedValue; ChannelData *_linkedChatOriginalValue = nullptr; bool _channelHasLocationOriginalValue = false; @@ -637,7 +643,7 @@ void Controller::refreshHistoryVisibility(anim::type animated) { && !_channelHasLocationOriginalValue && (!_linkedChatSavedValue || !*_linkedChatSavedValue)), animated); -}; +} void Controller::showEditPeerTypeBox( std::optional> error) { @@ -880,6 +886,68 @@ void Controller::fillHistoryVisibilityButton() { refreshHistoryVisibility(anim::type::instant); } +void Controller::fillSetMessagesTTLButton() { + Expects(_controls.buttonsLayout != nullptr); + + _ttlSavedValue = _peer->messagesTTL(); + + const auto updateMessagesTTL = + std::make_shared>(); + + const auto boxCallback = crl::guard(this, [=](TimeId value) { + updateMessagesTTL->fire_copy(value); + _ttlSavedValue = value; + }); + const auto buttonCallback = [=] { + Ui::show(Box([=](not_null box) { + const auto options = { + tr::lng_manage_messages_ttl_never(tr::now), + tr::lng_manage_messages_ttl_after1(tr::now), + tr::lng_manage_messages_ttl_after2(tr::now), + u"5 seconds"_q, AssertIsDebug() + }; + const auto initial = !*_ttlSavedValue + ? 0 + : (*_ttlSavedValue == 5) AssertIsDebug() + ? 3 AssertIsDebug() + : (*_ttlSavedValue < 3 * 86400) + ? 1 + : 2; + const auto callback = [=](int option) { + boxCallback(!option + ? 0 + : (option == 1) + ? 86400 + : (option == 3) AssertIsDebug() + ? 5 AssertIsDebug() + : 7 * 86400); + }; + SingleChoiceBox(box, { + .title = tr::lng_manage_messages_ttl_title(), + .options = options, + .initialSelection = initial, + .callback = callback, + }); + }), Ui::LayerOption::KeepOther); + }; + AddButtonWithText( + _controls.buttonsLayout, + tr::lng_manage_messages_ttl_title(), + updateMessagesTTL->events( + ) | rpl::map([](TimeId value) { + return !value + ? tr::lng_manage_messages_ttl_never() + : (value == 5) AssertIsDebug() + ? rpl::single("5 seconds") AssertIsDebug() + : (value < 3 * 86400) + ? tr::lng_manage_messages_ttl_after1() + : tr::lng_manage_messages_ttl_after2(); + }) | rpl::flatten_latest(), + buttonCallback); + + updateMessagesTTL->fire_copy(*_ttlSavedValue); +} + void Controller::fillManageSection() { Expects(_controls.buttonsLayout != nullptr); @@ -903,6 +971,11 @@ void Controller::fillManageSection() { ? channel->canEditPreHistoryHidden() : chat->canEditPreHistoryHidden(); }(); + const auto canSetMessagesTTL = [&] { + return isChannel + ? channel->canDeleteMessages() + : chat->canDeleteMessages(); + }(); const auto canEditPermissions = [&] { return isChannel @@ -965,17 +1038,21 @@ void Controller::fillManageSection() { if (canViewOrEditLinkedChat) { fillLinkedChatButton(); } - if (canEditSignatures) { - fillSignaturesButton(); - } if (canEditPreHistoryHidden) { fillHistoryVisibilityButton(); } + if (canSetMessagesTTL) { + fillSetMessagesTTLButton(); + } + if (canEditSignatures) { + fillSignaturesButton(); + } if (canEditPreHistoryHidden || canEditSignatures || canEditInviteLinks || canViewOrEditLinkedChat - || canEditUsername) { + || canEditUsername + || canSetMessagesTTL) { AddSkip( _controls.buttonsLayout, st::editPeerTopButtonsLayoutSkip, @@ -1132,6 +1209,7 @@ std::optional Controller::validate() const { && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) + && validateMessagesTTL(result) && validateSignatures(result)) { return result; } @@ -1199,6 +1277,14 @@ bool Controller::validateHistoryVisibility(Saving &to) const { return true; } +bool Controller::validateMessagesTTL(Saving &to) const { + if (!_ttlSavedValue) { + return true; + } + to.messagesTTL = _ttlSavedValue; + return true; +} + bool Controller::validateSignatures(Saving &to) const { if (!_signaturesSavedValue.has_value()) { return true; @@ -1220,6 +1306,7 @@ void Controller::save() { pushSaveStage([=] { saveTitle(); }); pushSaveStage([=] { saveDescription(); }); pushSaveStage([=] { saveHistoryVisibility(); }); + pushSaveStage([=] { saveMessagesTTL(); }); pushSaveStage([=] { saveSignatures(); }); pushSaveStage([=] { savePhoto(); }); continueSave(); @@ -1427,6 +1514,24 @@ void Controller::saveHistoryVisibility() { [=] { cancelSave(); }); } +void Controller::saveMessagesTTL() { + if (!_savingData.messagesTTL + || *_savingData.messagesTTL == _peer->messagesTTL()) { + return continueSave(); + } + using Flag = MTPmessages_SetHistoryTTL::Flag; + _api.request(MTPmessages_SetHistoryTTL( + MTP_flags(_peer->oneSideTTL() ? Flag::f_pm_oneside : Flag(0)), + _peer->input, + MTP_int(*_savingData.messagesTTL) + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + continueSave(); + }).fail([=](const RPCError &error) { + cancelSave(); + }).send(); +} + void Controller::togglePreHistoryHidden( not_null channel, bool hidden, diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 72a9e7788..051e78c9c 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -749,6 +749,9 @@ void ApplyChannelUpdate( channel->clearGroupCall(); } + if (const auto ttl = update.vttl()) { + channel->applyMessagesTTL(*ttl); + } channel->setFullFlags(update.vflags().v); channel->setUserpicPhoto(update.vchat_photo()); if (const auto migratedFrom = update.vmigrated_from_chat_id()) { diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index d554877ae..bb1385b62 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -84,6 +84,11 @@ bool ChatData::canEditPreHistoryHidden() const { return amCreator(); } +bool ChatData::canDeleteMessages() const { + return amCreator() + || (adminRights() & AdminRight::f_delete_messages); +} + bool ChatData::canAddMembers() const { return amIn() && !amRestricted(Restriction::f_invite_users); } @@ -372,6 +377,9 @@ void ApplyChatUpdate(not_null chat, const MTPDchatFull &update) { chat->clearGroupCall(); } + if (const auto ttl = update.vttl()) { + chat->applyMessagesTTL(*ttl); + } if (const auto info = update.vbot_info()) { for (const auto &item : info->v) { item.match([&](const MTPDbotInfo &data) { diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index d1c5d4343..b6ef3e5a0 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -128,16 +128,17 @@ public: [[nodiscard]] AdminRights defaultAdminRights(not_null user); // Like in ChannelData. - bool canWrite() const; - bool canEditInformation() const; - bool canEditPermissions() const; - bool canEditUsername() const; - bool canEditPreHistoryHidden() const; - bool canAddMembers() const; - bool canAddAdmins() const; - bool canBanMembers() const; - bool canSendPolls() const; - bool anyoneCanAddMembers() const; + [[nodiscard]] bool canWrite() const; + [[nodiscard]] bool canEditInformation() const; + [[nodiscard]] bool canEditPermissions() const; + [[nodiscard]] bool canEditUsername() const; + [[nodiscard]] bool canEditPreHistoryHidden() const; + [[nodiscard]] bool canDeleteMessages() const; + [[nodiscard]] bool canAddMembers() const; + [[nodiscard]] bool canAddAdmins() const; + [[nodiscard]] bool canBanMembers() const; + [[nodiscard]] bool canSendPolls() const; + [[nodiscard]] bool anyoneCanAddMembers() const; void applyEditAdmin(not_null user, bool isAdmin); diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 5d36a346c..52c2c750f 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -945,6 +945,35 @@ void PeerData::setLoadedStatus(LoadedStatus status) { _loadedStatus = status; } +TimeId PeerData::messagesTTL() const { + return (_ttlMyPeriod && _ttlPeerPeriod) + ? std::min(_ttlMyPeriod, _ttlPeerPeriod) + : std::max(_ttlMyPeriod, _ttlPeerPeriod); +} + +void PeerData::setMessagesTTL( + TimeId myPeriod, + TimeId peerPeriod, + bool oneSide) { + _ttlMyPeriod = myPeriod; + _ttlPeerPeriod = peerPeriod; + _ttlOneSide = oneSide; +} + +void PeerData::applyMessagesTTL(const MTPPeerHistoryTTL &ttl) { + ttl.match([&](const MTPDpeerHistoryTTL &data) { + setMessagesTTL( + data.vttl_period().v, + data.vttl_period().v, + false); + }, [&](const MTPDpeerHistoryTTLPM &data) { + setMessagesTTL( + data.vmy_ttl_period().value_or_empty(), + data.vpeer_ttl_period().value_or_empty(), + data.is_my_oneside()); + }); +} + namespace Data { std::vector ListOfRestrictions() { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 72b4593ee..26025bc11 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -17,6 +17,11 @@ class UserData; class ChatData; class ChannelData; +using ChatAdminRight = MTPDchatAdminRights::Flag; +using ChatRestriction = MTPDchatBannedRights::Flag; +using ChatAdminRights = MTPDchatAdminRights::Flags; +using ChatRestrictions = MTPDchatBannedRights::Flags; + namespace Ui { class EmptyUserpic; } // namespace Ui @@ -30,23 +35,13 @@ namespace Data { class Session; class GroupCall; +class CloudImageView; int PeerColorIndex(PeerId peerId); int PeerColorIndex(int32 bareId); style::color PeerUserpicColor(PeerId peerId); PeerId FakePeerIdForJustName(const QString &name); -} // namespace Data - -using ChatAdminRight = MTPDchatAdminRights::Flag; -using ChatRestriction = MTPDchatBannedRights::Flag; -using ChatAdminRights = MTPDchatAdminRights::Flags; -using ChatRestrictions = MTPDchatBannedRights::Flags; - -namespace Data { - -class CloudImageView; - class RestrictionCheckResult { public: [[nodiscard]] static RestrictionCheckResult Allowed() { @@ -386,6 +381,19 @@ public: } void setLoadedStatus(LoadedStatus status); + [[nodiscard]] TimeId myMessagesTTL() const { + return _ttlMyPeriod; + } + [[nodiscard]] TimeId peerMessagesTTL() const { + return _ttlPeerPeriod; + } + [[nodiscard]] bool oneSideTTL() const { + return _ttlOneSide; + } + [[nodiscard]] TimeId messagesTTL() const; + void setMessagesTTL(TimeId myPeriod, TimeId peerPeriod, bool oneSide); + void applyMessagesTTL(const MTPPeerHistoryTTL &ttl); + [[nodiscard]] Data::GroupCall *groupCall() const; const PeerId id; @@ -429,6 +437,10 @@ private: base::flat_set _nameFirstLetters; crl::time _lastFullUpdate = 0; + + TimeId _ttlMyPeriod = 0; + TimeId _ttlPeerPeriod = 0; + bool _ttlOneSide = false; bool _hasPinnedMessages = false; Settings _settings = { kSettingsUnknown }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index e7d83e8a7..e3843b692 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1846,33 +1846,7 @@ bool Session::checkEntitiesAndViewsUpdate(const MTPDmessage &data) { if (!existing) { return false; } - existing->updateSentContent({ - qs(data.vmessage()), - Api::EntitiesFromMTP( - &session(), - data.ventities().value_or_empty()) - }, data.vmedia()); - existing->updateReplyMarkup(data.vreply_markup()); - existing->updateForwardedInfo(data.vfwd_from()); - existing->setViewsCount(data.vviews().value_or(-1)); - if (const auto replies = data.vreplies()) { - existing->setReplies(*replies); - } else { - existing->clearReplies(); - } - existing->setForwardsCount(data.vforwards().value_or(-1)); - if (const auto reply = data.vreply_to()) { - reply->match([&](const MTPDmessageReplyHeader &data) { - existing->setReplyToTop( - data.vreply_to_top_id().value_or( - data.vreply_to_msg_id().v)); - }); - } - existing->setPostAuthor(data.vpost_author().value_or_empty()); - existing->indexAsNewItem(); - existing->contributeToSlowmode(data.vdate().v); - requestItemTextRefresh(existing); - updateDependentMessages(existing); + existing->applySentMessage(data); const auto result = (existing->mainView() != nullptr); if (result) { stickers().checkSavedGif(existing); diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 5a865fde1..86114b894 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -256,6 +256,9 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { MTP_inputNotifyPeer(user->input), update.vnotify_settings()); + if (const auto ttl = update.vttl()) { + user->applyMessagesTTL(*ttl); + } if (const auto info = update.vbot_info()) { user->setBotInfo(*info); } else { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index f5857ca09..5d887c9dc 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "core/crash_reports.h" #include "base/unixtime.h" +#include "api/api_text_entities.h" #include "data/data_scheduled_messages.h" // kScheduledUntilOnlineTimestamp #include "data/data_changes.h" #include "data/data_session.h" @@ -490,6 +491,52 @@ void HistoryItem::applyEditionToHistoryCleared() { ).c_messageService()); } +void HistoryItem::applySentMessage(const MTPDmessage &data) { + updateSentContent({ + qs(data.vmessage()), + Api::EntitiesFromMTP( + &history()->session(), + data.ventities().value_or_empty()) + }, data.vmedia()); + updateReplyMarkup(data.vreply_markup()); + updateForwardedInfo(data.vfwd_from()); + setViewsCount(data.vviews().value_or(-1)); + if (const auto replies = data.vreplies()) { + setReplies(*replies); + } else { + clearReplies(); + } + setForwardsCount(data.vforwards().value_or(-1)); + if (const auto reply = data.vreply_to()) { + reply->match([&](const MTPDmessageReplyHeader &data) { + setReplyToTop( + data.vreply_to_top_id().value_or( + data.vreply_to_msg_id().v)); + }); + } + setPostAuthor(data.vpost_author().value_or_empty()); + contributeToSlowmode(data.vdate().v); + indexAsNewItem(); + history()->owner().requestItemTextRefresh(this); + history()->owner().updateDependentMessages(this); +} + +void HistoryItem::applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready) { + updateSentContent({ + text, + Api::EntitiesFromMTP( + &history()->session(), + data.ventities().value_or_empty()) + }, data.vmedia()); + contributeToSlowmode(data.vdate().v); + if (!wasAlready) { + indexAsNewItem(); + } +} + void HistoryItem::indexAsNewItem() { if (IsServerMsgId(id)) { addToUnreadMentions(UnreadMentionType::New); @@ -629,9 +676,7 @@ bool HistoryItem::canDeleteForEveryone(TimeId now) const { } if (!out()) { if (const auto chat = peer->asChat()) { - if (!chat->amCreator() - && !(chat->adminRights() - & ChatAdminRight::f_delete_messages)) { + if (!chat->canDeleteMessages()) { return false; } } else if (peer->isUser()) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index de50cd2f2..778d08165 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -262,6 +262,12 @@ public: } [[nodiscard]] virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0; + virtual void applySentMessage(const MTPDmessage &data); + virtual void applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready); + void indexAsNewItem(); [[nodiscard]] virtual QString notificationHeader() const { diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 36f557505..1a0b7e555 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "base/openssl_help.h" +#include "base/unixtime.h" #include "lang/lang_keys.h" #include "mainwidget.h" #include "mainwindow.h" @@ -50,9 +51,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat.h" #include "styles/style_window.h" -#include "base/call_delayed.h" // #TODO ttl -#include "base/unixtime.h" - #include #include @@ -976,6 +974,29 @@ bool HistoryMessage::updateDependencyItem() { return true; } +void HistoryMessage::applySentMessage(const MTPDmessage &data) { + HistoryItem::applySentMessage(data); + + if (const auto period = data.vttl_period(); period && period->v > 0) { + applyTTL(data.vdate().v + period->v); + } else { + applyTTL(0); + } +} + +void HistoryMessage::applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready) { + HistoryItem::applySentMessage(text, data, wasAlready); + + if (const auto period = data.vttl_period(); period && period->v > 0) { + applyTTL(data.vdate().v + period->v); + } else { + applyTTL(0); + } +} + bool HistoryMessage::allowsForward() const { if (id < 0 || !isHistoryEntry()) { return false; diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 3f8ce8d41..2c832c797 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -188,6 +188,12 @@ public: return replyToId(); } + void applySentMessage(const MTPDmessage &data) override; + void applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready) override; + [[nodiscard]] TimeId ttlDestroyAt() const override { return _ttlDestroyAt; }