From 960cf7a34bfd86b2e5f558458570a433ed54db8d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 18 Feb 2025 19:50:55 +0400 Subject: [PATCH] Update API scheme, new paid. --- Telegram/Resources/langs/lang.strings | 2 +- Telegram/SourceFiles/api/api_common.h | 1 + Telegram/SourceFiles/api/api_polls.cpp | 7 +- Telegram/SourceFiles/api/api_polls.h | 2 +- Telegram/SourceFiles/api/api_sending.cpp | 21 +- Telegram/SourceFiles/apiwrap.cpp | 35 ++- Telegram/SourceFiles/apiwrap.h | 2 +- Telegram/SourceFiles/boxes/share_box.cpp | 7 +- Telegram/SourceFiles/data/data_channel.cpp | 42 ---- Telegram/SourceFiles/data/data_channel.h | 5 - .../data/data_chat_participant_status.cpp | 60 +++++ .../data/data_chat_participant_status.h | 20 ++ Telegram/SourceFiles/data/data_peer.cpp | 37 +-- Telegram/SourceFiles/data/data_peer.h | 5 +- Telegram/SourceFiles/data/data_user.cpp | 32 --- Telegram/SourceFiles/data/data_user.h | 5 - .../history/history_item_helpers.cpp | 49 +++- .../history/history_item_helpers.h | 1 + .../SourceFiles/history/history_widget.cpp | 219 +++++++----------- Telegram/SourceFiles/history/history_widget.h | 13 +- .../media/stories/media_stories_share.cpp | 22 +- Telegram/SourceFiles/mtproto/scheme/api.tl | 5 +- 22 files changed, 302 insertions(+), 290 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 95da224c6..ea11eb467 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3650,6 +3650,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_send_anonymous_ph" = "Send anonymously..."; "lng_story_reply_ph" = "Reply privately..."; "lng_story_comment_ph" = "Comment story..."; +"lng_message_paid_ph" = "Message for {amount}"; "lng_send_text_no" = "Text not allowed."; "lng_send_text_no_about" = "The admins of this group only allow sending {types}."; "lng_send_text_type_and_last" = "{types} and {last}"; @@ -4822,7 +4823,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_slowmode_seconds#one" = "{count} second"; "lng_slowmode_seconds#other" = "{count} seconds"; -"lng_payment_for_message" = "Pay {cost} for 1 Message"; "lng_payment_confirm_title" = "Confirm payment"; "lng_payment_confirm_text#one" = "{name} charges **{count}** Star per message."; "lng_payment_confirm_text#other" = "{name} charges **{count}** Stars per message."; diff --git a/Telegram/SourceFiles/api/api_common.h b/Telegram/SourceFiles/api/api_common.h index 77c30d095..c58f525c9 100644 --- a/Telegram/SourceFiles/api/api_common.h +++ b/Telegram/SourceFiles/api/api_common.h @@ -25,6 +25,7 @@ struct SendOptions { TimeId scheduled = 0; BusinessShortcutId shortcutId = 0; EffectId effectId = 0; + int starsApproved = 0; bool silent = false; bool handleSupportSwitch = false; bool invertCaption = false; diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp index aa03ee94d..398bd1acb 100644 --- a/Telegram/SourceFiles/api/api_polls.cpp +++ b/Telegram/SourceFiles/api/api_polls.cpp @@ -37,7 +37,7 @@ Polls::Polls(not_null api) void Polls::create( const PollData &data, - const SendAction &action, + SendAction action, Fn done, Fn fail) { _session->api().sendAction(action); @@ -59,7 +59,9 @@ void Polls::create( history->startSavingCloudDraft(topicRootId); } const auto silentPost = ShouldSendSilent(peer, action.options); - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + action.options.starsApproved); if (silentPost) { sendFlags |= MTPmessages_SendMedia::Flag::f_silent; } @@ -73,6 +75,7 @@ void Polls::create( sendFlags |= MTPmessages_SendMedia::Flag::f_effect; } if (starsPaid) { + action.options.starsApproved -= starsPaid; sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars; } const auto sendAs = action.options.sendAs; diff --git a/Telegram/SourceFiles/api/api_polls.h b/Telegram/SourceFiles/api/api_polls.h index 2ff08a1ac..f77e34d67 100644 --- a/Telegram/SourceFiles/api/api_polls.h +++ b/Telegram/SourceFiles/api/api_polls.h @@ -27,7 +27,7 @@ public: void create( const PollData &data, - const SendAction &action, + SendAction action, Fn done, Fn fail); void sendVotes( diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 319032aed..83ed31a6d 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -95,8 +95,9 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) { const auto messagePostAuthor = peer->isBroadcast() ? session->user()->name() : QString(); - const auto starsPaid = peer->commitStarsForMessage(); - + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + action.options.starsApproved); if (action.options.scheduled) { flags |= MessageFlag::IsOrWasScheduled; sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; @@ -113,6 +114,7 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) { sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media; } if (starsPaid) { + action.options.starsApproved -= starsPaid; sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars; } @@ -165,7 +167,7 @@ void SendExistingMedia( ? (*localMessageId) : session->data().nextLocalMessageId()); const auto randomId = base::RandomValue(); - const auto &action = message.action; + auto &action = message.action; auto flags = NewMessageFlags(peer); auto sendFlags = MTPmessages_SendMedia::Flags(0); @@ -195,8 +197,9 @@ void SendExistingMedia( sendFlags |= MTPmessages_SendMedia::Flag::f_entities; } const auto captionText = caption.text; - const auto starsPaid = peer->commitStarsForMessage(); - + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + action.options.starsApproved); if (action.options.scheduled) { flags |= MessageFlag::IsOrWasScheduled; sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; @@ -213,6 +216,7 @@ void SendExistingMedia( sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media; } if (starsPaid) { + action.options.starsApproved -= starsPaid; sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars; } @@ -351,7 +355,7 @@ bool SendDice(MessageToSend &message) { message.action.generateLocal = true; - const auto &action = message.action; + auto &action = message.action; api->sendAction(action); const auto newId = FullMsgId( @@ -390,8 +394,11 @@ bool SendDice(MessageToSend &message) { flags |= MessageFlag::InvertMedia; sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media; } - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + action.options.starsApproved); if (starsPaid) { + action.options.starsApproved -= starsPaid; sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars; } diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 71927f5fc..778d55228 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3883,8 +3883,11 @@ void ApiWrap::sendMessage(MessageToSend &&message) { sendFlags |= MTPmessages_SendMessage::Flag::f_effect; mediaFlags |= MTPmessages_SendMedia::Flag::f_effect; } - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + action.options.starsApproved); if (starsPaid) { + action.options.starsApproved -= starsPaid; sendFlags |= MTPmessages_SendMessage::Flag::f_allow_paid_stars; mediaFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars; } @@ -4020,7 +4023,7 @@ void ApiWrap::sendBotStart( void ApiWrap::sendInlineResult( not_null bot, not_null data, - const SendAction &action, + SendAction action, std::optional localMessageId, Fn done) { sendAction(action); @@ -4060,8 +4063,11 @@ void ApiWrap::sendInlineResult( if (action.options.hideViaBot) { sendFlags |= SendFlag::f_hide_via; } - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + action.options.starsApproved); if (starsPaid) { + action.options.starsApproved -= starsPaid; sendFlags |= SendFlag::f_allow_paid_stars; } @@ -4244,7 +4250,12 @@ void ApiWrap::sendMediaWithRandomId( Api::ConvertOption::SkipLocal); const auto updateRecentStickers = Api::HasAttachedStickers(media); - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + options.starsApproved); + if (starsPaid) { + options.starsApproved -= starsPaid; + } using Flag = MTPmessages_SendMedia::Flag; const auto flags = Flag(0) @@ -4304,7 +4315,7 @@ void ApiWrap::sendMultiPaidMedia( Expects(album->options.price > 0); const auto groupId = album->groupId; - const auto &options = album->options; + auto &options = album->options; const auto randomId = album->items.front().randomId; auto medias = album->items | ranges::view::transform([]( const SendingAlbum::Item &part) { @@ -4322,7 +4333,12 @@ void ApiWrap::sendMultiPaidMedia( _session, caption.entities, Api::ConvertOption::SkipLocal); - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + options.starsApproved); + if (starsPaid) { + options.starsApproved -= starsPaid; + } using Flag = MTPmessages_SendMedia::Flag; const auto flags = Flag(0) @@ -4452,7 +4468,12 @@ void ApiWrap::sendAlbumIfReady(not_null album) { const auto history = sample->history(); const auto replyTo = sample->replyTo(); const auto sendAs = album->options.sendAs; - const auto starsPaid = history->peer->commitStarsForMessage(); + const auto starsPaid = std::min( + history->peer->starsPerMessageChecked(), + album->options.starsApproved); + if (starsPaid) { + album->options.starsApproved -= starsPaid; + } using Flag = MTPmessages_SendMultiMedia::Flag; const auto flags = Flag(0) | (replyTo ? Flag::f_reply_to : Flag(0)) diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index d6e2bbc67..c9e97d0ed 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -368,7 +368,7 @@ public: void sendInlineResult( not_null bot, not_null data, - const SendAction &action, + SendAction action, std::optional localMessageId, Fn done = nullptr); void sendMessageFail( diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index ebf25e08f..3a890a91f 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -1568,7 +1568,12 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback( : topicRootId; const auto peer = thread->peer(); const auto threadHistory = thread->owningHistory(); - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + options.starsApproved); + if (starsPaid) { + options.starsApproved -= starsPaid; + } histories.sendRequest(threadHistory, requestType, [=]( Fn finish) { const auto session = &threadHistory->session(); diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index e4f3dc831..410abacf9 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -880,48 +880,6 @@ void ChannelData::setStarsPerMessage(int stars) { } } -int ChannelData::starsForMessageLocked() const { - if (const auto info = mgInfo.get()) { - return info->_starsForMessageLocked; - } - return 0; -} - -void ChannelData::lockStarsForMessage() { - const auto info = mgInfo.get(); - if (!info || info->_starsForMessageLocked == info->_starsPerMessage) { - return; - } - cancelStarsForMessage(); - if (info->_starsPerMessage) { - info->_starsForMessageLocked = info->_starsPerMessage; - session().credits().lock(StarsAmount(info->_starsPerMessage)); - session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage); - } -} - -int ChannelData::commitStarsForMessage() { - const auto info = mgInfo.get(); - if (!info) { - return 0; - } else if (const auto stars = base::take(info->_starsForMessageLocked)) { - session().credits().withdrawLocked(StarsAmount(stars)); - session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage); - return stars; - } - return 0; -} - -void ChannelData::cancelStarsForMessage() { - const auto info = mgInfo.get(); - if (!info) { - return; - } else if (const auto stars = base::take(info->_starsForMessageLocked)) { - session().credits().unlock(StarsAmount(stars)); - session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage); - } -} - int ChannelData::peerGiftsCount() const { return _peerGiftsCount; } diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 9484f8974..d9e835320 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -153,7 +153,6 @@ private: Data::ChatBotCommands _botCommands; std::unique_ptr _forum; int _starsPerMessage = 0; - int _starsForMessageLocked = 0; friend class ChannelData; @@ -464,10 +463,6 @@ public: void setStarsPerMessage(int stars); [[nodiscard]] int starsPerMessage() const; - [[nodiscard]] int starsForMessageLocked() const; - void lockStarsForMessage(); - [[nodiscard]] int commitStarsForMessage(); - void cancelStarsForMessage(); [[nodiscard]] int peerGiftsCount() const; void setPeerGiftsCount(int count); diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.cpp b/Telegram/SourceFiles/data/data_chat_participant_status.cpp index 10ffb3847..feb4349c5 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.cpp +++ b/Telegram/SourceFiles/data/data_chat_participant_status.cpp @@ -17,10 +17,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "lang/lang_keys.h" #include "main/main_session.h" +#include "storage/storage_account.h" +#include "ui/boxes/confirm_box.h" #include "ui/chat/attach/attach_prepare.h" +#include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" +#include "ui/widgets/checkbox.h" #include "window/window_session_controller.h" +#include "styles/style_widgets.h" namespace { @@ -425,4 +430,59 @@ void ShowSendErrorToast( }); } +void ShowSendPaidConfirm( + not_null navigation, + not_null peer, + SendError error, + Fn confirmed) { + return ShowSendPaidConfirm(navigation->uiShow(), peer, error, confirmed); +} + +void ShowSendPaidConfirm( + std::shared_ptr show, + not_null peer, + Data::SendError error, + Fn confirmed) { + const auto session = &peer->session(); + if (session->local().isPeerTrustedPayForMessage(peer->id)) { + confirmed(); + return; + } + //const auto messages = error.paidMessages; + const auto stars = error.paidStars; + show->showBox(Box([=](not_null box) { + const auto trust = std::make_shared>(); + const auto proceed = [=](Fn close) { + if ((*trust)->checked()) { + session->local().markPeerTrustedPayForMessage(peer->id); + } + confirmed(); + close(); + }; + Ui::ConfirmBox(box, { + .text = tr::lng_payment_confirm_text( + tr::now, + lt_count, + stars, + lt_name, + Ui::Text::Bold(peer->shortName()), + Ui::Text::RichLangValue).append(' ').append( + tr::lng_payment_confirm_sure( + tr::now, + lt_count, + stars, + Ui::Text::RichLangValue)), + .confirmed = proceed, + .confirmText = tr::lng_payment_confirm_button(), + .title = tr::lng_payment_confirm_title(), + }); + const auto skip = st::defaultCheckbox.margin.top(); + *trust = box->addRow( + object_ptr( + box, + tr::lng_payment_confirm_dont_ask(tr::now)), + st::boxRowPadding + QMargins(0, skip, 0, skip)); + })); +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.h b/Telegram/SourceFiles/data/data_chat_participant_status.h index bfa24fb86..912f580fe 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.h +++ b/Telegram/SourceFiles/data/data_chat_participant_status.h @@ -189,17 +189,26 @@ struct SendError { struct Args { QString text; + int paidStars = 0; + int paidMessages = 0; int boostsToLift = 0; + bool resolving = false; bool premiumToLift = false; }; SendError(Args &&args) : text(std::move(args.text)) + , paidStars(args.paidStars) + , paidMessages(args.paidMessages) , boostsToLift(args.boostsToLift) + , resolving(args.resolving) , premiumToLift(args.premiumToLift) { } QString text; + int paidStars = 0; + int paidMessages = 0; int boostsToLift = 0; + bool resolving = false; bool premiumToLift = false; [[nodiscard]] SendError value_or(SendError other) const { @@ -244,4 +253,15 @@ void ShowSendErrorToast( not_null peer, SendError error); +void ShowSendPaidConfirm( + not_null navigation, + not_null peer, + SendError error, + Fn confirmed); +void ShowSendPaidConfirm( + std::shared_ptr show, + not_null peer, + SendError error, + Fn confirmed); + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 96185bfa2..45849d439 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -1459,38 +1459,13 @@ int PeerData::starsPerMessage() const { return 0; } -int PeerData::starsForMessageLocked() const { - if (const auto user = asUser()) { - return user->starsForMessageLocked(); - } else if (const auto channel = asChannel()) { - return channel->starsForMessageLocked(); - } - return 0; -} - -void PeerData::lockStarsForMessage() { - if (const auto user = asUser()) { - user->lockStarsForMessage(); - } else if (const auto channel = asChannel()) { - channel->lockStarsForMessage(); - } -} - -int PeerData::commitStarsForMessage() { - if (const auto user = asUser()) { - return user->commitStarsForMessage(); - } else if (const auto channel = asChannel()) { - return channel->commitStarsForMessage(); - } - return 0; -} - -void PeerData::cancelStarsForMessage() { - if (const auto user = asUser()) { - user->cancelStarsForMessage(); - } else if (const auto channel = asChannel()) { - channel->cancelStarsForMessage(); +int PeerData::starsPerMessageChecked() const { + if (const auto channel = asChannel()) { + return (channel->adminRights() || channel->amCreator()) + ? 0 + : channel->starsPerMessage(); } + return starsPerMessage(); } Data::GroupCall *PeerData::groupCall() const { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index b67f3b5b5..8d1b09289 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -269,10 +269,7 @@ public: [[nodiscard]] bool canManageGroupCall() const; [[nodiscard]] int starsPerMessage() const; - [[nodiscard]] int starsForMessageLocked() const; - void lockStarsForMessage(); - [[nodiscard]] int commitStarsForMessage(); - void cancelStarsForMessage(); + [[nodiscard]] int starsPerMessageChecked() const; [[nodiscard]] UserData *asBot(); [[nodiscard]] const UserData *asBot() const; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 514e75b2a..903ea87e5 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -552,38 +552,6 @@ void UserData::setStarsPerMessage(int stars) { } } -int UserData::starsForMessageLocked() const { - return _starsForMessageLocked; -} - -void UserData::lockStarsForMessage() { - if (_starsPerMessage == _starsForMessageLocked) { - return; - } - cancelStarsForMessage(); - if (_starsPerMessage) { - _starsForMessageLocked = _starsPerMessage; - session().credits().lock(StarsAmount(_starsPerMessage)); - session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage); - } -} - -int UserData::commitStarsForMessage() { - if (const auto stars = base::take(_starsForMessageLocked)) { - session().credits().withdrawLocked(StarsAmount(stars)); - session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage); - return stars; - } - return 0; -} - -void UserData::cancelStarsForMessage() { - if (const auto stars = base::take(_starsForMessageLocked)) { - session().credits().unlock(StarsAmount(stars)); - session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage); - } -} - bool UserData::canAddContact() const { return canShareThisContact() && !isContact(); } diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 7bac7de75..515a33313 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -184,10 +184,6 @@ public: void setStarsPerMessage(int stars); [[nodiscard]] int starsPerMessage() const; - [[nodiscard]] int starsForMessageLocked() const; - void lockStarsForMessage(); - [[nodiscard]] int commitStarsForMessage(); - void cancelStarsForMessage(); [[nodiscard]] bool canShareThisContact() const; [[nodiscard]] bool canAddContact() const; @@ -279,7 +275,6 @@ private: int _commonChatsCount = 0; int _peerGiftsCount = 0; int _starsPerMessage = 0; - int _starsForMessageLocked = 0; ContactStatus _contactStatus = ContactStatus::Unknown; CallsStatus _callsStatus = CallsStatus::Unknown; diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index d2f557c7f..e3ac2a4e7 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -93,10 +93,33 @@ Data::SendError GetErrorForSending( return tr::lng_forward_cant(tr::now); } } - if (peer->slowmodeApplied()) { - const auto count = (hasText ? 1 : 0) + const auto countMessages = [&] { + auto result = 0; + if (hasText) { + auto sending = TextWithEntities(); + auto left = TextWithEntities{ + request.text->text, + TextUtilities::ConvertTextTagsToEntities(request.text->tags) + }; + auto prepareFlags = Ui::ItemTextOptions( + thread->owningHistory(), + peer->session().user()).flags; + TextUtilities::PrepareForSending(left, prepareFlags); + + while (TextUtilities::CutPart(sending, left, MaxMessageSize)) { + ++result; + } + if (!result) { + ++result; + } + } + return result + (request.story ? 1 : 0) + + (request.mediaMessage ? 1 : 0) + (request.forward ? int(request.forward->size()) : 0); + }; + if (peer->slowmodeApplied()) { + const auto count = countMessages(); if (const auto history = peer->owner().historyLoaded(peer)) { if (!request.ignoreSlowmodeCountdown && (history->latestSendingMessage() != nullptr) @@ -135,6 +158,28 @@ Data::SendError GetErrorForSending( } } + if (const auto user = peer->asUser()) { + if (user->hasStarsPerMessage() + && !user->messageMoneyRestrictionsKnown()) { + user->updateFull(); + return Data::SendError({ .resolving = true }); + } + } else if (const auto channel = peer->asChannel()) { + if (!channel->isFullLoaded()) { + channel->updateFull(); + return Data::SendError({ .resolving = true }); + } + } + if (!peer->session().credits().loaded()) { + peer->session().credits().load(); + return Data::SendError({ .resolving = true }); + } else if (const auto perMessage = peer->starsPerMessageChecked()) { + const auto count = countMessages(); + return Data::SendError({ + .paidStars = count * perMessage, + .paidMessages = count, + }); + } return {}; } diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index 4160753bc..20d78579d 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -115,6 +115,7 @@ struct SendingErrorRequest { const Data::Story *story = nullptr; const TextWithTags *text = nullptr; bool ignoreSlowmodeCountdown = false; + bool mediaMessage = false; }; [[nodiscard]] Data::SendError GetErrorForSending( not_null peer, diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 2334826f6..6b4abe9cc 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -266,7 +266,6 @@ HistoryWidget::HistoryWidget( tr::lng_channel_mute(tr::now).toUpper(), st::historyComposeButton) , _reportMessages(this, QString(), st::historyComposeButton) -, _payForMessage(this, QString(), st::historyComposeButton) , _attachToggle(this, st::historyAttach) , _tabbedSelectorToggle(this, st::historyAttachEmoji) , _botKeyboardShow(this, st::historyBotKeyboardShow) @@ -384,7 +383,6 @@ HistoryWidget::HistoryWidget( _muteUnmute->addClickHandler([=] { toggleMuteUnmute(); }); setupGiftToChannelButton(); _reportMessages->addClickHandler([=] { reportSelectedMessages(); }); - _payForMessage->addClickHandler([=] { payForMessage(); }); _field->submits( ) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) { sendWithModifiers(modifiers); @@ -525,7 +523,6 @@ HistoryWidget::HistoryWidget( _joinChannel->hide(); _muteUnmute->hide(); _reportMessages->hide(); - _payForMessage->hide(); initVoiceRecordBar(); @@ -816,7 +813,7 @@ HistoryWidget::HistoryWidget( const auto was = (_sendAs != nullptr); refreshSendAsToggle(); - if (was != (_sendAs != nullptr) || _peer->starsPerMessage()) { + if (was != (_sendAs != nullptr)) { updateControlsVisibility(); updateControlsGeometry(); orderWidgets(); @@ -839,8 +836,10 @@ HistoryWidget::HistoryWidget( return; } } - if ((flags & PeerUpdateFlag::BotStartToken) - || (flags & PeerUpdateFlag::StarsPerMessage)) { + if (flags & PeerUpdateFlag::StarsPerMessage) { + updateFieldPlaceholder(); + } + if (flags & PeerUpdateFlag::BotStartToken) { updateControlsVisibility(); updateControlsGeometry(); } @@ -878,7 +877,9 @@ HistoryWidget::HistoryWidget( } if (flags & PeerUpdateFlag::FullInfo) { fullInfoUpdated(); - if (const auto channel = _peer->asChannel()) { + if (_peer->starsPerMessageChecked()) { + session().credits().load(); + } else if (const auto channel = _peer->asChannel()) { if (channel->allowedReactions().paidEnabled) { session().credits().load(); } @@ -886,6 +887,15 @@ HistoryWidget::HistoryWidget( } }, lifetime()); + session().credits().loadedValue( + ) | rpl::filter( + rpl::mappers::_1 + ) | rpl::take(1) | rpl::start_with_next([=] { + if (const auto callback = base::take(_resendOnFullUpdated)) { + callback(); + } + }, lifetime()); + using Type = Data::DefaultNotify; rpl::merge( session().data().notifySettings().defaultUpdates(Type::User), @@ -1100,19 +1110,8 @@ void HistoryWidget::initVoiceRecordBar() { }, lifetime()); _voiceRecordBar->sendVoiceRequests( - ) | rpl::start_with_next([=](const auto &data) { - if (!canWriteMessage() || data.bytes.isEmpty() || !_history) { - return; - } - - auto action = prepareSendAction(data.options); - session().api().sendVoiceMessage( - data.bytes, - data.waveform, - data.duration, - data.video, - action); - _voiceRecordBar->clearListenState(); + ) | rpl::start_with_next([=](const VoiceToSend &data) { + sendVoice(data); }, lifetime()); _voiceRecordBar->cancelRequests( @@ -1965,7 +1964,6 @@ void HistoryWidget::setInnerFocus() { || isRecording() || isJoinChannel() || isBotStart() - || isPayForMessage() || isBlocked() || (!_canSendTexts && !_editMsgId)) { if (_scroll->isHidden()) { @@ -2388,10 +2386,8 @@ void HistoryWidget::showHistory( setHistory(nullptr); _list = nullptr; - if (_peer) { - _peer->cancelStarsForMessage(); - } _peer = nullptr; + _resendOnFullUpdated = nullptr; _topicsRequested.clear(); _canSendMessages = false; _canSendTexts = false; @@ -3035,7 +3031,6 @@ bool HistoryWidget::canWriteMessage() const { && !isJoinChannel() && !isMuteUnmute() && !isBotStart() - && !isPayForMessage() && !isSearching(); } @@ -3096,7 +3091,6 @@ void HistoryWidget::updateControlsVisibility() { || isJoinChannel() || isMuteUnmute() || isBotStart() - || isPayForMessage() || isReportMessages()))) { const auto toggle = [&](Ui::FlatButton *shown) { const auto toggleOne = [&](not_null button) { @@ -3108,7 +3102,6 @@ void HistoryWidget::updateControlsVisibility() { } }; toggleOne(_reportMessages); - toggleOne(_payForMessage); toggleOne(_joinChannel); toggleOne(_muteUnmute); toggleOne(_botStart); @@ -3122,8 +3115,6 @@ void HistoryWidget::updateControlsVisibility() { toggle(_reportMessages); } else if (isBlocked()) { toggle(_unblock); - } else if (isPayForMessage()) { - toggle(_payForMessage); } else if (isJoinChannel()) { toggle(_joinChannel); } else if (isMuteUnmute()) { @@ -3186,7 +3177,6 @@ void HistoryWidget::updateControlsVisibility() { _joinChannel->hide(); _muteUnmute->hide(); _reportMessages->hide(); - _payForMessage->hide(); _send->show(); updateSendButtonType(); @@ -3300,7 +3290,6 @@ void HistoryWidget::updateControlsVisibility() { _joinChannel->hide(); _muteUnmute->hide(); _reportMessages->hide(); - _payForMessage->hide(); _attachToggle->hide(); if (_silent) { _silent->hide(); @@ -4352,6 +4341,36 @@ Api::SendAction HistoryWidget::prepareSendAction( return result; } +void HistoryWidget::sendVoice(const VoiceToSend &data) { + if (!canWriteMessage() || data.bytes.isEmpty() || !_history) { + return; + } + + const auto withPaymentApproved = [=](int approved) { + auto copy = data; + copy.options.starsApproved = approved; + sendVoice(copy); + }; + const auto ignoreSlowmodeCountdown = data.options.scheduled != 0; + if (showSendMessageError( + {}, + ignoreSlowmodeCountdown, + crl::guard(this, withPaymentApproved), + data.options.starsApproved, + true)) { + return; + } + + auto action = prepareSendAction(data.options); + session().api().sendVoiceMessage( + data.bytes, + data.waveform, + data.duration, + data.video, + action); + _voiceRecordBar->clearListenState(); +} + void HistoryWidget::send(Api::SendOptions options) { if (!_history) { return; @@ -4360,9 +4379,7 @@ void HistoryWidget::send(Api::SendOptions options) { return; } else if (!options.scheduled && showSlowmodeError()) { return; - } - - if (_voiceRecordBar->isListenState()) { + } else if (_voiceRecordBar->isListenState()) { _voiceRecordBar->requestToSendWithOptions(options); return; } @@ -4376,9 +4393,16 @@ void HistoryWidget::send(Api::SendOptions options) { message.webPage = _preview->draft(); const auto ignoreSlowmodeCountdown = (options.scheduled != 0); + const auto withPaymentApproved = [=](int approved) { + auto copy = options; + copy.starsApproved = approved; + send(copy); + }; if (showSendMessageError( message.textWithTags, - ignoreSlowmodeCountdown)) { + ignoreSlowmodeCountdown, + crl::guard(this, withPaymentApproved), + options.starsApproved)) { return; } @@ -4541,53 +4565,7 @@ void HistoryWidget::reportSelectedMessages() { } } -void HistoryWidget::payForMessage() { - if (!_peer || !session().credits().loaded()) { - return; - } else if (!_peer->starsPerMessage() || _peer->starsForMessageLocked()) { - updateControlsVisibility(); - } else if (session().local().isPeerTrustedPayForMessage(_peer->id)) { - payForMessageSure(); - } else { - const auto peer = _peer; - const auto count = peer->starsPerMessage(); - controller()->show(Box([=](not_null box) { - const auto trust = std::make_shared>(); - const auto confirmed = [=](Fn close) { - payForMessageSure((*trust)->checked()); - close(); - }; - Ui::ConfirmBox(box, { - .text = tr::lng_payment_confirm_text( - tr::now, - lt_count, - count, - lt_name, - Ui::Text::Bold(peer->shortName()), - Ui::Text::RichLangValue).append(' ').append( - tr::lng_payment_confirm_sure( - tr::now, - lt_count, - count, - Ui::Text::RichLangValue)), - .confirmed = confirmed, - .confirmText = tr::lng_payment_confirm_button(), - .title = tr::lng_payment_confirm_title(), - }); - const auto skip = st::defaultCheckbox.margin.top(); - *trust = box->addRow( - object_ptr( - box, - tr::lng_payment_confirm_dont_ask(tr::now)), - st::boxRowPadding + QMargins(0, skip, 0, skip)); - })); - } -} - void HistoryWidget::payForMessageSure(bool trust) { - if (trust) { - session().local().markPeerTrustedPayForMessage(_peer->id); - } const auto required = _peer->starsPerMessage(); if (!required) { return; @@ -4595,7 +4573,6 @@ void HistoryWidget::payForMessageSure(bool trust) { const auto done = [=](Settings::SmallBalanceResult result) { if (result == Settings::SmallBalanceResult::Success || result == Settings::SmallBalanceResult::Already) { - _peer->lockStarsForMessage(); if (canWriteMessage()) { setInnerFocus(); } @@ -5154,40 +5131,6 @@ bool HistoryWidget::isSearching() const { return _composeSearch != nullptr; } -bool HistoryWidget::isPayForMessage() const { - const auto stars = _peer ? _peer->starsPerMessage() : 0; - const auto locked = _peer ? _peer->starsForMessageLocked() : 0; - if (!stars || locked) { - return false; - } else if (const auto channel = _peer->asChannel()) { - if (channel->amCreator() || channel->adminRights()) { - return false; - } - } - - const auto creating = !_payForMessageStars.current(); - _payForMessageStars = stars; - if (creating) { - session().credits().load(); - - auto text = _payForMessageStars.value( - ) | rpl::map([session = &session()](int stars) { - return tr::lng_payment_for_message( - tr::now, - lt_cost, - Ui::CreditsEmojiSmall(session).append( - Lang::FormatCountDecimal(stars)), - Ui::Text::WithEntities); - }); - Ui::SetButtonMarkedLabel( - _payForMessage, - std::move(text), - &session(), - st::historyComposeButtonText); - } - return true; -} - bool HistoryWidget::showRecordButton() const { return (_recordAvailability != Webrtc::RecordAvailability::None) && !_voiceRecordBar->isListenState() @@ -5245,7 +5188,6 @@ bool HistoryWidget::updateCmdStartShown() { && _peer->asChannel()->mgInfo->botStatus > 0))) { if (!isBotStart() && !isBlocked() - && !isPayForMessage() && !_keyboard->hasMarkup() && !_keyboard->forceReply() && !_editMsgId) { @@ -5659,7 +5601,7 @@ void HistoryWidget::moveFieldControls() { // (_botMenu.button) (_attachToggle|_replaceMedia) (_sendAs) ---- _inlineResults ------------------------------ _tabbedPanel ------ _fieldBarCancel // (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send -// (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages|_payForMessage) +// (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages) auto buttonsBottom = bottom - _attachToggle->height(); auto left = st::historySendRight; @@ -5733,7 +5675,6 @@ void HistoryWidget::moveFieldControls() { _joinChannel->setGeometry(fullWidthButtonRect); _muteUnmute->setGeometry(fullWidthButtonRect); _reportMessages->setGeometry(fullWidthButtonRect); - _payForMessage->setGeometry(fullWidthButtonRect); if (_sendRestriction) { _sendRestriction->setGeometry(fullWidthButtonRect); } @@ -5824,14 +5765,21 @@ void HistoryWidget::updateFieldPlaceholder() { } _field->setPlaceholder([&]() -> rpl::producer { + const auto peer = _history ? _history->peer.get() : nullptr; if (_editMsgId) { return tr::lng_edit_message_text(); - } else if (!_history) { + } else if (!peer) { return tr::lng_message_ph(); } else if ((_kbShown || _keyboard->forceReply()) && !_keyboard->placeholder().isEmpty()) { return rpl::single(_keyboard->placeholder()); - } else if (const auto channel = _history->peer->asChannel()) { + } else if (const auto stars = peer->starsPerMessageChecked()) { + return tr::lng_message_paid_ph( + lt_amount, + tr::lng_prize_credits_amount( + lt_count, + rpl::single(stars * 1.))); + } else if (const auto channel = peer->asChannel()) { const auto topic = resolveReplyToTopic(); const auto topicRootId = topic ? topic->rootId() @@ -5948,7 +5896,10 @@ Data::ForumTopic *HistoryWidget::resolveReplyToTopic() { bool HistoryWidget::showSendMessageError( const TextWithTags &textWithTags, - bool ignoreSlowmodeCountdown) { + bool ignoreSlowmodeCountdown, + Fn resend, + int starsApproved, + bool mediaMessage) { if (!_canSendMessages) { return false; } @@ -5960,8 +5911,17 @@ bool HistoryWidget::showSendMessageError( .forward = &_forwardPanel->items(), .text = &textWithTags, .ignoreSlowmodeCountdown = ignoreSlowmodeCountdown, + .mediaMessage = mediaMessage, }); - if (!error) { + if (resend && error.resolving) { + _resendOnFullUpdated = [=] { resend(starsApproved); }; + return true; + } else if (resend && error.paidStars > starsApproved) { + Data::ShowSendPaidConfirm(controller(), _peer, error, [=] { + resend(error.paidStars); + }); + return true; + } else if (!error) { return false; } Data::ShowSendErrorToast(controller(), _peer, error); @@ -6174,25 +6134,18 @@ void HistoryWidget::handleHistoryChange(not_null history) { const auto joinChannel = isJoinChannel(); const auto muteUnmute = isMuteUnmute(); const auto reportMessages = isReportMessages(); - const auto payForMessage = isPayForMessage(); const auto update = false || (_reportMessages->isHidden() == reportMessages) || (!reportMessages && _unblock->isHidden() == unblock) || (!reportMessages && !unblock - && _payForMessage->isHidden() == payForMessage) - || (!reportMessages - && !unblock - && !payForMessage && _botStart->isHidden() == botStart) || (!reportMessages && !unblock - && !payForMessage && !botStart && _joinChannel->isHidden() == joinChannel) || (!reportMessages && !unblock - && !payForMessage && !botStart && !joinChannel && _muteUnmute->isHidden() == muteUnmute); @@ -6556,7 +6509,6 @@ void HistoryWidget::updateHistoryGeometry( } else if (!editingMessage() && (isSearching() || isBlocked() - || isPayForMessage() || isBotStart() || isJoinChannel() || isMuteUnmute() @@ -6866,7 +6818,6 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { if (!isSearching() && !isBotStart() && !isBlocked() - && !isPayForMessage() && _canSendMessages && (wasVisible || (_replyTo && _replyEditMsg) @@ -8528,6 +8479,9 @@ void HistoryWidget::fullInfoUpdated() { updateControlsVisibility(); updateControlsGeometry(); } + if (const auto callback = base::take(_resendOnFullUpdated)) { + callback(); + } } void HistoryWidget::handlePeerUpdate() { @@ -8743,7 +8697,6 @@ void HistoryWidget::updateTopBarSelection() { || (_list && _list->wasSelectedText()) || isRecording() || isBotStart() - || isPayForMessage() || isBlocked() || (!_canSendTexts && !_editMsgId)) { _list->setFocus(); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index a6f61228e..0b487ea74 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -115,6 +115,7 @@ class TTLButton; class WebpageProcessor; class CharactersLimitLabel; class PhotoEditSpoilerManager; +struct VoiceToSend; } // namespace HistoryView::Controls class BotKeyboard; @@ -318,6 +319,7 @@ protected: private: using TabbedPanel = ChatHelpers::TabbedPanel; using TabbedSelector = ChatHelpers::TabbedSelector; + using VoiceToSend = HistoryView::Controls::VoiceToSend; enum ScrollChangeType { ScrollChangeNone, @@ -401,6 +403,7 @@ private: [[nodiscard]] Api::SendAction prepareSendAction( Api::SendOptions options) const; + void sendVoice(const VoiceToSend &data); void send(Api::SendOptions options); void sendWithModifiers(Qt::KeyboardModifiers modifiers); void sendScheduled(Api::SendOptions initialOptions); @@ -422,7 +425,6 @@ private: [[nodiscard]] int computeMaxFieldHeight() const; void toggleMuteUnmute(); void reportSelectedMessages(); - void payForMessage(); void payForMessageSure(bool trust = false); void showKeyboardHideButton(); void toggleKeyboard(bool manual = true); @@ -468,7 +470,10 @@ private: std::optional compress) const; bool showSendMessageError( const TextWithTags &textWithTags, - bool ignoreSlowmodeCountdown); + bool ignoreSlowmodeCountdown, + Fn resend = nullptr, + int starsApproved = 0, + bool mediaMessage = false); void sendingFilesConfirmed( Ui::PreparedList &&list, @@ -642,7 +647,6 @@ private: [[nodiscard]] bool isJoinChannel() const; [[nodiscard]] bool isMuteUnmute() const; [[nodiscard]] bool isReportMessages() const; - [[nodiscard]] bool isPayForMessage() const; bool updateCmdStartShown(); void updateSendButtonType(); [[nodiscard]] bool showRecordButton() const; @@ -761,6 +765,7 @@ private: std::unique_ptr _autocomplete; std::unique_ptr _emojiSuggestions; object_ptr _supportAutocomplete; + Fn _resendOnFullUpdated; UserData *_inlineBot = nullptr; QString _inlineBotUsername; @@ -781,8 +786,6 @@ private: QPointer _giftToChannelIn; QPointer _giftToChannelOut; object_ptr _reportMessages; - object_ptr _payForMessage; - mutable rpl::variable _payForMessageStars; struct { object_ptr button = { nullptr }; QString text; diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.cpp b/Telegram/SourceFiles/media/stories/media_stories_share.cpp index 37e881f38..90f6ca1e2 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_share.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_share.cpp @@ -111,29 +111,33 @@ namespace Media::Stories { const auto threadPeer = thread->peer(); const auto threadHistory = thread->owningHistory(); const auto randomId = base::RandomValue(); - auto sendFlags = MTPmessages_SendMedia::Flags(0); + using SendFlag = MTPmessages_SendMedia::Flag; + auto sendFlags = SendFlag(0) | SendFlag(0); if (action.replyTo) { - sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to; + sendFlags |= SendFlag::f_reply_to; } const auto silentPost = ShouldSendSilent(threadPeer, options); if (silentPost) { - sendFlags |= MTPmessages_SendMedia::Flag::f_silent; + sendFlags |= SendFlag::f_silent; } if (options.scheduled) { - sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; + sendFlags |= SendFlag::f_schedule_date; } if (options.shortcutId) { - sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut; + sendFlags |= SendFlag::f_quick_reply_shortcut; } if (options.effectId) { - sendFlags |= MTPmessages_SendMedia::Flag::f_effect; + sendFlags |= SendFlag::f_effect; } if (options.invertCaption) { - sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media; + sendFlags |= SendFlag::f_invert_media; } - const auto starsPaid = peer->commitStarsForMessage(); + const auto starsPaid = std::min( + peer->starsPerMessageChecked(), + options.starsApproved); if (starsPaid) { - sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars; + options.starsApproved -= starsPaid; + sendFlags |= SendFlag::f_allow_paid_stars; } const auto done = [=] { if (!--state->requests) { diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index 452671035..69a98e922 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -220,7 +220,7 @@ inputPeerNotifySettings#cacb6ae2 flags:# show_previews:flags.0?Bool silent:flags peerNotifySettings#99622c0c flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int ios_sound:flags.3?NotificationSound android_sound:flags.4?NotificationSound other_sound:flags.5?NotificationSound stories_muted:flags.6?Bool stories_hide_sender:flags.7?Bool stories_ios_sound:flags.8?NotificationSound stories_android_sound:flags.9?NotificationSound stories_other_sound:flags.10?NotificationSound = PeerNotifySettings; -peerSettings#a8639d72 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true business_bot_paused:flags.11?true business_bot_can_reply:flags.12?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int business_bot_id:flags.13?long business_bot_manage_url:flags.13?string charge_paid_message_stars:flags.14?long = PeerSettings; +peerSettings#d8c39ec flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true business_bot_paused:flags.11?true business_bot_can_reply:flags.12?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int business_bot_id:flags.13?long business_bot_manage_url:flags.13?string charge_paid_message_stars:flags.14?long registration_month:flags.15?string phone_country:flags.16?string location_country:flags.17?string = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; wallPaperNoFile#e0804116 id:long flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; @@ -1480,6 +1480,7 @@ inputInvoiceChatInviteSubscription#34e793f1 hash:string = InputInvoice; inputInvoiceStarGift#e8625e92 flags:# hide_name:flags.0?true include_upgrade:flags.2?true peer:InputPeer gift_id:long message:flags.1?TextWithEntities = InputInvoice; inputInvoiceStarGiftUpgrade#4d818d5d flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = InputInvoice; inputInvoiceStarGiftTransfer#4a5f5bd9 stargift:InputSavedStarGift to_id:InputPeer = InputInvoice; +inputInvoicePremiumGiftStars#dabab2ef flags:# user_id:InputUser months:int message:flags.0?TextWithEntities = InputInvoice; payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice; @@ -1495,7 +1496,7 @@ inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = In inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose; inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector countries_iso2:flags.2?Vector prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose; -premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption; +premiumGiftOption#79c059f7 flags:# months:int currency:string amount:long bot_url:flags.1?string store_product:flags.0?string = PremiumGiftOption; paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;