From b929e2a7b2b0d36bc23f64c682000a200394fac6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Jun 2025 13:55:20 +0400 Subject: [PATCH] Update API scheme on layer 160, show correct warnings. --- Telegram/Resources/langs/lang.strings | 5 +++ .../SourceFiles/boxes/delete_messages_box.cpp | 33 +++++++++++----- .../SourceFiles/boxes/delete_messages_box.h | 4 +- Telegram/SourceFiles/data/data_types.h | 3 +- .../export/data/export_data_types.cpp | 8 ++++ .../export/data/export_data_types.h | 12 +++++- .../export/output/export_output_html.cpp | 9 +++++ .../export/output/export_output_json.cpp | 10 +++++ Telegram/SourceFiles/history/history_item.cpp | 38 ++++++++++++++++--- Telegram/SourceFiles/history/history_item.h | 8 +++- .../history/history_item_components.h | 13 +++++++ .../history/history_item_helpers.cpp | 6 ++- Telegram/SourceFiles/mtproto/scheme/api.tl | 4 +- 13 files changed, 132 insertions(+), 21 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 59438de715..3ff6069205 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4472,6 +4472,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_suggest_decline_title" = "Decline"; "lng_suggest_decline_text" = "Do you want to decline publishing this post from {from}?"; "lng_suggest_decline_reason" = "Add a reason (optional)"; +"lng_suggest_warn_title_stars" = "Stars will be lost"; +"lng_suggest_warn_title_ton" = "TON will be lost"; +"lng_suggest_warn_text_stars" = "You won't receive **Stars** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published."; +"lng_suggest_warn_text_ton" = "You won't receive **TON** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published."; +"lng_suggest_warn_delete_anyway" = "Delete Anyway"; "lng_reply_in_another_title" = "Reply in..."; "lng_reply_in_another_chat" = "Reply in Another Chat"; diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.cpp b/Telegram/SourceFiles/boxes/delete_messages_box.cpp index cee747e797..885ce1d2b6 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/delete_messages_box.cpp @@ -499,23 +499,32 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) { } } -bool DeleteMessagesBox::hasPaidSuggestedPosts() const { +PaidPostType DeleteMessagesBox::paidPostType() const { + auto result = PaidPostType::None; const auto now = base::unixtime::now(); for (const auto &id : _ids) { if (const auto item = _session->data().message(id)) { - if (item->isPaidSuggestedPost()) { + const auto type = item->paidType(); + if (type != PaidPostType::None) { const auto date = item->date(); if (now < date || now - date <= kPaidShowLive) { - return true; + if (type == PaidPostType::Ton) { + return type; + } else if (type == PaidPostType::Stars) { + result = type; + } } } } } - return false; + return result; } void DeleteMessagesBox::deleteAndClear() { - if (hasPaidSuggestedPosts() && !_confirmedDeletePaidSuggestedPosts) { + const auto warnPaidType = _confirmedDeletePaidSuggestedPosts + ? PaidPostType::None + : paidPostType(); + if (warnPaidType != PaidPostType::None) { const auto weak = Ui::MakeWeak(this); const auto callback = [=](Fn close) { close(); @@ -524,13 +533,19 @@ void DeleteMessagesBox::deleteAndClear() { strong->deleteAndClear(); } }; - AssertIsDebug(); + const auto ton = (warnPaidType == PaidPostType::Ton); uiShow()->show(Ui::MakeConfirmBox({ - .text = u"You won't receive Stars for this post if you delete it now. The post must remain visible for at least 24 hours after it was published."_q, + .text = (ton + ? tr::lng_suggest_warn_text_ton + : tr::lng_suggest_warn_text_stars)( + tr::now, + Ui::Text::RichLangValue), .confirmed = callback, - .confirmText = u"Delete Anyway"_q, + .confirmText = tr::lng_suggest_warn_delete_anyway(tr::now), .confirmStyle = &st::attentionBoxButton, - .title = u"Stars will be lost"_q, + .title = (ton + ? tr::lng_suggest_warn_title_ton + : tr::lng_suggest_warn_title_stars)(tr::now), })); return; } diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.h b/Telegram/SourceFiles/boxes/delete_messages_box.h index ce5d430c8b..3762212b0d 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.h +++ b/Telegram/SourceFiles/boxes/delete_messages_box.h @@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/box_content.h" +enum class PaidPostType : uchar; + namespace Main { class Session; } // namespace Main @@ -58,7 +60,7 @@ private: [[nodiscard]] bool hasScheduledMessages() const; [[nodiscard]] std::optional revokeText( not_null peer) const; - [[nodiscard]] bool hasPaidSuggestedPosts() const; + [[nodiscard]] PaidPostType paidPostType() const; const not_null _session; diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 46da299a9b..d0dce0f1ea 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -354,7 +354,8 @@ enum class MessageFlag : uint64 { HideDisplayDate = (1ULL << 51), - PaidSuggestedPost = (1ULL << 52), + StarsPaidSuggested = (1ULL << 52), + TonPaidSuggested = (1ULL << 53), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags; diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 8b7cf589c1..e4ef4a0a6f 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -1775,6 +1775,14 @@ ServiceAction ParseServiceAction( .rejected = data.is_rejected(), .balanceTooLow = data.is_balance_too_low(), }; + }, [&](const MTPDmessageActionSuggestedPostSuccess &data) { + result.content = ActionSuggestedPostSuccess{ + .price = CreditsAmountFromTL(data.vprice()), + }; + }, [&](const MTPDmessageActionSuggestedPostRefund &data) { + result.content = ActionSuggestedPostRefund{ + .payerInitiated = data.is_payer_initiated(), + }; }, [&](const MTPDmessageActionConferenceCall &data) { auto content = ActionPhoneCall(); using State = ActionPhoneCall::State; diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 805e2d7682..2c650871a3 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -707,6 +707,14 @@ struct ActionSuggestedPostApproval { bool balanceTooLow = false; }; +struct ActionSuggestedPostSuccess { + CreditsAmount price; +}; + +struct ActionSuggestedPostRefund { + bool payerInitiated = false; +}; + struct ServiceAction { std::variant< v::null_t, @@ -757,7 +765,9 @@ struct ServiceAction { ActionPaidMessagesPrice, ActionTodoCompletions, ActionTodoAppendTasks, - ActionSuggestedPostApproval> content; + ActionSuggestedPostApproval, + ActionSuggestedPostSuccess, + ActionSuggestedPostRefund> content; }; ServiceAction ParseServiceAction( diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 5ada50c0bb..11b0e4913b 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -1466,6 +1466,15 @@ auto HtmlWriter::Wrap::pushMessage( : (", with comment: "" + SerializeString(data.rejectComment) + """)); + }, [&](const ActionSuggestedPostSuccess &data) { + return "The paid post was shown for 24 hours and " + + QString::number(data.price.value()).toUtf8() + + (data.price.ton() ? " TON" : " stars") + + " were transferred to the channel."; + }, [&](const ActionSuggestedPostRefund &data) { + return QByteArray() + (data.payerInitiated + ? "The user refunded the payment, post was deleted." + : "The admin deleted the post early, the payment was refunded."); }, [](v::null_t) { return QByteArray(); }); if (!serviceText.isEmpty()) { diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index b1317a9103..b591bfb3dd 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -725,6 +725,16 @@ QByteArray SerializeMessage( push("price_currency", data.price.ton() ? "TON" : "Stars"); push("scheduled_date", data.scheduleDate); } + }, [&](const ActionSuggestedPostSuccess &data) { + pushActor(); + pushAction("suggested_post_success"); + push("price_amount_whole", NumberToString(data.price.whole())); + push("price_amount_nano", NumberToString(data.price.nano())); + push("price_currency", data.price.ton() ? "TON" : "Stars"); + }, [&](const ActionSuggestedPostRefund &data) { + pushActor(); + pushAction("suggested_post_refund"); + push("user_initiated", data.payerInitiated); }, [](v::null_t) {}); if (v::is_null(message.action.content)) { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 311f5c3d4c..d3800408c7 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -830,6 +830,8 @@ HistoryServiceDependentData *HistoryItem::GetServiceDependentData() { return append; } else if (const auto decision = Get()) { return decision; + } else if (const auto finish = Get()) { + return finish; } return nullptr; } @@ -1645,8 +1647,12 @@ bool HistoryItem::isEditingMedia() const { return Has(); } -bool HistoryItem::isPaidSuggestedPost() const { - return _flags & MessageFlag::PaidSuggestedPost; +PaidPostType HistoryItem::paidType() const { + return (_flags & MessageFlag::StarsPaidSuggested) + ? PaidPostType::Stars + : (_flags & MessageFlag::TonPaidSuggested) + ? PaidPostType::Ton + : PaidPostType::None; } void HistoryItem::clearSavedMedia() { @@ -2474,7 +2480,7 @@ bool HistoryItem::allowsSendNow() const { && !isSending() && !hasFailed() && !isEditingMedia() - && !isPaidSuggestedPost(); + && (paidType() == PaidPostType::None); } bool HistoryItem::allowsReschedule() const { @@ -2502,7 +2508,7 @@ bool HistoryItem::allowsEdit(TimeId now) const { && (!_media || _media->allowsEdit()) && !isLegacyMessage() && !isEditingMedia() - && !isPaidSuggestedPost(); + && (paidType() == PaidPostType::None); } bool HistoryItem::allowsEditMedia() const { @@ -4673,6 +4679,18 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) { decision->rejected = data.is_rejected(); decision->rejectComment = qs(data.vreject_comment().value_or_empty()); decision->date = data.vschedule_date().value_or_empty(); + } else if (type == mtpc_messageActionSuggestedPostSuccess) { + const auto &data = action.c_messageActionSuggestedPostSuccess(); + UpdateComponents(HistoryServiceSuggestFinish::Bit()); + const auto finish = Get(); + finish->successPrice = CreditsAmountFromTL(data.vprice()); + } else if (type == mtpc_messageActionSuggestedPostRefund) { + const auto &data = action.c_messageActionSuggestedPostRefund(); + UpdateComponents(HistoryServiceSuggestFinish::Bit()); + const auto finish = Get(); + finish->refundType = data.is_payer_initiated() + ? SuggestRefundType::User + : SuggestRefundType::Admin; } if (const auto replyTo = message.vreply_to()) { replyTo->match([&](const MTPDmessageReplyHeader &data) { @@ -6058,7 +6076,15 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { }; auto prepareSuggestedPostApproval = [&](const MTPDmessageActionSuggestedPostApproval &data) { - return PreparedServiceText{ { u"hello"_q } }; + return PreparedServiceText{ { tr::lng_suggest_action_agreement(tr::now) } }; + }; + + auto prepareSuggestedPostSuccess = [&](const MTPDmessageActionSuggestedPostSuccess &data) { + return PreparedServiceText{ { u"hello"_q } }; AssertIsDebug(); + }; + + auto prepareSuggestedPostRefund = [&](const MTPDmessageActionSuggestedPostRefund &data) { + return PreparedServiceText{ { u"hello"_q } }; AssertIsDebug(); }; auto prepareConferenceCall = [&](const MTPDmessageActionConferenceCall &) -> PreparedServiceText { @@ -6119,6 +6145,8 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { prepareTodoCompletions, prepareTodoAppendTasks, prepareSuggestedPostApproval, + prepareSuggestedPostSuccess, + prepareSuggestedPostRefund, PrepareEmptyText, PrepareErrorText)); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 62bb3b6e94..7126716fd5 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -95,6 +95,12 @@ enum class HistoryReactionSource : char { Existing, }; +enum class PaidPostType : uchar { + None, + Stars, + Ton, +}; + class HistoryItem final : public RuntimeComposer { public: [[nodiscard]] static std::unique_ptr CreateMedia( @@ -314,7 +320,6 @@ public: [[nodiscard]] bool hasRealFromId() const; [[nodiscard]] bool isPostHidingAuthor() const; [[nodiscard]] bool isPostShowingAuthor() const; - [[nodiscard]] bool isPaidSuggestedPost() const; [[nodiscard]] bool isRegular() const; [[nodiscard]] bool isUploading() const; void sendFailed(); @@ -325,6 +330,7 @@ public: [[nodiscard]] bool hasUnpaidContent() const; [[nodiscard]] bool inHighlightProcess() const; void highlightProcessDone(); + [[nodiscard]] PaidPostType paidType() const; void setCommentsInboxReadTill(MsgId readTillId); void setCommentsMaxId(MsgId maxId); diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index f934726c64..4ca58bde84 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -708,6 +708,19 @@ struct HistoryServiceSuggestDecision bool balanceTooLow = false; }; +enum class SuggestRefundType { + None, + User, + Admin, +}; + +struct HistoryServiceSuggestFinish +: RuntimeComponent +, HistoryServiceDependentData { + CreditsAmount successPrice; + SuggestRefundType refundType = SuggestRefundType::None; +}; + struct HistoryServiceGameScore : RuntimeComponent , HistoryServiceDependentData { diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 3297854a55..81f52890cb 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -763,8 +763,10 @@ MessageFlags FlagsFromMTP( | ((flags & MTP::f_video_processing_pending) ? Flag::EstimatedDate : Flag()) - | ((flags & MTP::f_paid_suggested_post) - ? Flag::PaidSuggestedPost + | ((flags & MTP::f_paid_suggested_post_ton) + ? Flag::TonPaidSuggested + : (flags & MTP::f_paid_suggested_post_stars) + ? Flag::StarsPaidSuggested : Flag()); } diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index 54f9cd1816..2a8ccb6b4b 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -117,7 +117,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto; messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; -message#9815cec8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post:flags2.8?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost = Message; +message#9815cec8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post_stars:flags2.8?true paid_suggested_post_ton:flags2.9?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost = Message; messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message; messageMediaEmpty#3ded6320 = MessageMedia; @@ -193,6 +193,8 @@ messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1? messageActionTodoCompletions#cc7c5c89 completed:Vector incompleted:Vector = MessageAction; messageActionTodoAppendTasks#c7edbc83 list:Vector = MessageAction; messageActionSuggestedPostApproval#ee7a1596 flags:# rejected:flags.0?true balance_too_low:flags.1?true reject_comment:flags.2?string schedule_date:flags.3?int price:flags.4?StarsAmount = MessageAction; +messageActionSuggestedPostSuccess#95ddcf69 price:StarsAmount = MessageAction; +messageActionSuggestedPostRefund#69f916f8 flags:# payer_initiated:flags.0?true = MessageAction; messageActionGiftTon#a8a3c699 flags:# currency:string amount:long crypto_currency:string crypto_amount:long transaction_id:flags.0?string = MessageAction; dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;