Allow sending paid messages.

This commit is contained in:
John Preston 2025-02-13 19:03:11 +04:00
parent 45c7829cd8
commit bbc14ba74f
25 changed files with 497 additions and 143 deletions

View file

@ -2789,6 +2789,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_small_balance_reaction" = "Buy **Stars** and send them to {channel} to support their posts.";
"lng_credits_small_balance_subscribe" = "Buy **Stars** and subscribe to **{channel}** and other channels.";
"lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts.";
"lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}.";
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
"lng_credits_enough" = "You have enough stars at the moment. {link}";
@ -4821,8 +4822,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_slowmode_seconds#one" = "{count} second";
"lng_slowmode_seconds#other" = "{count} seconds";
"lng_payment_for_message#one" = "Pay {star}{count} for 1 Message";
"lng_payment_for_message#other" = "Pay {star}{count} for 1 Message";
"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.";
@ -4830,8 +4830,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payment_confirm_sure#other" = "Would you like to pay **{count}** Stars to send one message?";
"lng_payment_confirm_dont_ask" = "Don't ask me again";
"lng_payment_confirm_button" = "Pay for 1 Message";
"lng_payment_bar_text#one" = "{name} must pay {star}{count} for each message to you.";
"lng_payment_bar_text#other" = "{name} must pay {star}{count} for each message to you.";
"lng_payment_bar_text" = "{name} must pay {cost} for each message to you.";
"lng_payment_bar_button" = "Remove Fee";
"lng_payment_refund_title" = "Remove Fee";
"lng_payment_refund_text" = "Are you sure you want to allow {name} to message you for free?";

View file

@ -21,7 +21,6 @@ inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
struct SendOptions {
uint64 price = 0;
int64 paidByStars = 0;
PeerData *sendAs = nullptr;
TimeId scheduled = 0;
BusinessShortcutId shortcutId = 0;

View file

@ -59,6 +59,7 @@ void Polls::create(
history->startSavingCloudDraft(topicRootId);
}
const auto silentPost = ShouldSendSilent(peer, action.options);
const auto starsPaid = peer->commitStarsForMessage();
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
@ -71,7 +72,7 @@ void Polls::create(
if (action.options.effectId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
}
if (action.options.paidByStars) {
if (starsPaid) {
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
const auto sendAs = action.options.sendAs;
@ -97,7 +98,7 @@ void Polls::create(
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
MTP_long(action.options.effectId),
MTP_long(action.options.paidByStars)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) {
if (clearCloudDraft) {
history->finishSavingCloudDraft(

View file

@ -95,6 +95,7 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
const auto messagePostAuthor = peer->isBroadcast()
? session->user()->name()
: QString();
const auto starsPaid = peer->commitStarsForMessage();
if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled;
@ -111,7 +112,7 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
}
if (action.options.paidByStars) {
if (starsPaid) {
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
@ -133,7 +134,7 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId),
MTP_long(action.options.paidByStars)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId);
@ -194,6 +195,7 @@ void SendExistingMedia(
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
const auto captionText = caption.text;
const auto starsPaid = peer->commitStarsForMessage();
if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled;
@ -210,7 +212,7 @@ void SendExistingMedia(
flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
}
if (action.options.paidByStars) {
if (starsPaid) {
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
@ -248,7 +250,7 @@ void SendExistingMedia(
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId),
MTP_long(action.options.paidByStars)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
if (error.code() == 400
@ -388,7 +390,8 @@ bool SendDice(MessageToSend &message) {
flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
}
if (action.options.paidByStars) {
const auto starsPaid = peer->commitStarsForMessage();
if (starsPaid) {
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
@ -423,7 +426,7 @@ bool SendDice(MessageToSend &message) {
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId),
MTP_long(action.options.paidByStars)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId, newId);

View file

@ -3883,7 +3883,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
}
if (action.options.paidByStars) {
const auto starsPaid = peer->commitStarsForMessage();
if (starsPaid) {
sendFlags |= MTPmessages_SendMessage::Flag::f_allow_paid_stars;
mediaFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
@ -3943,7 +3944,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut,
MTP_long(action.options.effectId),
MTP_long(action.options.paidByStars)
MTP_long(starsPaid)
), done, fail);
} else {
histories.sendPreparedMessage(
@ -3962,7 +3963,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut,
MTP_long(action.options.effectId),
MTP_long(action.options.paidByStars)
MTP_long(starsPaid)
), done, fail);
}
isFirst = false;
@ -4059,7 +4060,8 @@ void ApiWrap::sendInlineResult(
if (action.options.hideViaBot) {
sendFlags |= SendFlag::f_hide_via;
}
if (action.options.paidByStars) {
const auto starsPaid = peer->commitStarsForMessage();
if (starsPaid) {
sendFlags |= SendFlag::f_allow_paid_stars;
}
@ -4100,7 +4102,7 @@ void ApiWrap::sendInlineResult(
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
MTP_long(action.options.paidByStars)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) {
history->finishSavingCloudDraft(
topicRootId,
@ -4232,6 +4234,7 @@ void ApiWrap::sendMediaWithRandomId(
Fn<void(bool)> done) {
const auto history = item->history();
const auto replyTo = item->replyTo();
const auto peer = history->peer;
auto caption = item->originalText();
TextUtilities::Trim(caption);
@ -4241,6 +4244,7 @@ void ApiWrap::sendMediaWithRandomId(
Api::ConvertOption::SkipLocal);
const auto updateRecentStickers = Api::HasAttachedStickers(media);
const auto starsPaid = peer->commitStarsForMessage();
using Flag = MTPmessages_SendMedia::Flag;
const auto flags = Flag(0)
@ -4254,10 +4258,9 @@ void ApiWrap::sendMediaWithRandomId(
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
| (options.effectId ? Flag::f_effect : Flag(0))
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
| (options.paidByStars ? Flag::f_allow_paid_stars : Flag(0));
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
auto &histories = history->owner().histories();
const auto peer = history->peer;
const auto itemId = item->fullId();
histories.sendPreparedMessage(
history,
@ -4282,7 +4285,7 @@ void ApiWrap::sendMediaWithRandomId(
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, options.shortcutId),
MTP_long(options.effectId),
MTP_long(options.paidByStars)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) {
if (done) done(true);
if (updateRecentStickers) {
@ -4311,6 +4314,7 @@ void ApiWrap::sendMultiPaidMedia(
const auto history = item->history();
const auto replyTo = item->replyTo();
const auto peer = history->peer;
auto caption = item->originalText();
TextUtilities::Trim(caption);
@ -4318,6 +4322,7 @@ void ApiWrap::sendMultiPaidMedia(
_session,
caption.entities,
Api::ConvertOption::SkipLocal);
const auto starsPaid = peer->commitStarsForMessage();
using Flag = MTPmessages_SendMedia::Flag;
const auto flags = Flag(0)
@ -4331,10 +4336,9 @@ void ApiWrap::sendMultiPaidMedia(
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
| (options.effectId ? Flag::f_effect : Flag(0))
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
| (options.paidByStars ? Flag::f_allow_paid_stars : Flag(0));
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
auto &histories = history->owner().histories();
const auto peer = history->peer;
const auto itemId = item->fullId();
album->sent = true;
histories.sendPreparedMessage(
@ -4358,7 +4362,7 @@ void ApiWrap::sendMultiPaidMedia(
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, options.shortcutId),
MTP_long(options.effectId),
MTP_long(options.paidByStars)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) {
if (const auto album = _sendingAlbums.take(groupId)) {
const auto copy = (*album)->items;

View file

@ -1571,6 +1571,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
: topicRootId;
const auto peer = thread->peer();
const auto threadHistory = thread->owningHistory();
const auto starsPaid = peer->commitStarsForMessage();
histories.sendRequest(threadHistory, requestType, [=](
Fn<void()> finish) {
const auto session = &threadHistory->session();
@ -1583,9 +1584,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
| (options.shortcutId
? Flag::f_quick_reply_shortcut
: Flag(0))
| (options.paidByStars
? Flag::f_allow_paid_stars
: Flag());
| (starsPaid ? Flag::f_allow_paid_stars : Flag());
threadHistory->sendRequestId = api.request(
MTPmessages_ForwardMessages(
MTP_flags(sendFlags),
@ -1598,7 +1597,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
MTP_inputPeerEmpty(), // send_as
Data::ShortcutIdToMTP(session, options.shortcutId),
MTP_int(videoTimestamp.value_or(0)),
MTP_long(options.paidByStars)
MTP_long(starsPaid)
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
threadHistory->session().api().applyUpdates(updates);
state->requests.remove(reqId);

View file

@ -855,6 +855,10 @@ historyComposeButton: FlatButton {
color: historyComposeButtonBgRipple;
}
}
historyComposeButtonText: FlatLabel(defaultFlatLabel) {
style: semiboldTextStyle;
textFg: windowActiveTextFg;
}
historyGiftToChannel: IconButton(defaultIconButton) {
width: 46px;
height: 46px;

View file

@ -205,13 +205,13 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
});
};
if (_bot->isVerified()
|| _bot->session().local().isBotTrustedOpenGame(_bot->id)) {
|| _bot->session().local().isPeerTrustedOpenGame(_bot->id)) {
openGame();
} else {
if (const auto controller = my.sessionWindow.get()) {
const auto callback = [=, bot = _bot](Fn<void()> close) {
close();
bot->session().local().markBotTrustedOpenGame(bot->id);
bot->session().local().markPeerTrustedOpenGame(bot->id);
openGame();
};
controller->show(Ui::MakeConfirmBox({

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "api/api_global_privacy.h"
#include "data/components/credits.h"
#include "data/data_changes.h"
#include "data/data_channel_admins.h"
#include "data/data_user.h"
@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_chat_invite.h"
#include "api/api_invite_links.h"
#include "apiwrap.h"
#include "storage/storage_account.h"
#include "ui/unread_badge.h"
#include "window/notifications_manager.h"
@ -861,7 +863,7 @@ void ChannelData::growSlowmodeLastMessage(TimeId when) {
int ChannelData::starsPerMessage() const {
if (const auto info = mgInfo.get()) {
return info->starsPerMessage;
return info->_starsPerMessage;
}
return 0;
}
@ -870,8 +872,54 @@ void ChannelData::setStarsPerMessage(int stars) {
if (!mgInfo || starsPerMessage() == stars) {
return;
}
mgInfo->starsPerMessage = stars;
const auto removed = mgInfo->_starsPerMessage && !stars;
mgInfo->_starsPerMessage = stars;
session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage);
if (removed) {
session().local().clearPeerTrusted(id);
}
}
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 {

View file

@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_bot_commands.h"
#include "data/data_user_names.h"
class ChannelData;
struct ChannelLocation {
QString address;
Data::LocationPoint point;
@ -145,13 +147,15 @@ public:
int slowmodeSeconds = 0;
TimeId slowmodeLastMessage = 0;
int starsPerMessage = 0;
private:
ChatData *_migratedFrom = nullptr;
ChannelLocation _location;
Data::ChatBotCommands _botCommands;
std::unique_ptr<Data::Forum> _forum;
int _starsPerMessage = 0;
int _starsForMessageLocked = 0;
friend class ChannelData;
};
@ -458,8 +462,12 @@ public:
[[nodiscard]] TimeId slowmodeLastMessage() const;
void growSlowmodeLastMessage(TimeId when);
[[nodiscard]] int starsPerMessage() const;
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);

View file

@ -1450,6 +1450,49 @@ bool PeerData::canManageGroupCall() const {
return false;
}
int PeerData::starsPerMessage() const {
if (const auto user = asUser()) {
return user->starsPerMessage();
} else if (const auto channel = asChannel()) {
return channel->starsPerMessage();
}
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();
}
}
Data::GroupCall *PeerData::groupCall() const {
if (const auto chat = asChat()) {
return chat->groupCall();

View file

@ -268,6 +268,12 @@ public:
[[nodiscard]] int slowmodeSecondsLeft() const;
[[nodiscard]] bool canManageGroupCall() const;
[[nodiscard]] int starsPerMessage() const;
[[nodiscard]] int starsForMessageLocked() const;
void lockStarsForMessage();
[[nodiscard]] int commitStarsForMessage();
void cancelStarsForMessage();
[[nodiscard]] UserData *asBot();
[[nodiscard]] const UserData *asBot() const;
[[nodiscard]] UserData *asUser();

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_sensitive_content.h"
#include "api/api_statistics.h"
#include "storage/localstorage.h"
#include "storage/storage_account.h"
#include "storage/storage_user_photos.h"
#include "main/main_session.h"
#include "data/business/data_business_common.h"
@ -538,8 +539,44 @@ int UserData::starsPerMessage() const {
void UserData::setStarsPerMessage(int stars) {
if (_starsPerMessage != stars) {
const auto removed = _starsPerMessage && !stars;
_starsPerMessage = stars;
session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage);
if (removed) {
session().local().clearPeerTrusted(id);
}
}
}
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);
}
}

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "core/stars_amount.h"
#include "data/components/credits.h"
#include "data/data_birthday.h"
#include "data/data_peer.h"
#include "data/data_chat_participant_status.h"
@ -179,8 +180,12 @@ public:
[[nodiscard]] bool canSendIgnoreRequirePremium() const;
[[nodiscard]] bool readDatesPrivate() const;
[[nodiscard]] int starsPerMessage() const;
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;
@ -272,6 +277,7 @@ private:
int _commonChatsCount = 0;
int _peerGiftsCount = 0;
int _starsPerMessage = 0;
int _starsForMessageLocked = 0;
ContactStatus _contactStatus = ContactStatus::Unknown;
CallsStatus _callsStatus = CallsStatus::Unknown;

View file

@ -5577,7 +5577,23 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
auto preparePaidMessage = [&](
const MTPDmessageActionPaidMessage &action) {
auto result = PreparedServiceText();
result.text.text = u"paid for message"_q; AssertIsDebug();
const auto stars = action.vstars().v;
if (_from->isSelf()) {
result.text = tr::lng_action_paid_message_sent(
tr::now,
lt_count,
stars,
Ui::Text::WithEntities);
} else {
result.links.push_back(_from->createOpenLink());
result.text = tr::lng_action_paid_message_got(
tr::now,
lt_count,
stars,
lt_name,
Ui::Text::Link(_from->shortName(), 1),
Ui::Text::WithEntities);
}
return result;
};

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_unread_things.h"
#include "ui/boxes/confirm_box.h"
#include "boxes/delete_messages_box.h"
#include "boxes/send_credits_box.h"
#include "boxes/send_files_box.h"
#include "boxes/share_box.h"
#include "boxes/edit_caption_box.h"
@ -129,6 +130,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtproto_config.h"
#include "lang/lang_keys.h"
#include "settings/business/settings_quick_replies.h"
#include "settings/settings_credits_graphics.h"
#include "storage/localimageloader.h"
#include "storage/storage_account.h"
#include "storage/file_upload.h"
@ -146,6 +148,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/chat_theme.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/continuous_scroll.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/elastic_scroll.h"
#include "ui/widgets/popup_menu.h"
#include "ui/item_text_options.h"
@ -263,6 +266,7 @@ 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)
@ -380,6 +384,7 @@ HistoryWidget::HistoryWidget(
_muteUnmute->addClickHandler([=] { toggleMuteUnmute(); });
setupGiftToChannelButton();
_reportMessages->addClickHandler([=] { reportSelectedMessages(); });
_payForMessage->addClickHandler([=] { payForMessage(); });
_field->submits(
) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) {
sendWithModifiers(modifiers);
@ -520,6 +525,7 @@ HistoryWidget::HistoryWidget(
_joinChannel->hide();
_muteUnmute->hide();
_reportMessages->hide();
_payForMessage->hide();
initVoiceRecordBar();
@ -798,6 +804,7 @@ HistoryWidget::HistoryWidget(
| PeerUpdateFlag::MessagesTTL
| PeerUpdateFlag::ChatThemeEmoji
| PeerUpdateFlag::FullInfo
| PeerUpdateFlag::StarsPerMessage
) | rpl::filter([=](const Data::PeerUpdate &update) {
return (update.peer.get() == _peer);
}) | rpl::map([](const Data::PeerUpdate &update) {
@ -809,7 +816,7 @@ HistoryWidget::HistoryWidget(
const auto was = (_sendAs != nullptr);
refreshSendAsToggle();
if (was != (_sendAs != nullptr)) {
if (was != (_sendAs != nullptr) || _peer->starsPerMessage()) {
updateControlsVisibility();
updateControlsGeometry();
orderWidgets();
@ -832,7 +839,8 @@ HistoryWidget::HistoryWidget(
return;
}
}
if (flags & PeerUpdateFlag::BotStartToken) {
if ((flags & PeerUpdateFlag::BotStartToken)
|| (flags & PeerUpdateFlag::StarsPerMessage)) {
updateControlsVisibility();
updateControlsGeometry();
}
@ -1957,6 +1965,7 @@ void HistoryWidget::setInnerFocus() {
|| isRecording()
|| isJoinChannel()
|| isBotStart()
|| isPayForMessage()
|| isBlocked()
|| (!_canSendTexts && !_editMsgId)) {
if (_scroll->isHidden()) {
@ -2379,6 +2388,9 @@ void HistoryWidget::showHistory(
setHistory(nullptr);
_list = nullptr;
if (_peer) {
_peer->cancelStarsForMessage();
}
_peer = nullptr;
_topicsRequested.clear();
_canSendMessages = false;
@ -3017,16 +3029,14 @@ bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
}
bool HistoryWidget::canWriteMessage() const {
if (!_history || !_canSendMessages) {
return false;
}
if (isBlocked() || isJoinChannel() || isMuteUnmute() || isBotStart()) {
return false;
}
if (isSearching()) {
return false;
}
return true;
return _history
&& _canSendMessages
&& !isBlocked()
&& !isJoinChannel()
&& !isMuteUnmute()
&& !isBotStart()
&& !isPayForMessage()
&& !isSearching();
}
void HistoryWidget::updateControlsVisibility() {
@ -3086,6 +3096,7 @@ void HistoryWidget::updateControlsVisibility() {
|| isJoinChannel()
|| isMuteUnmute()
|| isBotStart()
|| isPayForMessage()
|| isReportMessages()))) {
const auto toggle = [&](Ui::FlatButton *shown) {
const auto toggleOne = [&](not_null<Ui::FlatButton*> button) {
@ -3097,6 +3108,7 @@ void HistoryWidget::updateControlsVisibility() {
}
};
toggleOne(_reportMessages);
toggleOne(_payForMessage);
toggleOne(_joinChannel);
toggleOne(_muteUnmute);
toggleOne(_botStart);
@ -3110,6 +3122,8 @@ void HistoryWidget::updateControlsVisibility() {
toggle(_reportMessages);
} else if (isBlocked()) {
toggle(_unblock);
} else if (isPayForMessage()) {
toggle(_payForMessage);
} else if (isJoinChannel()) {
toggle(_joinChannel);
} else if (isMuteUnmute()) {
@ -3172,6 +3186,7 @@ void HistoryWidget::updateControlsVisibility() {
_joinChannel->hide();
_muteUnmute->hide();
_reportMessages->hide();
_payForMessage->hide();
_send->show();
updateSendButtonType();
@ -3285,6 +3300,7 @@ void HistoryWidget::updateControlsVisibility() {
_joinChannel->hide();
_muteUnmute->hide();
_reportMessages->hide();
_payForMessage->hide();
_attachToggle->hide();
if (_silent) {
_silent->hide();
@ -4525,6 +4541,73 @@ 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<Ui::GenericBox*> box) {
const auto trust = std::make_shared<QPointer<Ui::Checkbox>>();
const auto confirmed = [=](Fn<void()> 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<Ui::Checkbox>(
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;
}
const auto done = [=](Settings::SmallBalanceResult result) {
if (result == Settings::SmallBalanceResult::Success
|| result == Settings::SmallBalanceResult::Already) {
_peer->lockStarsForMessage();
if (canWriteMessage()) {
setInnerFocus();
}
}
};
Settings::MaybeRequestBalanceIncrease(
controller()->uiShow(),
required,
Settings::SmallBalanceForMessage{ .recipientId = _peer->id },
crl::guard(this, done));
}
History *HistoryWidget::history() const {
return _history;
}
@ -5071,6 +5154,40 @@ 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()
@ -5128,6 +5245,7 @@ bool HistoryWidget::updateCmdStartShown() {
&& _peer->asChannel()->mgInfo->botStatus > 0))) {
if (!isBotStart()
&& !isBlocked()
&& !isPayForMessage()
&& !_keyboard->hasMarkup()
&& !_keyboard->forceReply()
&& !_editMsgId) {
@ -5541,7 +5659,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)
// (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages|_payForMessage)
auto buttonsBottom = bottom - _attachToggle->height();
auto left = st::historySendRight;
@ -5615,6 +5733,7 @@ void HistoryWidget::moveFieldControls() {
_joinChannel->setGeometry(fullWidthButtonRect);
_muteUnmute->setGeometry(fullWidthButtonRect);
_reportMessages->setGeometry(fullWidthButtonRect);
_payForMessage->setGeometry(fullWidthButtonRect);
if (_sendRestriction) {
_sendRestriction->setGeometry(fullWidthButtonRect);
}
@ -6055,18 +6174,25 @@ void HistoryWidget::handleHistoryChange(not_null<const History*> 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);
@ -6430,6 +6556,7 @@ void HistoryWidget::updateHistoryGeometry(
} else if (!editingMessage()
&& (isSearching()
|| isBlocked()
|| isPayForMessage()
|| isBotStart()
|| isJoinChannel()
|| isMuteUnmute()
@ -6739,6 +6866,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
if (!isSearching()
&& !isBotStart()
&& !isBlocked()
&& !isPayForMessage()
&& _canSendMessages
&& (wasVisible
|| (_replyTo && _replyEditMsg)
@ -8615,6 +8743,7 @@ void HistoryWidget::updateTopBarSelection() {
|| (_list && _list->wasSelectedText())
|| isRecording()
|| isBotStart()
|| isPayForMessage()
|| isBlocked()
|| (!_canSendTexts && !_editMsgId)) {
_list->setFocus();

View file

@ -422,6 +422,8 @@ private:
[[nodiscard]] int computeMaxFieldHeight() const;
void toggleMuteUnmute();
void reportSelectedMessages();
void payForMessage();
void payForMessageSure(bool trust = false);
void showKeyboardHideButton();
void toggleKeyboard(bool manual = true);
void startBotCommand();
@ -640,6 +642,7 @@ 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;
@ -778,6 +781,8 @@ private:
QPointer<Ui::IconButton> _giftToChannelIn;
QPointer<Ui::IconButton> _giftToChannelOut;
object_ptr<Ui::FlatButton> _reportMessages;
object_ptr<Ui::FlatButton> _payForMessage;
mutable rpl::variable<int> _payForMessageStars;
struct {
object_ptr<Ui::RoundButton> button = { nullptr };
QString text;

View file

@ -348,6 +348,7 @@ private:
std::unique_ptr<ComposeControls> _composeControls;
std::unique_ptr<ComposeSearch> _composeSearch;
std::unique_ptr<Ui::FlatButton> _joinGroup;
std::unique_ptr<Ui::FlatButton> _payForMessage;
std::unique_ptr<TopicReopenBar> _topicReopenBar;
std::unique_ptr<EmptyPainter> _emptyPainter;
bool _skipScrollEvent = false;

View file

@ -1015,12 +1015,12 @@ void WebViewInstance::resolveApp(
void WebViewInstance::confirmOpen(Fn<void()> done) {
if (_bot->isVerified()
|| _session->local().isBotTrustedOpenWebView(_bot->id)) {
|| _session->local().isPeerTrustedOpenWebView(_bot->id)) {
done();
return;
}
const auto callback = [=](Fn<void()> close) {
_session->local().markBotTrustedOpenWebView(_bot->id);
_session->local().markPeerTrustedOpenWebView(_bot->id);
close();
done();
};
@ -1052,14 +1052,14 @@ void WebViewInstance::confirmAppOpen(
bool forceConfirmation) {
if (!forceConfirmation
&& (_bot->isVerified()
|| _session->local().isBotTrustedOpenWebView(_bot->id))) {
|| _session->local().isPeerTrustedOpenWebView(_bot->id))) {
done(writeAccess);
return;
}
_parentShow->show(Box([=](not_null<Ui::GenericBox*> box) {
const auto allowed = std::make_shared<Ui::Checkbox*>();
const auto callback = [=](Fn<void()> close) {
_session->local().markBotTrustedOpenWebView(_bot->id);
_session->local().markPeerTrustedOpenWebView(_bot->id);
done((*allowed) && (*allowed)->checked());
close();
};

View file

@ -131,7 +131,8 @@ namespace Media::Stories {
if (options.invertCaption) {
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
}
if (options.paidByStars) {
const auto starsPaid = peer->commitStarsForMessage();
if (starsPaid) {
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
const auto done = [=] {
@ -159,7 +160,7 @@ namespace Media::Stories {
MTP_inputPeerEmpty(),
Data::ShortcutIdToMTP(session, options.shortcutId),
MTP_long(options.effectId),
MTP_long(options.paidByStars)
MTP_long(starsPaid)
), [=](
const MTPUpdates &result,
const MTP::Response &response) {

View file

@ -904,7 +904,7 @@ void Form::submit() {
if (index < list.size() && password.isEmpty()) {
_updates.fire(TmpPasswordRequired{});
return;
} else if (!_session->local().isBotTrustedPayment(_details.botId)) {
} else if (!_session->local().isPeerTrustedPayment(_details.botId)) {
_updates.fire(BotTrustRequired{
.bot = _session->data().user(_details.botId),
.provider = _session->data().user(_details.providerId),
@ -1307,7 +1307,7 @@ void Form::acceptTerms() {
}
void Form::trustBot() {
_session->local().markBotTrustedPayment(_details.botId);
_session->local().markPeerTrustedPayment(_details.botId);
}
void Form::processShippingOptions(const QVector<MTPShippingOption> &data) {

View file

@ -2090,6 +2090,8 @@ void SmallBalanceBox(
return QString();
}, [&](SmallBalanceStarGift value) {
return owner->peer(value.recipientId)->shortName();
}, [&](SmallBalanceForMessage value) {
return owner->peer(value.recipientId)->shortName();
});
auto needed = show->session().credits().balanceValue(
@ -2128,6 +2130,11 @@ void SmallBalanceBox(
lt_user,
rpl::single(Ui::Text::Bold(name)),
Ui::Text::RichLangValue)
: v::is<SmallBalanceForMessage>(source)
? tr::lng_credits_small_balance_for_message(
lt_user,
rpl::single(Ui::Text::Bold(name)),
Ui::Text::RichLangValue)
: name.isEmpty()
? tr::lng_credits_small_balance_fallback(
Ui::Text::RichLangValue)

View file

@ -200,12 +200,16 @@ struct SmallBalanceDeepLink {
struct SmallBalanceStarGift {
PeerId recipientId;
};
struct SmallBalanceForMessage {
PeerId recipientId;
};
struct SmallBalanceSource : std::variant<
SmallBalanceBot,
SmallBalanceReaction,
SmallBalanceSubscription,
SmallBalanceDeepLink,
SmallBalanceStarGift> {
SmallBalanceStarGift,
SmallBalanceForMessage> {
using variant::variant;
};

View file

@ -86,7 +86,7 @@ enum { // Local Storage Keys
lskSavedGifsOld = 0x0e, // no data
lskSavedGifs = 0x0f, // no data
lskStickersKeys = 0x10, // no data
lskTrustedBots = 0x11, // no data
lskTrustedPeers = 0x11, // no data
lskFavedStickers = 0x12, // no data
lskExportSettings = 0x13, // no data
lskBackgroundOld = 0x14, // no data
@ -220,7 +220,7 @@ base::flat_set<QString> Account::collectGoodNames() const {
_legacyBackgroundKeyDay,
_recentHashtagsAndBotsKey,
_exportSettingsKey,
_trustedBotsKey,
_trustedPeersKey,
_installedMasksKey,
_recentMasksKey,
_archivedMasksKey,
@ -308,7 +308,7 @@ Account::ReadMapResult Account::readMapWith(
base::flat_map<PeerId, FileKey> draftsMap;
base::flat_map<PeerId, FileKey> draftCursorsMap;
base::flat_map<PeerId, bool> draftsNotReadMap;
quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedBotsKey = 0;
quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedPeersKey = 0;
quint64 recentStickersKeyOld = 0;
quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0;
quint64 installedMasksKey = 0, recentMasksKey = 0, archivedMasksKey = 0;
@ -371,8 +371,8 @@ Account::ReadMapResult Account::readMapWith(
map.stream >> reportSpamStatusesKey;
ClearKey(reportSpamStatusesKey, _basePath);
} break;
case lskTrustedBots: {
map.stream >> trustedBotsKey;
case lskTrustedPeers: {
map.stream >> trustedPeersKey;
} break;
case lskRecentStickersOld: {
map.stream >> recentStickersKeyOld;
@ -459,7 +459,7 @@ Account::ReadMapResult Account::readMapWith(
_draftsNotReadMap = draftsNotReadMap;
_locationsKey = locationsKey;
_trustedBotsKey = trustedBotsKey;
_trustedPeersKey = trustedPeersKey;
_recentStickersKeyOld = recentStickersKeyOld;
_installedStickersKey = installedStickersKey;
_featuredStickersKey = featuredStickersKey;
@ -573,7 +573,7 @@ void Account::writeMap() {
if (!_draftsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
if (!_draftCursorsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftCursorsMap.size() * sizeof(quint64) * 2;
if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_trustedBotsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_trustedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64);
if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) {
mapSize += sizeof(quint32) + 4 * sizeof(quint64);
@ -619,8 +619,8 @@ void Account::writeMap() {
if (_locationsKey) {
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
}
if (_trustedBotsKey) {
mapData.stream << quint32(lskTrustedBots) << quint64(_trustedBotsKey);
if (_trustedPeersKey) {
mapData.stream << quint32(lskTrustedPeers) << quint64(_trustedPeersKey);
}
if (_recentStickersKeyOld) {
mapData.stream << quint32(lskRecentStickersOld) << quint64(_recentStickersKeyOld);
@ -693,7 +693,7 @@ void Account::reset() {
_draftsMap.clear();
_draftCursorsMap.clear();
_draftsNotReadMap.clear();
_locationsKey = _trustedBotsKey = 0;
_locationsKey = _trustedPeersKey = 0;
_recentStickersKeyOld = 0;
_installedStickersKey = 0;
_featuredStickersKey = 0;
@ -3147,47 +3147,47 @@ void Account::readSelf(
}
}
void Account::writeTrustedBots() {
if (_trustedBots.empty()) {
if (_trustedBotsKey) {
ClearKey(_trustedBotsKey, _basePath);
_trustedBotsKey = 0;
void Account::writeTrustedPeers() {
if (_trustedPeers.empty()) {
if (_trustedPeersKey) {
ClearKey(_trustedPeersKey, _basePath);
_trustedPeersKey = 0;
writeMapDelayed();
}
return;
}
if (!_trustedBotsKey) {
_trustedBotsKey = GenerateKey(_basePath);
if (!_trustedPeersKey) {
_trustedPeersKey = GenerateKey(_basePath);
writeMapQueued();
}
quint32 size = sizeof(qint32) + _trustedBots.size() * sizeof(quint64);
quint32 size = sizeof(qint32) + _trustedPeers.size() * sizeof(quint64);
EncryptedDescriptor data(size);
data.stream << qint32(_trustedBots.size());
for (const auto &[peerId, mask] : _trustedBots) {
// value: 8 bit mask, 56 bit bot peer_id.
data.stream << qint32(_trustedPeers.size());
for (const auto &[peerId, mask] : _trustedPeers) {
// value: 8 bit mask, 56 bit peer_id.
auto value = SerializePeerId(peerId);
Assert((value >> 56) == 0);
value |= (quint64(mask) << 56);
data.stream << value;
}
FileWriteDescriptor file(_trustedBotsKey, _basePath);
FileWriteDescriptor file(_trustedPeersKey, _basePath);
file.writeEncrypted(data, _localKey);
}
void Account::readTrustedBots() {
if (_trustedBotsRead) {
void Account::readTrustedPeers() {
if (_trustedPeersRead) {
return;
}
_trustedBotsRead = true;
if (!_trustedBotsKey) {
_trustedPeersRead = true;
if (!_trustedPeersKey) {
return;
}
FileReadDescriptor trusted;
if (!ReadEncryptedFile(trusted, _trustedBotsKey, _basePath, _localKey)) {
ClearKey(_trustedBotsKey, _basePath);
_trustedBotsKey = 0;
if (!ReadEncryptedFile(trusted, _trustedPeersKey, _basePath, _localKey)) {
ClearKey(_trustedPeersKey, _basePath);
_trustedPeersKey = 0;
writeMapDelayed();
return;
}
@ -3197,76 +3197,106 @@ void Account::readTrustedBots() {
for (int i = 0; i < size; ++i) {
auto value = quint64();
trusted.stream >> value;
const auto mask = base::flags<BotTrustFlag>::from_raw(
const auto mask = base::flags<PeerTrustFlag>::from_raw(
uchar(value >> 56));
const auto peerIdSerialized = value & ~(0xFFULL << 56);
const auto peerId = DeserializePeerId(peerIdSerialized);
_trustedBots.emplace(peerId, mask);
_trustedPeers.emplace(peerId, mask);
}
}
void Account::markBotTrustedOpenGame(PeerId botId) {
if (isBotTrustedOpenGame(botId)) {
void Account::markPeerTrustedOpenGame(PeerId peerId) {
if (isPeerTrustedOpenGame(peerId)) {
return;
}
const auto i = _trustedBots.find(botId);
if (i == end(_trustedBots)) {
_trustedBots.emplace(botId, BotTrustFlag());
const auto i = _trustedPeers.find(peerId);
if (i == end(_trustedPeers)) {
_trustedPeers.emplace(peerId, PeerTrustFlag());
} else {
i->second &= ~BotTrustFlag::NoOpenGame;
i->second &= ~PeerTrustFlag::NoOpenGame;
}
writeTrustedBots();
writeTrustedPeers();
}
bool Account::isBotTrustedOpenGame(PeerId botId) {
readTrustedBots();
const auto i = _trustedBots.find(botId);
return (i != end(_trustedBots))
&& ((i->second & BotTrustFlag::NoOpenGame) == 0);
bool Account::isPeerTrustedOpenGame(PeerId peerId) {
readTrustedPeers();
const auto i = _trustedPeers.find(peerId);
return (i != end(_trustedPeers))
&& ((i->second & PeerTrustFlag::NoOpenGame) == 0);
}
void Account::markBotTrustedPayment(PeerId botId) {
if (isBotTrustedPayment(botId)) {
void Account::markPeerTrustedPayment(PeerId peerId) {
if (isPeerTrustedPayment(peerId)) {
return;
}
const auto i = _trustedBots.find(botId);
if (i == end(_trustedBots)) {
_trustedBots.emplace(
botId,
BotTrustFlag::NoOpenGame | BotTrustFlag::Payment);
const auto i = _trustedPeers.find(peerId);
if (i == end(_trustedPeers)) {
_trustedPeers.emplace(
peerId,
PeerTrustFlag::NoOpenGame | PeerTrustFlag::Payment);
} else {
i->second |= BotTrustFlag::Payment;
i->second |= PeerTrustFlag::Payment;
}
writeTrustedBots();
writeTrustedPeers();
}
bool Account::isBotTrustedPayment(PeerId botId) {
readTrustedBots();
const auto i = _trustedBots.find(botId);
return (i != end(_trustedBots))
&& ((i->second & BotTrustFlag::Payment) != 0);
bool Account::isPeerTrustedPayment(PeerId peerId) {
readTrustedPeers();
const auto i = _trustedPeers.find(peerId);
return (i != end(_trustedPeers))
&& ((i->second & PeerTrustFlag::Payment) != 0);
}
void Account::markBotTrustedOpenWebView(PeerId botId) {
if (isBotTrustedOpenWebView(botId)) {
void Account::markPeerTrustedOpenWebView(PeerId peerId) {
if (isPeerTrustedOpenWebView(peerId)) {
return;
}
const auto i = _trustedBots.find(botId);
if (i == end(_trustedBots)) {
_trustedBots.emplace(
botId,
BotTrustFlag::NoOpenGame | BotTrustFlag::OpenWebView);
const auto i = _trustedPeers.find(peerId);
if (i == end(_trustedPeers)) {
_trustedPeers.emplace(
peerId,
PeerTrustFlag::NoOpenGame | PeerTrustFlag::OpenWebView);
} else {
i->second |= BotTrustFlag::OpenWebView;
i->second |= PeerTrustFlag::OpenWebView;
}
writeTrustedBots();
writeTrustedPeers();
}
bool Account::isBotTrustedOpenWebView(PeerId botId) {
readTrustedBots();
const auto i = _trustedBots.find(botId);
return (i != end(_trustedBots))
&& ((i->second & BotTrustFlag::OpenWebView) != 0);
bool Account::isPeerTrustedOpenWebView(PeerId peerId) {
readTrustedPeers();
const auto i = _trustedPeers.find(peerId);
return (i != end(_trustedPeers))
&& ((i->second & PeerTrustFlag::OpenWebView) != 0);
}
void Account::markPeerTrustedPayForMessage(PeerId peerId) {
if (isPeerTrustedPayForMessage(peerId)) {
return;
}
const auto i = _trustedPeers.find(peerId);
if (i == end(_trustedPeers)) {
_trustedPeers.emplace(
peerId,
PeerTrustFlag::NoOpenGame | PeerTrustFlag::PayForMessage);
} else {
i->second |= PeerTrustFlag::PayForMessage;
}
writeTrustedPeers();
}
bool Account::isPeerTrustedPayForMessage(PeerId peerId) {
readTrustedPeers();
const auto i = _trustedPeers.find(peerId);
return (i != end(_trustedPeers))
&& ((i->second & PeerTrustFlag::PayForMessage) != 0);
}
void Account::clearPeerTrusted(PeerId peerId) {
const auto i = _trustedPeers.find(peerId);
if (i != end(_trustedPeers)) {
_trustedPeers.erase(i);
writeTrustedPeers();
}
}
void Account::enforceModernStorageIdBots() {

View file

@ -167,12 +167,15 @@ public:
const QByteArray& serialized,
int32 streamVersion);
void markBotTrustedOpenGame(PeerId botId);
[[nodiscard]] bool isBotTrustedOpenGame(PeerId botId);
void markBotTrustedPayment(PeerId botId);
[[nodiscard]] bool isBotTrustedPayment(PeerId botId);
void markBotTrustedOpenWebView(PeerId botId);
[[nodiscard]] bool isBotTrustedOpenWebView(PeerId botId);
void markPeerTrustedOpenGame(PeerId peerId);
[[nodiscard]] bool isPeerTrustedOpenGame(PeerId peerId);
void markPeerTrustedPayment(PeerId peerId);
[[nodiscard]] bool isPeerTrustedPayment(PeerId peerId);
void markPeerTrustedOpenWebView(PeerId peerId);
[[nodiscard]] bool isPeerTrustedOpenWebView(PeerId peerId);
void markPeerTrustedPayForMessage(PeerId peerId);
[[nodiscard]] bool isPeerTrustedPayForMessage(PeerId peerId);
void clearPeerTrusted(PeerId peerId);
void enforceModernStorageIdBots();
[[nodiscard]] Webview::StorageId resolveStorageIdBots();
@ -203,12 +206,13 @@ private:
IncorrectPasscode,
Failed,
};
enum class BotTrustFlag : uchar {
enum class PeerTrustFlag : uchar {
NoOpenGame = (1 << 0),
Payment = (1 << 1),
OpenWebView = (1 << 2),
PayForMessage = (1 << 3),
};
friend inline constexpr bool is_flag_type(BotTrustFlag) { return true; };
friend inline constexpr bool is_flag_type(PeerTrustFlag) { return true; };
[[nodiscard]] base::flat_set<QString> collectGoodNames() const;
[[nodiscard]] auto prepareReadSettingsContext() const
@ -261,8 +265,8 @@ private:
Data::StickersSetFlags readingFlags = 0);
void importOldRecentStickers();
void readTrustedBots();
void writeTrustedBots();
void readTrustedPeers();
void writeTrustedPeers();
void readMediaLastPlaybackPositions();
void writeMediaLastPlaybackPositions();
@ -295,7 +299,7 @@ private:
Fn<std::optional<QByteArray>()> _downloadsSerialize;
FileKey _locationsKey = 0;
FileKey _trustedBotsKey = 0;
FileKey _trustedPeersKey = 0;
FileKey _installedStickersKey = 0;
FileKey _featuredStickersKey = 0;
FileKey _recentStickersKey = 0;
@ -324,8 +328,8 @@ private:
qint32 _cacheTotalTimeLimit = 0;
qint32 _cacheBigFileTotalTimeLimit = 0;
base::flat_map<PeerId, base::flags<BotTrustFlag>> _trustedBots;
bool _trustedBotsRead = false;
base::flat_map<PeerId, base::flags<PeerTrustFlag>> _trustedPeers;
bool _trustedPeersRead = false;
bool _readingUserSettings = false;
bool _recentHashtagsAndBotsWereRead = false;
bool _searchSuggestionsRead = false;