// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2026 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/StarGiftManager.h" #include "td/telegram/AccessRights.h" #include "td/telegram/AnimationsManager.h" #include "td/telegram/AuthManager.h" #include "td/telegram/BusinessConnectionManager.h" #include "td/telegram/ChatManager.h" #include "td/telegram/DialogId.h" #include "td/telegram/DialogManager.h" #include "td/telegram/Document.h" #include "td/telegram/DocumentsManager.h" #include "td/telegram/EmojiStatus.h" #include "td/telegram/Global.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessageSender.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/OnlineManager.h" #include "td/telegram/OptionManager.h" #include "td/telegram/PasswordManager.h" #include "td/telegram/StarGift.h" #include "td/telegram/StarGiftAttribute.h" #include "td/telegram/StarGiftAttributeId.h" #include "td/telegram/StarGiftCollection.h" #include "td/telegram/StarManager.h" #include "td/telegram/StateManager.h" #include "td/telegram/Td.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UpdatesManager.h" #include "td/telegram/UserManager.h" #include "td/telegram/UserStarGift.h" #include "td/utils/algorithm.h" #include "td/utils/buffer.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include namespace td { class GetStarGiftsQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetStarGiftsQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send() { send_query(G()->net_query_creator().create(telegram_api::payments_getStarGifts(0))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(DEBUG) << "Receive result for GetStarGiftsQuery: " << to_string(ptr); if (ptr->get_id() != telegram_api::payments_starGifts::ID) { LOG(ERROR) << "Receive " << to_string(ptr); return promise_.set_error(500, "Receive unexpected response"); } auto results = telegram_api::move_object_as(ptr); td_->user_manager_->on_get_users(std::move(results->users_), "GetStarGiftsQuery"); td_->chat_manager_->on_get_chats(std::move(results->chats_), "GetStarGiftsQuery"); vector> options; for (auto &gift : results->gifts_) { int64 availability_resale = 0; int64 resell_min_stars = 0; string title; if (gift->get_id() == telegram_api::starGift::ID) { auto star_gift = static_cast(gift.get()); if (td_->auth_manager_->is_bot()) { if (star_gift->availability_total_ > 0 && star_gift->availability_remains_ == 0) { continue; } if (star_gift->per_user_total_ > 0 && star_gift->per_user_remains_ == 0) { continue; } if (star_gift->require_premium_) { continue; } if (star_gift->auction_) { continue; } if (star_gift->locked_until_date_ > G()->unix_time()) { continue; } } else { availability_resale = star_gift->availability_resale_; resell_min_stars = StarManager::get_star_count(star_gift->resell_min_stars_); title = star_gift->title_; if (availability_resale < 0 || availability_resale > 1000000000) { LOG(ERROR) << "Receive " << availability_resale << " available gifts"; availability_resale = 0; } else if (resell_min_stars == 0 && availability_resale > 0) { LOG(ERROR) << "Receive " << availability_resale << " available gifts with the minimum price of " << resell_min_stars; availability_resale = 0; } if (availability_resale == 0) { resell_min_stars = 0; title.clear(); } } } StarGift star_gift(td_, std::move(gift), false); if (!star_gift.is_valid()) { continue; } td_->star_gift_manager_->on_get_star_gift(star_gift, true); options.push_back(td_api::make_object( star_gift.get_gift_object(td_), narrow_cast(availability_resale), resell_min_stars, title)); } promise_.set_value(td_api::make_object(std::move(options))); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class CheckCanSendGiftQuery final : public Td::ResultHandler { Promise> promise_; public: explicit CheckCanSendGiftQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(int64 gift_id) { send_query(G()->net_query_creator().create(telegram_api::payments_checkCanSendGift(gift_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(DEBUG) << "Receive result for CheckCanSendGiftQuery: " << to_string(ptr); switch (ptr->get_id()) { case telegram_api::payments_checkCanSendGiftResultOk::ID: return promise_.set_value(td_api::make_object()); case telegram_api::payments_checkCanSendGiftResultFail::ID: { auto result = telegram_api::move_object_as(ptr); auto reason = get_formatted_text(td_->user_manager_.get(), std::move(result->reason_), true, false, "CheckCanSendGiftQuery"); return promise_.set_value(td_api::make_object( get_formatted_text_object(td_->user_manager_.get(), reason, true, true))); } default: UNREACHABLE(); } } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class SendGiftQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; public: explicit SendGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; send_query(G()->net_query_creator().create( telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_result = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for SendGiftQuery: " << to_string(payment_result); switch (payment_result->get_id()) { case telegram_api::payments_paymentResult::ID: { auto result = telegram_api::move_object_as(payment_result); td_->star_manager_->add_pending_owned_star_count(star_count_, true); td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise_)); break; } case telegram_api::payments_paymentVerificationNeeded::ID: LOG(ERROR) << "Receive " << to_string(payment_result); return on_error(Status::Error(500, "Receive invalid response")); default: UNREACHABLE(); } } void on_error(Status status) final { if (status.message() == "FORM_SUBMIT_DUPLICATE") { LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; } td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class GetGiftPaymentFormQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; telegram_api::object_ptr send_input_invoice_; public: explicit GetGiftPaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, telegram_api::object_ptr send_input_invoice, int64 star_count) { send_input_invoice_ = std::move(send_input_invoice); star_count_ = star_count; td_->star_manager_->add_pending_owned_star_count(-star_count, false); send_query( G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_form_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetGiftPaymentFormQuery: " << to_string(payment_form_ptr); switch (payment_form_ptr->get_id()) { case telegram_api::payments_paymentForm::ID: case telegram_api::payments_paymentFormStars::ID: LOG(ERROR) << "Receive " << to_string(payment_form_ptr); td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(500, "Unsupported"); break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); if (!td_->auth_manager_->is_bot()) { if (payment_form->invoice_->prices_.size() != 1u || payment_form->invoice_->prices_[0]->amount_ > star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_, false); return promise_.set_error(400, "Wrong gift price specified"); } if (payment_form->invoice_->prices_[0]->amount_ != star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_ - payment_form->invoice_->prices_[0]->amount_, false); star_count_ = payment_form->invoice_->prices_[0]->amount_; } } td_->create_handler(std::move(promise_)) ->send(std::move(send_input_invoice_), payment_form->form_id_, star_count_); break; } default: UNREACHABLE(); } } void on_error(Status status) final { td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class PutGiftAuctionBidQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; public: explicit PutGiftAuctionBidQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; send_query(G()->net_query_creator().create( telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_result = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for PutGiftAuctionBidQuery: " << to_string(payment_result); switch (payment_result->get_id()) { case telegram_api::payments_paymentResult::ID: { auto result = telegram_api::move_object_as(payment_result); td_->star_manager_->add_pending_owned_star_count(star_count_, true); td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise_)); break; } case telegram_api::payments_paymentVerificationNeeded::ID: LOG(ERROR) << "Receive " << to_string(payment_result); return on_error(Status::Error(500, "Receive invalid response")); default: UNREACHABLE(); } } void on_error(Status status) final { if (status.message() == "FORM_SUBMIT_DUPLICATE") { LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; } td_->star_manager_->add_pending_owned_star_count(star_count_, false); td_->star_manager_->reload_owned_star_count(); promise_.set_error(std::move(status)); } }; class GetGiftAuctionBidPaymentFormQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; int64 old_star_count_; telegram_api::object_ptr send_input_invoice_; public: explicit GetGiftAuctionBidPaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, telegram_api::object_ptr send_input_invoice, int64 star_count, int64 old_star_count) { send_input_invoice_ = std::move(send_input_invoice); star_count_ = star_count; old_star_count_ = old_star_count; td_->star_manager_->add_pending_owned_star_count(old_star_count - star_count, false); send_query( G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_form_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetGiftAuctionBidPaymentFormQuery: " << to_string(payment_form_ptr); switch (payment_form_ptr->get_id()) { case telegram_api::payments_paymentForm::ID: case telegram_api::payments_paymentFormStars::ID: LOG(ERROR) << "Receive " << to_string(payment_form_ptr); td_->star_manager_->add_pending_owned_star_count(star_count_ - old_star_count_, false); promise_.set_error(500, "Unsupported"); break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); if (!td_->auth_manager_->is_bot()) { if (payment_form->invoice_->prices_.size() != 1u || payment_form->invoice_->prices_[0]->amount_ != star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_ - old_star_count_, false); return promise_.set_error(500, "Receive invalid payment form"); } } td_->create_handler(std::move(promise_)) ->send(std::move(send_input_invoice_), payment_form->form_id_, star_count_); break; } default: UNREACHABLE(); } } void on_error(Status status) final { td_->star_manager_->add_pending_owned_star_count(star_count_ - old_star_count_, false); promise_.set_error(std::move(status)); } }; class GetStarGiftAuctionStateQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetStarGiftAuctionStateQuery( Promise> &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_auction, int32 version) { send_query(G()->net_query_creator().create( telegram_api::payments_getStarGiftAuctionState(std::move(input_auction), version))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetStarGiftAuctionStateQuery: " << to_string(ptr); promise_.set_value(std::move(ptr)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetStarGiftActiveAuctionsQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetStarGiftActiveAuctionsQuery( Promise> &&promise) : promise_(std::move(promise)) { } void send() { send_query(G()->net_query_creator().create(telegram_api::payments_getStarGiftActiveAuctions(0))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetStarGiftActiveAuctionsQuery: " << to_string(ptr); promise_.set_value(std::move(ptr)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetStarGiftAuctionAcquiredGiftsQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetStarGiftAuctionAcquiredGiftsQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(int64 gift_id) { send_query(G()->net_query_creator().create(telegram_api::payments_getStarGiftAuctionAcquiredGifts(gift_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetStarGiftAuctionAcquiredGiftsQuery: " << to_string(ptr); td_->user_manager_->on_get_users(std::move(ptr->users_), "GetStarGiftAuctionAcquiredGiftsQuery"); td_->chat_manager_->on_get_chats(std::move(ptr->chats_), "GetStarGiftAuctionAcquiredGiftsQuery"); vector> gifts; for (auto &gift : ptr->gifts_) { auto text = get_formatted_text(td_->user_manager_.get(), std::move(gift->message_), true, false, "giftAuctionAcquiredGift"); gifts.push_back(td_api::make_object( get_message_sender_object(td_, DialogId(gift->peer_), "giftAuctionAcquiredGift"), gift->date_, StarManager::get_star_count(gift->bid_amount_), gift->round_, gift->pos_, gift->gift_num_, get_formatted_text_object(td_->user_manager_.get(), text, true, -1), gift->name_hidden_)); } promise_.set_value(td_api::make_object(std::move(gifts))); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class ConvertStarGiftQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; public: explicit ConvertStarGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, DialogId dialog_id) { dialog_id_ = dialog_id; auto input_gift = star_gift_id.get_input_saved_star_gift(td_); CHECK(input_gift != nullptr); send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_convertStarGift(std::move(input_gift)), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } if (dialog_id_ == td_->dialog_manager_->get_my_dialog_id()) { td_->user_manager_->reload_user_full(td_->user_manager_->get_my_id(), std::move(promise_), "ConvertStarGiftQuery"); } else if (dialog_id_.get_type() == DialogType::Channel) { td_->chat_manager_->reload_channel_full(dialog_id_.get_channel_id(), std::move(promise_), "ConvertStarGiftQuery"); } else { promise_.set_value(Unit()); } } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ConvertStarGiftQuery"); promise_.set_error(std::move(status)); } }; class SaveStarGiftQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; bool is_saved_; public: explicit SaveStarGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(StarGiftId star_gift_id, bool is_saved) { dialog_id_ = star_gift_id.get_dialog_id(td_); is_saved_ = is_saved; send_query(G()->net_query_creator().create( telegram_api::payments_saveStarGift(0, !is_saved, star_gift_id.get_input_saved_star_gift(td_)), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } promise_.set_value(Unit()); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "SaveStarGiftQuery"); promise_.set_error(std::move(status)); } }; class ToggleStarGiftsPinnedToTopQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; public: explicit ToggleStarGiftsPinnedToTopQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, const vector &star_gift_ids) { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create( telegram_api::payments_toggleStarGiftsPinnedToTop(std::move(input_peer), StarGiftId::get_input_saved_star_gifts(td_, star_gift_ids)), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } promise_.set_value(Unit()); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ToggleStarGiftsPinnedToTopQuery"); promise_.set_error(std::move(status)); } }; class ToggleChatStarGiftNotificationsQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; public: explicit ToggleChatStarGiftNotificationsQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, bool are_enabled) { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create( telegram_api::payments_toggleChatStarGiftNotifications(0, are_enabled, std::move(input_peer)), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } promise_.set_value(Unit()); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ToggleChatStarGiftNotificationsQuery"); promise_.set_error(std::move(status)); } }; class GetUpgradeGiftPreviewQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetUpgradeGiftPreviewQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(int64 gift_id) { send_query(G()->net_query_creator().create(telegram_api::payments_getStarGiftUpgradePreview(gift_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetUpgradeGiftPreviewQuery: " << to_string(ptr); auto result = td_api::make_object(); for (auto &attribute : ptr->sample_attributes_) { switch (attribute->get_id()) { case telegram_api::starGiftAttributeModel::ID: { auto model = StarGiftAttributeSticker( td_, telegram_api::move_object_as(attribute)); if (!model.is_valid()) { LOG(ERROR) << "Receive invalid model"; } else { result->models_.push_back(model.get_upgraded_gift_model_object(td_)); } break; } case telegram_api::starGiftAttributePattern::ID: { auto pattern = StarGiftAttributeSticker( td_, telegram_api::move_object_as(attribute)); if (!pattern.is_valid()) { LOG(ERROR) << "Receive invalid symbol"; } else { result->symbols_.push_back(pattern.get_upgraded_gift_symbol_object(td_)); } break; } case telegram_api::starGiftAttributeBackdrop::ID: { auto backdrop = StarGiftAttributeBackdrop( telegram_api::move_object_as(attribute)); if (!backdrop.is_valid()) { LOG(ERROR) << "Receive invalid backdrop"; } else { result->backdrops_.push_back(backdrop.get_upgraded_gift_backdrop_object()); } break; } case telegram_api::starGiftAttributeOriginalDetails::ID: LOG(ERROR) << "Receive unexpected original details"; break; default: UNREACHABLE(); } } for (auto &price : ptr->prices_) { result->prices_.push_back(td_api::make_object( price->date_, StarManager::get_star_count(price->upgrade_stars_))); } for (auto &price : ptr->next_prices_) { result->next_prices_.push_back(td_api::make_object( price->date_, StarManager::get_star_count(price->upgrade_stars_))); } promise_.set_value(std::move(result)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetStarGiftUpgradeAttributesQuery final : public Td::ResultHandler { Promise> promise_; bool return_upgrade_models_; bool return_craft_models_; public: explicit GetStarGiftUpgradeAttributesQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(int64 gift_id, bool return_upgrade_models, bool return_craft_models) { return_upgrade_models_ = return_upgrade_models; return_craft_models_ = return_craft_models; send_query(G()->net_query_creator().create(telegram_api::payments_getStarGiftUpgradeAttributes(gift_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetStarGiftUpgradeAttributesQuery: " << to_string(ptr); auto result = td_api::make_object(); for (auto &attribute : ptr->attributes_) { switch (attribute->get_id()) { case telegram_api::starGiftAttributeModel::ID: { auto model = StarGiftAttributeSticker( td_, telegram_api::move_object_as(attribute)); if (!model.is_valid()) { LOG(ERROR) << "Receive invalid model"; } else if ((!model.is_crafted() && return_upgrade_models_) || (model.is_crafted() && return_craft_models_)) { result->models_.push_back(model.get_upgraded_gift_model_object(td_)); } break; } case telegram_api::starGiftAttributePattern::ID: { auto pattern = StarGiftAttributeSticker( td_, telegram_api::move_object_as(attribute)); if (!pattern.is_valid()) { LOG(ERROR) << "Receive invalid symbol"; } else { result->symbols_.push_back(pattern.get_upgraded_gift_symbol_object(td_)); } break; } case telegram_api::starGiftAttributeBackdrop::ID: { auto backdrop = StarGiftAttributeBackdrop( telegram_api::move_object_as(attribute)); if (!backdrop.is_valid()) { LOG(ERROR) << "Receive invalid backdrop"; } else { result->backdrops_.push_back(backdrop.get_upgraded_gift_backdrop_object()); } break; } case telegram_api::starGiftAttributeOriginalDetails::ID: LOG(ERROR) << "Receive unexpected original details"; break; default: UNREACHABLE(); } } promise_.set_value(std::move(result)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; static Promise get_gift_upgrade_promise(Td *td, const telegram_api::object_ptr &updates, Promise> &&promise) { if (td->auth_manager_->is_bot()) { return PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } promise.set_value(td_api::make_object()); }); } vector> new_messages = UpdatesManager::get_new_messages(updates.get()); td::remove_if(new_messages, [](const auto &p) { if (p.first->get_id() != telegram_api::messageService::ID) { return false; } auto message = static_cast(p.first); if (message->action_->get_id() != telegram_api::messageActionStarGiftUnique::ID) { return false; } auto action = static_cast(message->action_.get()); return action->prepaid_upgrade_; }); if (new_messages.size() != 1u || new_messages[0].second || new_messages[0].first->get_id() != telegram_api::messageService::ID) { promise.set_error(500, "Receive invalid server response"); return Auto(); } auto message = static_cast(new_messages[0].first); if (message->action_->get_id() != telegram_api::messageActionStarGiftUnique::ID) { promise.set_error(500, "Receive invalid server response"); return Auto(); } auto action = static_cast(message->action_.get()); if (!action->upgrade_ || action->transferred_ || action->refunded_ || action->gift_->get_id() != telegram_api::starGiftUnique::ID) { promise.set_error(500, "Receive invalid server response"); return Auto(); } auto message_full_id = MessageFullId::get_message_full_id(new_messages[0].first, false); return PromiseCreator::lambda([message_full_id, promise = std::move(promise)](Result result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(G()->messages_manager(), &MessagesManager::finish_gift_upgrade, message_full_id, std::move(promise)); }); } class UpgradeStarGiftQuery final : public Td::ResultHandler { Promise> promise_; public: explicit UpgradeStarGiftQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, bool keep_original_details) { auto input_gift = star_gift_id.get_input_saved_star_gift(td_); CHECK(input_gift != nullptr); send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_upgradeStarGift(0, keep_original_details, std::move(input_gift)), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for UpgradeStarGiftQuery: " << to_string(ptr); auto promise = get_gift_upgrade_promise(td_, ptr, std::move(promise_)); td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise)); get_upgraded_gift_emoji_statuses(td_, Auto()); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class UpgradeGiftQuery final : public Td::ResultHandler { Promise> promise_; int64 star_count_; public: explicit UpgradeGiftQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_result = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for UpgradeGiftQuery: " << to_string(payment_result); switch (payment_result->get_id()) { case telegram_api::payments_paymentResult::ID: { auto result = telegram_api::move_object_as(payment_result); td_->star_manager_->add_pending_owned_star_count(star_count_, true); auto promise = get_gift_upgrade_promise(td_, result->updates_, std::move(promise_)); td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise)); break; } case telegram_api::payments_paymentVerificationNeeded::ID: LOG(ERROR) << "Receive " << to_string(payment_result); return on_error(Status::Error(500, "Receive invalid response")); default: UNREACHABLE(); } get_upgraded_gift_emoji_statuses(td_, Auto()); } void on_error(Status status) final { if (status.message() == "FORM_SUBMIT_DUPLICATE") { LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; } td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class GetGiftUpgradePaymentFormQuery final : public Td::ResultHandler { Promise> promise_; BusinessConnectionId business_connection_id_; int64 star_count_; telegram_api::object_ptr upgrade_input_invoice_; public: explicit GetGiftUpgradePaymentFormQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, telegram_api::object_ptr input_invoice, telegram_api::object_ptr upgrade_input_invoice, int64 star_count) { business_connection_id_ = business_connection_id; upgrade_input_invoice_ = std::move(upgrade_input_invoice); star_count_ = star_count; td_->star_manager_->add_pending_owned_star_count(-star_count, false); send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_form_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetGiftUpgradePaymentFormQuery: " << to_string(payment_form_ptr); switch (payment_form_ptr->get_id()) { case telegram_api::payments_paymentForm::ID: case telegram_api::payments_paymentFormStars::ID: LOG(ERROR) << "Receive " << to_string(payment_form_ptr); td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(500, "Unsupported"); break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); if (payment_form->invoice_->prices_.size() != 1u || payment_form->invoice_->prices_[0]->amount_ > star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_, false); return promise_.set_error(400, "Wrong upgrade price specified"); } if (payment_form->invoice_->prices_[0]->amount_ != star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_ - payment_form->invoice_->prices_[0]->amount_, false); star_count_ = payment_form->invoice_->prices_[0]->amount_; } td_->create_handler(std::move(promise_)) ->send(business_connection_id_, std::move(upgrade_input_invoice_), payment_form->form_id_, star_count_); break; } default: UNREACHABLE(); } } void on_error(Status status) final { td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class PrepaidUpgradeGiftQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; public: explicit PrepaidUpgradeGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; send_query(G()->net_query_creator().create( telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_result = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for PrepaidUpgradeGiftQuery: " << to_string(payment_result); switch (payment_result->get_id()) { case telegram_api::payments_paymentResult::ID: { auto result = telegram_api::move_object_as(payment_result); td_->star_manager_->add_pending_owned_star_count(star_count_, true); td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise_)); break; } case telegram_api::payments_paymentVerificationNeeded::ID: LOG(ERROR) << "Receive " << to_string(payment_result); return on_error(Status::Error(500, "Receive invalid response")); default: UNREACHABLE(); } get_upgraded_gift_emoji_statuses(td_, Auto()); } void on_error(Status status) final { if (status.message() == "FORM_SUBMIT_DUPLICATE") { LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; } td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class GetGiftPrepaidUpgradePaymentFormQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; telegram_api::object_ptr upgrade_input_invoice_; public: explicit GetGiftPrepaidUpgradePaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, telegram_api::object_ptr upgrade_input_invoice, int64 star_count) { upgrade_input_invoice_ = std::move(upgrade_input_invoice); star_count_ = star_count; td_->star_manager_->add_pending_owned_star_count(-star_count, false); send_query( G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_form_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetGiftPrepaidUpgradePaymentFormQuery: " << to_string(payment_form_ptr); switch (payment_form_ptr->get_id()) { case telegram_api::payments_paymentForm::ID: case telegram_api::payments_paymentFormStars::ID: LOG(ERROR) << "Receive " << to_string(payment_form_ptr); td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(500, "Unsupported"); break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); if (payment_form->invoice_->prices_.size() != 1u || payment_form->invoice_->prices_[0]->amount_ > star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_, false); return promise_.set_error(400, "Wrong upgrade price specified"); } if (payment_form->invoice_->prices_[0]->amount_ != star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_ - payment_form->invoice_->prices_[0]->amount_, false); star_count_ = payment_form->invoice_->prices_[0]->amount_; } td_->create_handler(std::move(promise_)) ->send(std::move(upgrade_input_invoice_), payment_form->form_id_, star_count_); break; } default: UNREACHABLE(); } } void on_error(Status status) final { td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; static Promise get_gift_craft_promise(Td *td, telegram_api::object_ptr &updates, Promise> &&promise) { if (UpdatesManager::extract_star_gift_craft_fail(updates.get())) { return PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } promise.set_value(td_api::make_object()); }); } vector> new_messages = UpdatesManager::get_new_messages(updates.get()); if (new_messages.size() != 1u || new_messages[0].second || new_messages[0].first->get_id() != telegram_api::messageService::ID) { promise.set_error(500, "Receive invalid server response"); return Auto(); } auto message = static_cast(new_messages[0].first); auto action_id = message->action_->get_id(); if (action_id != telegram_api::messageActionStarGiftUnique::ID) { promise.set_error(500, "Receive invalid server response"); return Auto(); } auto action = static_cast(message->action_.get()); if (!action->craft_ || action->transferred_ || action->refunded_ || action->gift_->get_id() != telegram_api::starGiftUnique::ID) { promise.set_error(500, "Receive invalid server response"); return Auto(); } auto message_full_id = MessageFullId::get_message_full_id(new_messages[0].first, false); return PromiseCreator::lambda([message_full_id, promise = std::move(promise)](Result result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(G()->messages_manager(), &MessagesManager::finish_gift_craft, message_full_id, std::move(promise)); }); } class CraftStarGiftQuery final : public Td::ResultHandler { Promise> promise_; public: explicit CraftStarGiftQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(const vector &star_gift_ids) { send_query(G()->net_query_creator().create( telegram_api::payments_craftStarGift(StarGiftId::get_input_saved_star_gifts(td_, star_gift_ids)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for CraftStarGiftQuery: " << to_string(ptr); auto promise = get_gift_craft_promise(td_, ptr, std::move(promise_)); td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise)); get_upgraded_gift_emoji_statuses(td_, Auto()); } void on_error(Status status) final { Slice too_early_prefix = "STARGIFT_CRAFT_TOO_EARLY_"; if (begins_with(status.message(), too_early_prefix)) { return promise_.set_value(td_api::make_object( to_integer(status.message().substr(too_early_prefix.size())))); } if (status.message() == "STARGIFT_CRAFT_UNAVAILABLE" || status.message() == "STARGIFT_ALREADY_CRAFTED") { return promise_.set_value(td_api::make_object()); } promise_.set_error(std::move(status)); } }; class TransferStarGiftQuery final : public Td::ResultHandler { Promise promise_; public: explicit TransferStarGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, telegram_api::object_ptr receiver_input_peer) { auto input_gift = star_gift_id.get_input_saved_star_gift(td_); CHECK(input_gift != nullptr); send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_transferStarGift(std::move(input_gift), std::move(receiver_input_peer)), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for TransferStarGiftQuery: " << to_string(ptr); td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); get_upgraded_gift_emoji_statuses(td_, Auto()); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class TransferGiftQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; public: explicit TransferGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_result = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for TransferGiftQuery: " << to_string(payment_result); switch (payment_result->get_id()) { case telegram_api::payments_paymentResult::ID: { auto result = telegram_api::move_object_as(payment_result); td_->star_manager_->add_pending_owned_star_count(star_count_, true); td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise_)); break; } case telegram_api::payments_paymentVerificationNeeded::ID: LOG(ERROR) << "Receive " << to_string(payment_result); return on_error(Status::Error(500, "Receive invalid response")); default: UNREACHABLE(); } get_upgraded_gift_emoji_statuses(td_, Auto()); } void on_error(Status status) final { if (status.message() == "FORM_SUBMIT_DUPLICATE") { LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; } td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class GetGiftTransferPaymentFormQuery final : public Td::ResultHandler { Promise promise_; BusinessConnectionId business_connection_id_; int64 star_count_; telegram_api::object_ptr transfer_input_invoice_; public: explicit GetGiftTransferPaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, telegram_api::object_ptr input_invoice, telegram_api::object_ptr transfer_input_invoice, int64 star_count) { business_connection_id_ = business_connection_id; transfer_input_invoice_ = std::move(transfer_input_invoice); star_count_ = star_count; td_->star_manager_->add_pending_owned_star_count(-star_count, false); send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_form_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetGiftTransferPaymentFormQuery: " << to_string(payment_form_ptr); switch (payment_form_ptr->get_id()) { case telegram_api::payments_paymentForm::ID: case telegram_api::payments_paymentFormStars::ID: LOG(ERROR) << "Receive " << to_string(payment_form_ptr); td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(500, "Unsupported"); break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); if (payment_form->invoice_->prices_.size() != 1u || payment_form->invoice_->prices_[0]->amount_ != star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_, false); return promise_.set_error(400, "Wrong transfer price specified"); } td_->create_handler(std::move(promise_)) ->send(business_connection_id_, std::move(transfer_input_invoice_), payment_form->form_id_, star_count_); break; } default: UNREACHABLE(); } } void on_error(Status status) final { td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class DropGiftOriginalDetailsQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; public: explicit DropGiftOriginalDetailsQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; send_query(G()->net_query_creator().create( telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_result = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for DropGiftOriginalDetailsQuery: " << to_string(payment_result); switch (payment_result->get_id()) { case telegram_api::payments_paymentResult::ID: { auto result = telegram_api::move_object_as(payment_result); td_->star_manager_->add_pending_owned_star_count(star_count_, true); td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise_)); break; } case telegram_api::payments_paymentVerificationNeeded::ID: LOG(ERROR) << "Receive " << to_string(payment_result); return on_error(Status::Error(500, "Receive invalid response")); default: UNREACHABLE(); } } void on_error(Status status) final { if (status.message() == "FORM_SUBMIT_DUPLICATE") { LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; } td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; class GetGiftDropOriginalDetailsPaymentFormQuery final : public Td::ResultHandler { Promise promise_; int64 star_count_; telegram_api::object_ptr drop_original_details_input_invoice_; public: explicit GetGiftDropOriginalDetailsPaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, telegram_api::object_ptr drop_original_details_input_invoice, int64 star_count) { drop_original_details_input_invoice_ = std::move(drop_original_details_input_invoice); star_count_ = star_count; td_->star_manager_->add_pending_owned_star_count(-star_count, false); send_query( G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_form_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetGiftDropOriginalDetailsPaymentFormQuery: " << to_string(payment_form_ptr); switch (payment_form_ptr->get_id()) { case telegram_api::payments_paymentForm::ID: case telegram_api::payments_paymentFormStarGift::ID: LOG(ERROR) << "Receive " << to_string(payment_form_ptr); td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(500, "Unsupported"); break; case telegram_api::payments_paymentFormStars::ID: { auto payment_form = static_cast(payment_form_ptr.get()); if (payment_form->invoice_->prices_.size() != 1u || payment_form->invoice_->prices_[0]->amount_ != star_count_) { td_->star_manager_->add_pending_owned_star_count(star_count_, false); return promise_.set_error(400, "Wrong drop original details price specified"); } td_->create_handler(std::move(promise_)) ->send(std::move(drop_original_details_input_invoice_), payment_form->form_id_, star_count_); break; } default: UNREACHABLE(); } } void on_error(Status status) final { td_->star_manager_->add_pending_owned_star_count(star_count_, false); promise_.set_error(std::move(status)); } }; static Promise get_gift_resale_promise(Td *td, StarGiftId star_gift_id, const telegram_api::object_ptr &updates, Promise &&promise) { vector> new_messages = UpdatesManager::get_new_messages(updates.get()); if (new_messages.size() == 1u && !new_messages[0].second && new_messages[0].first->get_id() == telegram_api::messageService::ID) { auto message = static_cast(new_messages[0].first); if (message->action_->get_id() == telegram_api::messageActionStarGiftUnique::ID) { auto action = static_cast(message->action_.get()); if (action->gift_->get_id() == telegram_api::starGiftUnique::ID && action->resale_amount_ != nullptr) { auto message_full_id = MessageFullId::get_message_full_id(new_messages[0].first, false); if (message_full_id.get_dialog_id() == td->dialog_manager_->get_my_dialog_id()) { star_gift_id = StarGiftId(message_full_id.get_message_id().get_server_message_id()); } } } } return PromiseCreator::lambda( [received_gift_id = star_gift_id.get_star_gift_id(), promise = std::move(promise)](Result result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } promise.set_value(std::move(received_gift_id)); }); } class ResaleGiftQuery final : public Td::ResultHandler { Promise promise_; StarGiftResalePrice price_; StarGiftId star_gift_id_; public: explicit ResaleGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, int64 payment_form_id, StarGiftResalePrice price, StarGiftId star_gift_id) { price_ = price; star_gift_id_ = std::move(star_gift_id); send_query(G()->net_query_creator().create( telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_result = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for ResaleGiftQuery: " << to_string(payment_result); switch (payment_result->get_id()) { case telegram_api::payments_paymentResult::ID: { auto result = telegram_api::move_object_as(payment_result); td_->star_manager_->add_pending_owned_amount(price_, 1, true); auto promise = get_gift_resale_promise(td_, std::move(star_gift_id_), result->updates_, std::move(promise_)); td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise)); break; } case telegram_api::payments_paymentVerificationNeeded::ID: LOG(ERROR) << "Receive " << to_string(payment_result); return on_error(Status::Error(500, "Receive invalid response")); default: UNREACHABLE(); } get_upgraded_gift_emoji_statuses(td_, Auto()); } void on_error(Status status) final { if (status.message() == "FORM_SUBMIT_DUPLICATE") { LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; } td_->star_manager_->add_pending_owned_amount(price_, 1, false); promise_.set_error(std::move(status)); } }; class GetGiftResalePaymentFormQuery final : public Td::ResultHandler { Promise> promise_; StarGiftResalePrice price_; telegram_api::object_ptr resale_input_invoice_; StarGiftId star_gift_id_; public: explicit GetGiftResalePaymentFormQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr input_invoice, telegram_api::object_ptr resale_input_invoice, StarGiftResalePrice price, StarGiftId star_gift_id) { resale_input_invoice_ = std::move(resale_input_invoice); price_ = price; star_gift_id_ = std::move(star_gift_id); td_->star_manager_->add_pending_owned_amount(price_, -1, false); send_query( G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto payment_form_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetGiftResalePaymentFormQuery: " << to_string(payment_form_ptr); switch (payment_form_ptr->get_id()) { case telegram_api::payments_paymentForm::ID: case telegram_api::payments_paymentFormStars::ID: LOG(ERROR) << "Receive " << to_string(payment_form_ptr); td_->star_manager_->add_pending_owned_amount(price_, 1, false); promise_.set_error(500, "Unsupported"); break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); if (payment_form->invoice_->prices_.size() != 1u) { td_->star_manager_->add_pending_owned_amount(price_, 1, false); return promise_.set_error(500, "Receive invalid price"); } if (payment_form->invoice_->currency_ != (price_.is_ton() ? "TON" : "XTR")) { td_->star_manager_->add_pending_owned_amount(price_, 1, false); return promise_.set_error(500, "Receive invalid price currency"); } auto amount = payment_form->invoice_->prices_[0]->amount_; auto expected_amount = price_.is_ton() ? price_.get_ton_count() : price_.get_star_count(); auto real_price = price_.is_ton() ? StarGiftResalePrice(telegram_api::make_object(amount)) : StarGiftResalePrice(telegram_api::make_object(amount, 0)); if (amount > expected_amount) { td_->star_manager_->add_pending_owned_amount(price_, 1, false); return promise_.set_value( td_api::make_object(real_price.get_gift_resale_price_object())); } if (amount != expected_amount) { td_->star_manager_->add_pending_owned_amount(price_, 1, false); td_->star_manager_->add_pending_owned_amount(real_price, -1, false); price_ = real_price; } td_->create_handler( PromiseCreator::lambda([promise = std::move(promise_)](Result result) mutable { if (result.is_error()) { promise.set_error(result.move_as_error()); } else { promise.set_value(td_api::make_object(result.move_as_ok())); } })) ->send(std::move(resale_input_invoice_), payment_form->form_id_, price_, std::move(star_gift_id_)); break; } default: UNREACHABLE(); } } void on_error(Status status) final { td_->star_manager_->add_pending_owned_amount(price_, 1, false); promise_.set_error(std::move(status)); } }; class SendStarGiftOfferQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; StarGiftResalePrice price_; int64 paid_message_star_count_; public: explicit SendStarGiftOfferQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, const string &slug, const StarGiftResalePrice &price, int32 duration, int64 paid_message_star_count) { dialog_id_ = dialog_id; price_ = price; paid_message_star_count_ = paid_message_star_count; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); int32 flags = 0; if (paid_message_star_count > 0) { flags |= telegram_api::payments_sendStarGiftOffer::ALLOW_PAID_STARS_MASK; } send_query(G()->net_query_creator().create( telegram_api::payments_sendStarGiftOffer(flags, std::move(input_peer), slug, price.get_input_stars_amount(), duration, Random::secure_int64(), paid_message_star_count), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for SendStarGiftOfferQuery: " << to_string(ptr); td_->star_manager_->add_pending_owned_star_count(paid_message_star_count_, true); td_->star_manager_->add_pending_owned_amount(price_, 1, true); td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); } void on_error(Status status) final { td_->star_manager_->add_pending_owned_star_count(paid_message_star_count_, false); td_->star_manager_->add_pending_owned_amount(price_, 1, false); td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "SendStarGiftOfferQuery"); promise_.set_error(std::move(status)); } }; class ResolveStarGiftOfferQuery final : public Td::ResultHandler { Promise promise_; public: explicit ResolveStarGiftOfferQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(MessageId message_id, bool decline) { send_query(G()->net_query_creator().create( telegram_api::payments_resolveStarGiftOffer(0, decline, message_id.get_server_message_id().get()))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for ResolveStarGiftOfferQuery: " << to_string(ptr); td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetSavedStarGiftsQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; BusinessConnectionId business_connection_id_; bool is_full_list_ = false; public: explicit GetSavedStarGiftsQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(BusinessConnectionId business_connection_id, DialogId dialog_id, StarGiftCollectionId collection_id, bool exclude_unsaved, bool exclude_saved, bool exclude_unlimited, bool exclude_upgradable, bool exclude_non_upgradable, bool exclude_unique, bool peer_color_available, bool exclude_hosted, bool sort_by_value, const string &offset, int32 limit) { business_connection_id_ = business_connection_id; dialog_id_ = business_connection_id.is_valid() ? DialogId(td_->business_connection_manager_->get_business_connection_user_id(business_connection_id)) : dialog_id; is_full_list_ = !collection_id.is_valid() && !exclude_unsaved && !exclude_saved && !exclude_unlimited && !exclude_upgradable && !exclude_non_upgradable && !exclude_unique && !peer_color_available && !exclude_hosted; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); if (input_peer == nullptr) { return on_error(Status::Error(400, "Can't access the chat")); } int32 flags = 0; if (collection_id.is_valid()) { flags |= telegram_api::payments_getSavedStarGifts::COLLECTION_ID_MASK; } send_query(G()->net_query_creator().create_with_prefix( business_connection_id.get_invoke_prefix(), telegram_api::payments_getSavedStarGifts(flags, exclude_unsaved, exclude_saved, exclude_unlimited, exclude_unique, sort_by_value, exclude_upgradable, exclude_non_upgradable, peer_color_available, exclude_hosted, std::move(input_peer), collection_id.get(), offset, limit), td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetSavedStarGiftsQuery: " << to_string(ptr); td_->user_manager_->on_get_users(std::move(ptr->users_), "GetSavedStarGiftsQuery"); td_->chat_manager_->on_get_chats(std::move(ptr->chats_), "GetSavedStarGiftsQuery"); auto total_count = ptr->count_; if (total_count < static_cast(ptr->gifts_.size())) { LOG(ERROR) << "Receive " << ptr->gifts_.size() << " gifts with total count = " << total_count; total_count = static_cast(ptr->gifts_.size()); } vector> gifts; for (auto &gift : ptr->gifts_) { UserStarGift user_gift(td_, std::move(gift), dialog_id_); if (!user_gift.is_valid()) { LOG(ERROR) << "Receive invalid user gift"; continue; } gifts.push_back(user_gift.get_received_gift_object(td_)); } bool are_notifications_enabled = false; if (dialog_id_.get_type() == DialogType::User) { if (dialog_id_ != td_->dialog_manager_->get_my_dialog_id()) { if (is_full_list_) { td_->user_manager_->on_update_user_gift_count(dialog_id_.get_user_id(), total_count); } } else { are_notifications_enabled = true; } } else if (dialog_id_.get_type() == DialogType::Channel) { if (is_full_list_) { td_->chat_manager_->on_update_channel_gift_count(dialog_id_.get_channel_id(), total_count, false); } are_notifications_enabled = ptr->chat_notifications_enabled_; } promise_.set_value(td_api::make_object(total_count, std::move(gifts), are_notifications_enabled, ptr->next_offset_)); } void on_error(Status status) final { if (!business_connection_id_.is_valid()) { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetSavedStarGiftsQuery"); } promise_.set_error(std::move(status)); } }; class GetSavedStarGiftQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; public: explicit GetSavedStarGiftQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(StarGiftId star_gift_id) { auto input_gift = star_gift_id.get_input_saved_star_gift(td_); if (input_gift == nullptr) { return on_error(Status::Error(400, "Gift not found")); } vector> gifts; gifts.push_back(std::move(input_gift)); dialog_id_ = star_gift_id.get_dialog_id(td_); send_query(G()->net_query_creator().create(telegram_api::payments_getSavedStarGift(std::move(gifts)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetSavedStarGiftQuery: " << to_string(ptr); for (auto &gift : ptr->gifts_) { UserStarGift user_gift(td_, std::move(gift), dialog_id_); if (!user_gift.is_valid()) { LOG(ERROR) << "Receive invalid user gift"; continue; } return promise_.set_value(user_gift.get_received_gift_object(td_)); } promise_.set_error(400, "Gift not found"); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetSavedStarGiftQuery"); promise_.set_error(std::move(status)); } }; class GetCraftStarGiftsQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetCraftStarGiftsQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(int64 gift_id, const string &offset, int32 limit) { send_query(G()->net_query_creator().create(telegram_api::payments_getCraftStarGifts(gift_id, offset, limit))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetCraftStarGiftsQuery: " << to_string(ptr); td_->user_manager_->on_get_users(std::move(ptr->users_), "GetCraftStarGiftsQuery"); td_->chat_manager_->on_get_chats(std::move(ptr->chats_), "GetCraftStarGiftsQuery"); auto total_count = ptr->count_; if (total_count < static_cast(ptr->gifts_.size())) { LOG(ERROR) << "Receive " << ptr->gifts_.size() << " gifts with total count = " << total_count; total_count = static_cast(ptr->gifts_.size()); } vector> gifts; for (auto &gift : ptr->gifts_) { UserStarGift user_gift(td_, std::move(gift), td_->dialog_manager_->get_my_dialog_id()); if (!user_gift.is_valid()) { LOG(ERROR) << "Receive invalid gift for crafting"; continue; } if (!user_gift.is_unique()) { LOG(ERROR) << "Receive non-unique gift for crafting"; continue; } gifts.push_back(user_gift.get_received_gift_object(td_)); } auto attribute_persistence_per_mille = transform(full_split(td_->option_manager_->get_option_string("stargifts_craft_attribute_permilles", "100,80,400,70,350,700,60,300,600,1000"), ','), [](const auto &str) { return to_integer(str); }); CHECK(attribute_persistence_per_mille.size() == 10u); vector> probabilities; for (size_t num = 1; num <= 4; num++) { vector numbers; for (size_t i = 1; i <= 4; i++) { numbers.push_back(i <= num ? attribute_persistence_per_mille[num * (num - 1) / 2 + i - 1] : 0); } probabilities.push_back(td_api::make_object(std::move(numbers))); } LOG(ERROR) << to_string(probabilities); promise_.set_value(td_api::make_object(total_count, std::move(gifts), std::move(probabilities), ptr->next_offset_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetUniqueStarGiftQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetUniqueStarGiftQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(const string &name) { send_query(G()->net_query_creator().create(telegram_api::payments_getUniqueStarGift(name))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetUniqueStarGiftQuery: " << to_string(ptr); td_->user_manager_->on_get_users(std::move(ptr->users_), "GetUniqueStarGiftQuery"); td_->chat_manager_->on_get_chats(std::move(ptr->chats_), "GetUniqueStarGiftQuery"); StarGift star_gift(td_, std::move(ptr->gift_), true); if (!star_gift.is_valid() || !star_gift.is_unique()) { LOG(ERROR) << "Receive invalid user gift"; return promise_.set_error(400, "Gift not found"); } promise_.set_value(star_gift.get_upgraded_gift_object(td_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetUniqueStarGiftValueInfoQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetUniqueStarGiftValueInfoQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(const string &name) { send_query(G()->net_query_creator().create(telegram_api::payments_getUniqueStarGiftValueInfo(name))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetUniqueStarGiftValueInfoQuery: " << to_string(ptr); promise_.set_value(td_api::make_object( ptr->currency_, ptr->value_, ptr->value_is_average_, max(0, ptr->initial_sale_date_), StarManager::get_star_count(ptr->initial_sale_stars_), ptr->initial_sale_price_, max(0, ptr->last_sale_date_), ptr->last_sale_price_, ptr->last_sale_on_fragment_, ptr->floor_price_, ptr->average_price_, ptr->listed_count_, ptr->fragment_listed_count_, ptr->fragment_listed_url_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetStarGiftWithdrawalUrlQuery final : public Td::ResultHandler { Promise promise_; public: explicit GetStarGiftWithdrawalUrlQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(StarGiftId star_gift_id, telegram_api::object_ptr input_check_password) { auto input_gift = star_gift_id.get_input_saved_star_gift(td_); if (input_gift == nullptr) { return on_error(Status::Error(400, "Gift not found")); } send_query(G()->net_query_creator().create( telegram_api::payments_getStarGiftWithdrawalUrl(std::move(input_gift), std::move(input_check_password)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } promise_.set_value(std::move(result_ptr.ok_ref()->url_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class UpdateStarGiftPriceQuery final : public Td::ResultHandler { Promise promise_; public: explicit UpdateStarGiftPriceQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(StarGiftId star_gift_id, StarGiftResalePrice price) { auto input_gift = star_gift_id.get_input_saved_star_gift(td_); if (input_gift == nullptr) { return on_error(Status::Error(400, "Gift not found")); } send_query(G()->net_query_creator().create( telegram_api::payments_updateStarGiftPrice(std::move(input_gift), price.get_input_stars_amount()))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for UpdateStarGiftPriceQuery: " << to_string(ptr); td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetResaleStarGiftsQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetResaleStarGiftsQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(int64 gift_id, const td_api::object_ptr &order, bool for_craft, bool for_stars, const vector &attribute_ids, const string &offset, int32 limit) { int32 flags = 0; auto order_id = order->get_id(); auto attributes = StarGiftAttributeId::get_input_star_gift_attribute_ids_object(attribute_ids); if (!attributes.empty()) { flags |= telegram_api::payments_getResaleStarGifts::ATTRIBUTES_MASK; } if (offset.empty() && attributes.empty()) { flags |= telegram_api::payments_getResaleStarGifts::ATTRIBUTES_HASH_MASK; } send_query(G()->net_query_creator().create(telegram_api::payments_getResaleStarGifts( flags, order_id == td_api::giftForResaleOrderPrice::ID, order_id == td_api::giftForResaleOrderNumber::ID, for_craft, for_stars, 0, gift_id, std::move(attributes), offset, limit))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetResaleStarGiftsQuery: " << to_string(ptr); td_->user_manager_->on_get_users(std::move(ptr->users_), "GetResaleStarGiftsQuery"); td_->chat_manager_->on_get_chats(std::move(ptr->chats_), "GetResaleStarGiftsQuery"); auto total_count = ptr->count_; if (total_count < static_cast(ptr->gifts_.size())) { LOG(ERROR) << "Receive " << ptr->gifts_.size() << " gifts with total count = " << total_count; total_count = static_cast(ptr->gifts_.size()); } vector> gifts; for (auto &gift : ptr->gifts_) { StarGift star_gift(td_, std::move(gift), true); if (!star_gift.is_valid() || !star_gift.is_unique()) { LOG(ERROR) << "Receive invalid upgraded gift"; continue; } gifts.push_back(star_gift.get_gift_for_resale_object(td_)); } FlatHashMap counters; for (auto &counter : ptr->counters_) { if (counter->count_ <= 0) { LOG(ERROR) << "Receive " << to_string(counter); continue; } StarGiftAttributeId attribute(std::move(counter->attribute_)); CHECK(attribute != StarGiftAttributeId()); counters[attribute] = counter->count_; } auto get_count = [&](StarGiftAttributeId attribute_id) { auto it = counters.find(attribute_id); if (it == counters.end()) { LOG(ERROR) << "Can't find counter for " << attribute_id; return 0; } auto count = it->second; counters.erase(it); return count; }; vector> models; vector> symbols; vector> backdrops; for (auto &attribute : ptr->attributes_) { switch (attribute->get_id()) { case telegram_api::starGiftAttributeModel::ID: { auto model = StarGiftAttributeSticker( td_, telegram_api::move_object_as(attribute)); if (!model.is_valid()) { LOG(ERROR) << "Receive invalid model"; break; } auto count = get_count(model.get_id(td_, true)); if (count > 0) { models.push_back( td_api::make_object(model.get_upgraded_gift_model_object(td_), count)); } break; } case telegram_api::starGiftAttributePattern::ID: { auto pattern = StarGiftAttributeSticker( td_, telegram_api::move_object_as(attribute)); if (!pattern.is_valid()) { LOG(ERROR) << "Receive invalid symbol"; break; } auto count = get_count(pattern.get_id(td_, false)); if (count > 0) { symbols.push_back(td_api::make_object( pattern.get_upgraded_gift_symbol_object(td_), count)); } break; } case telegram_api::starGiftAttributeBackdrop::ID: { auto backdrop = StarGiftAttributeBackdrop( telegram_api::move_object_as(attribute)); if (!backdrop.is_valid()) { LOG(ERROR) << "Receive invalid backdrop"; break; } auto count = get_count(backdrop.get_id()); if (count > 0) { backdrops.push_back(td_api::make_object( backdrop.get_upgraded_gift_backdrop_object(), count)); } break; } case telegram_api::starGiftAttributeOriginalDetails::ID: LOG(ERROR) << "Receive original details"; break; default: UNREACHABLE(); } } auto compare_by_count = [](auto &lhs, auto &rhs) { return lhs->total_count_ > rhs->total_count_; }; std::sort(models.begin(), models.end(), compare_by_count); std::sort(symbols.begin(), symbols.end(), compare_by_count); std::sort(backdrops.begin(), backdrops.end(), compare_by_count); if (!counters.empty()) { LOG(ERROR) << "Receive " << counters.size() << " unused counters"; } promise_.set_value(td_api::make_object( total_count, std::move(gifts), std::move(models), std::move(symbols), std::move(backdrops), ptr->next_offset_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class GetStarGiftCollectionsQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; public: explicit GetStarGiftCollectionsQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id) { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create(telegram_api::payments_getStarGiftCollections(std::move(input_peer), 0), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetStarGiftCollectionsQuery: " << to_string(ptr); switch (ptr->get_id()) { case telegram_api::payments_starGiftCollectionsNotModified::ID: LOG(ERROR) << "Receive " << to_string(ptr); return promise_.set_value(td_api::make_object()); case telegram_api::payments_starGiftCollections::ID: { auto collections = telegram_api::move_object_as(ptr); auto result = td_api::make_object(); for (auto &collection : collections->collections_) { StarGiftCollection gift_collection(td_, std::move(collection)); result->collections_.push_back(gift_collection.get_gift_collection_object(td_)); } return promise_.set_value(std::move(result)); } default: UNREACHABLE(); } } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetStarGiftCollectionsQuery"); promise_.set_error(std::move(status)); } }; class CreateStarGiftCollectionQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; public: explicit CreateStarGiftCollectionQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, const string &title, const vector &star_gift_ids) { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create( telegram_api::payments_createStarGiftCollection(std::move(input_peer), title, StarGiftId::get_input_saved_star_gifts(td_, star_gift_ids)), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for CreateStarGiftCollectionQuery: " << to_string(ptr); StarGiftCollection gift_collection(td_, std::move(ptr)); promise_.set_value(gift_collection.get_gift_collection_object(td_)); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "CreateStarGiftCollectionQuery"); promise_.set_error(std::move(status)); } }; class ReorderStarGiftCollectionsQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; public: explicit ReorderStarGiftCollectionsQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, const vector &collection_ids) { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create( telegram_api::payments_reorderStarGiftCollections( std::move(input_peer), transform(collection_ids, [](const StarGiftCollectionId &collection_id) { return collection_id.get(); })), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } promise_.set_value(Unit()); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ReorderStarGiftCollectionsQuery"); promise_.set_error(std::move(status)); } }; class DeleteStarGiftCollectionQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; public: explicit DeleteStarGiftCollectionQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, StarGiftCollectionId collection_id) { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create( telegram_api::payments_deleteStarGiftCollection(std::move(input_peer), collection_id.get()), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } promise_.set_value(Unit()); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "DeleteStarGiftCollectionQuery"); promise_.set_error(std::move(status)); } }; class UpdateStarGiftCollectionQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; public: explicit UpdateStarGiftCollectionQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, StarGiftCollectionId collection_id, const string &title, const vector &deleted_star_gift_ids, const vector &added_star_gift_ids, const vector &ordered_star_gift_ids) { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); int32 flags = 0; if (!title.empty()) { flags |= telegram_api::payments_updateStarGiftCollection::TITLE_MASK; } if (!deleted_star_gift_ids.empty()) { flags |= telegram_api::payments_updateStarGiftCollection::DELETE_STARGIFT_MASK; } if (!added_star_gift_ids.empty()) { flags |= telegram_api::payments_updateStarGiftCollection::ADD_STARGIFT_MASK; } if (!ordered_star_gift_ids.empty()) { flags |= telegram_api::payments_updateStarGiftCollection::ORDER_MASK; } send_query(G()->net_query_creator().create(telegram_api::payments_updateStarGiftCollection( flags, std::move(input_peer), collection_id.get(), title, StarGiftId::get_input_saved_star_gifts(td_, deleted_star_gift_ids), StarGiftId::get_input_saved_star_gifts(td_, added_star_gift_ids), StarGiftId::get_input_saved_star_gifts(td_, ordered_star_gift_ids)), {{dialog_id_}})); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for UpdateStarGiftCollectionQuery: " << to_string(ptr); StarGiftCollection gift_collection(td_, std::move(ptr)); promise_.set_value(gift_collection.get_gift_collection_object(td_)); } void on_error(Status status) final { td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "UpdateStarGiftCollectionQuery"); promise_.set_error(std::move(status)); } }; class GetStarGiftPromoAnimationQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetStarGiftPromoAnimationQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send() { send_query(G()->net_query_creator().create(telegram_api::help_getPremiumPromo())); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto promo = result_ptr.move_as_ok(); td_->user_manager_->on_get_users(std::move(promo->users_), "GetStarGiftPromoAnimationQuery"); for (size_t i = 0; i < promo->video_sections_.size(); i++) { if (i >= promo->videos_.size()) { break; } if (promo->video_sections_[i] != "gifts") { continue; } auto video = std::move(promo->videos_[i]); if (video->get_id() != telegram_api::document::ID) { LOG(ERROR) << "Receive " << to_string(video) << " for " << promo->video_sections_[i]; break; } auto parsed_document = td_->documents_manager_->on_get_document(telegram_api::move_object_as(video), DialogId(), false, false, nullptr, Document::Type::Animation); if (parsed_document.type != Document::Type::Animation) { LOG(ERROR) << "Receive " << parsed_document.type << " for " << promo->video_sections_[i]; break; } return promise_.set_value(td_->animations_manager_->get_animation_object(parsed_document.file_id)); } promise_.set_value(nullptr); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; StarGiftManager::StarGiftManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { update_gift_message_timeout_.set_callback(on_update_gift_message_timeout_callback); update_gift_message_timeout_.set_callback_data(static_cast(this)); reload_gift_auction_timeout_.set_callback(on_reload_gift_auction_timeout_callback); reload_gift_auction_timeout_.set_callback_data(static_cast(this)); } StarGiftManager::~StarGiftManager() { Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), gift_message_full_ids_, gift_message_full_ids_by_id_, being_reloaded_gift_messages_); } void StarGiftManager::start_up() { if (!td_->auth_manager_->is_bot()) { class StateCallback final : public StateManager::Callback { public: explicit StateCallback(ActorId parent) : parent_(std::move(parent)) { } bool on_online(bool is_online) final { if (is_online) { send_closure(parent_, &StarGiftManager::on_online); } return parent_.is_alive(); } private: ActorId parent_; }; send_closure(G()->state_manager(), &StateManager::add_callback, make_unique(actor_id(this))); } } void StarGiftManager::tear_down() { parent_.reset(); } void StarGiftManager::get_gift_payment_options(Promise> &&promise) { td_->create_handler(std::move(promise))->send(); } void StarGiftManager::on_get_star_gift(const StarGift &star_gift, bool from_server) { if (td_->auth_manager_->is_bot() || !star_gift.is_valid() || star_gift.is_unique()) { return; } if (!from_server && gift_prices_.count(star_gift.get_id())) { return; } gift_prices_[star_gift.get_id()] = {star_gift.get_star_count(), star_gift.get_upgrade_star_count()}; } void StarGiftManager::can_send_gift(int64 gift_id, Promise> &&promise) { td_->create_handler(std::move(promise))->send(gift_id); } void StarGiftManager::send_gift(int64 gift_id, DialogId dialog_id, td_api::object_ptr text, bool is_private, bool pay_for_upgrade, Promise &&promise) { int64 star_count = 0; if (!td_->auth_manager_->is_bot()) { auto it = gift_prices_.find(gift_id); if (it == gift_prices_.end()) { return promise.set_error(400, "Gift not found"); } star_count = it->second.first; if (pay_for_upgrade) { star_count += it->second.second; } if (!td_->star_manager_->has_owned_star_count(star_count)) { return promise.set_error(400, "Have not enough Telegram Stars"); } } auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); auto send_input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); if (input_peer == nullptr || send_input_peer == nullptr) { return promise.set_error(400, "Have no access to the gift receiver"); } TRY_RESULT_PROMISE( promise, message, get_formatted_text(td_, td_->dialog_manager_->get_my_dialog_id(), std::move(text), false, true, true, false)); remove_unallowed_quote_entities(message); auto input_invoice = telegram_api::make_object( 0, is_private, pay_for_upgrade, std::move(input_peer), gift_id, nullptr); auto send_input_invoice = telegram_api::make_object( 0, is_private, pay_for_upgrade, std::move(send_input_peer), gift_id, nullptr); if (!message.text.empty()) { input_invoice->flags_ |= telegram_api::inputInvoiceStarGift::MESSAGE_MASK; input_invoice->message_ = get_input_text_with_entities(td_->user_manager_.get(), message, "send_gift"); send_input_invoice->flags_ |= telegram_api::inputInvoiceStarGift::MESSAGE_MASK; send_input_invoice->message_ = get_input_text_with_entities(td_->user_manager_.get(), message, "send_gift"); } td_->create_handler(std::move(promise)) ->send(std::move(input_invoice), std::move(send_input_invoice), star_count); } void StarGiftManager::get_gift_auction_state(const string &auction_id, Promise> &&promise) { reload_gift_auction_state(telegram_api::make_object(auction_id), 0, std::move(promise)); } const StarGiftManager::AuctionInfo *StarGiftManager::get_auction_info( telegram_api::object_ptr &&star_gift, telegram_api::object_ptr &&state, telegram_api::object_ptr &&user_state) { auto gift = StarGift(td_, std::move(star_gift), false); if (!gift.is_valid() || gift.get_id() == 0 || !gift.is_auction()) { LOG(ERROR) << "Receive invalid auction gift"; return nullptr; } bool is_changed = false; auto &info = gift_auction_infos_[gift.get_id()]; if (info.gift_ != gift) { info.gift_ = std::move(gift); is_changed = true; } auto new_state = StarGiftAuctionState(state); if (!new_state.is_not_modified() && new_state.get_version() >= info.state_.get_version() && new_state != info.state_) { info.state_ = std::move(new_state); is_changed = true; } auto new_user_state = StarGiftAuctionUserState(user_state); if (info.user_state_ != new_user_state) { info.user_state_ = std::move(new_user_state); is_changed = true; } if (is_changed) { send_update_gift_auction_state(info); } return &info; } void StarGiftManager::reload_gift_auction_state( telegram_api::object_ptr &&input_auction, int32 version, Promise> &&promise) { auto query_promise = PromiseCreator::lambda( [actor_id = actor_id(this), promise = std::move(promise)]( Result> r_state) mutable { send_closure(actor_id, &StarGiftManager::on_get_auction_state, std::move(r_state), std::move(promise)); }); td_->create_handler(std::move(query_promise))->send(std::move(input_auction), version); } void StarGiftManager::on_get_auction_state( Result> r_state, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); if (r_state.is_error()) { return promise.set_error(r_state.move_as_error()); } auto state = r_state.move_as_ok(); td_->user_manager_->on_get_users(std::move(state->users_), "on_get_auction_state"); td_->chat_manager_->on_get_chats(std::move(state->chats_), "on_get_auction_state"); auto info = get_auction_info(std::move(state->gift_), std::move(state->state_), std::move(state->user_state_)); if (info == nullptr) { return promise.set_error(500, "Receive invalid response"); } auto gift_id = info->gift_.get_id(); if (gift_auction_infos_.count(gift_id) > 0) { reload_gift_auction_timeout_.set_timeout_in(gift_id, state->timeout_); } if (promise) { promise.set_value(get_gift_auction_state_object(*info)); } } void StarGiftManager::on_update_gift_auction_state( int64 gift_id, telegram_api::object_ptr &&state) { auto it = gift_auction_infos_.find(gift_id); if (it == gift_auction_infos_.end()) { reload_active_gift_auctions(); return; } auto &info = it->second; auto new_state = StarGiftAuctionState(state); if (new_state.is_not_modified()) { LOG(ERROR) << "Receive " << to_string(state); return; } if (new_state.get_version() <= info.state_.get_version() || new_state == info.state_) { return; } info.state_ = std::move(new_state); send_update_gift_auction_state(info); } void StarGiftManager::on_update_gift_auction_user_state( int64 gift_id, telegram_api::object_ptr &&state) { auto it = gift_auction_infos_.find(gift_id); if (it == gift_auction_infos_.end()) { reload_active_gift_auctions(); return; } auto &info = it->second; auto new_user_state = StarGiftAuctionUserState(state); if (new_user_state == info.user_state_) { return; } info.user_state_ = std::move(new_user_state); send_update_gift_auction_state(info); } void StarGiftManager::schedule_active_gift_auctions_reload() { if (active_gift_auctions_reload_timeout_.has_timeout()) { return; } LOG(INFO) << "Schedule active gift auctions reload"; active_gift_auctions_reload_timeout_.set_callback(std::move(reload_active_gift_auctions_static)); active_gift_auctions_reload_timeout_.set_callback_data(static_cast(td_)); active_gift_auctions_reload_timeout_.set_timeout_in(10.0); } void StarGiftManager::reload_active_gift_auctions_static(void *td) { if (G()->close_flag()) { return; } CHECK(td != nullptr); static_cast(td)->star_gift_manager_->reload_active_gift_auctions(); } void StarGiftManager::reload_active_gift_auctions() { auto promise = PromiseCreator::lambda( [actor_id = actor_id(this)]( Result> r_auctions) mutable { send_closure(actor_id, &StarGiftManager::on_get_active_gift_auctions, std::move(r_auctions)); }); td_->create_handler(std::move(promise))->send(); } void StarGiftManager::on_get_active_gift_auctions( Result> r_auctions) { if (G()->close_flag()) { return; } if (r_auctions.is_error()) { schedule_active_gift_auctions_reload(); return; } auto auctions_ptr = r_auctions.move_as_ok(); switch (auctions_ptr->get_id()) { case telegram_api::payments_starGiftActiveAuctionsNotModified::ID: LOG(ERROR) << "Receive " << to_string(auctions_ptr); break; case telegram_api::payments_starGiftActiveAuctions::ID: { auto auctions = telegram_api::move_object_as(auctions_ptr); td_->user_manager_->on_get_users(std::move(auctions->users_), "on_get_active_gift_auctions"); td_->chat_manager_->on_get_chats(std::move(auctions->chats_), "on_get_active_gift_auctions"); vector new_active_auctions; for (auto &auction : auctions->auctions_) { auto info = get_auction_info(std::move(auction->gift_), std::move(auction->state_), std::move(auction->user_state_)); if (info == nullptr) { continue; } new_active_auctions.push_back(*info); } if (active_gift_auctions_ != new_active_auctions) { active_gift_auctions_ = std::move(new_active_auctions); send_update_active_gift_auctions(); } break; } default: UNREACHABLE(); } } void StarGiftManager::get_gift_auction_acquired_gifts( int64 gift_id, Promise> &&promise) { td_->create_handler(std::move(promise))->send(gift_id); } td_api::object_ptr StarGiftManager::get_gift_auction_state_object( const AuctionInfo &info) const { return td_api::make_object(info.gift_.get_gift_object(td_), info.state_.get_auction_state_object(td_, info.user_state_)); } void StarGiftManager::send_update_gift_auction_state(const AuctionInfo &info) { if (td_->auth_manager_->is_bot()) { return; } bool is_active = info.user_state_.is_active(); auto gift_id = info.gift_.get_id(); bool is_found = false; for (auto it = active_gift_auctions_.begin(); it != active_gift_auctions_.end(); ++it) { auto &active_info = *it; if (active_info.gift_.get_id() == gift_id) { if (is_active) { if (active_info.state_ != info.state_ || active_info.user_state_ != info.user_state_) { active_info.state_ = info.state_; active_info.user_state_ = info.user_state_; send_update_active_gift_auctions(); } } else { active_gift_auctions_.erase(it); send_update_active_gift_auctions(); } is_found = true; break; } } if (!is_found && is_active) { active_gift_auctions_.push_back(info); send_update_active_gift_auctions(); } send_closure(G()->td(), &Td::send_update, td_api::make_object(get_gift_auction_state_object(info))); } void StarGiftManager::send_update_active_gift_auctions() { if (td_->auth_manager_->is_bot()) { return; } if (!active_gift_auctions_.empty()) { schedule_active_gift_auctions_reload(); } send_closure( G()->td(), &Td::send_update, td_api::make_object(transform( active_gift_auctions_, [this](const AuctionInfo &info) { return get_gift_auction_state_object(info); }))); } void StarGiftManager::open_gift_auction(int64 gift_id, bool is_recursive, Promise &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); auto it = gift_auction_infos_.find(gift_id); if (it == gift_auction_infos_.end()) { CHECK(!is_recursive); return reload_gift_auction_state( telegram_api::make_object(gift_id), 0, PromiseCreator::lambda([actor_id = actor_id(this), gift_id, promise = std::move(promise)]( Result> &&r_state) mutable { if (r_state.is_error()) { return promise.set_error(r_state.move_as_error()); } else { send_closure(actor_id, &StarGiftManager::open_gift_auction, gift_id, true, std::move(promise)); } })); } auto &count = gift_auction_open_counts_[gift_id]; if (++count == 1) { LOG(INFO) << "Opened auction for gift " << gift_id; if (!is_recursive) { reload_gift_auction_state(telegram_api::make_object(gift_id), it->second.state_.get_version(), Auto()); } } promise.set_value(Unit()); } void StarGiftManager::close_gift_auction(int64 gift_id, Promise &&promise) { auto it = gift_auction_open_counts_.find(gift_id); if (it == gift_auction_open_counts_.end()) { return promise.set_error(400, "The gift auction isn't open"); } if (--it->second == 0) { gift_auction_open_counts_.erase(it); } promise.set_value(Unit()); } void StarGiftManager::place_gift_auction_bid(int64 gift_id, int64 star_count, UserId user_id, td_api::object_ptr text, bool is_private, Promise &&promise) { if (gift_auction_infos_.count(gift_id) == 0) { return reload_gift_auction_state( telegram_api::make_object(gift_id), 0, PromiseCreator::lambda( [actor_id = actor_id(this), gift_id, star_count, user_id, text = std::move(text), is_private, promise = std::move(promise)](Result> &&r_state) mutable { if (r_state.is_error()) { return promise.set_error(r_state.move_as_error()); } else { send_closure(actor_id, &StarGiftManager::do_place_gift_auction_bid, gift_id, star_count, user_id, std::move(text), is_private, std::move(promise)); } })); } do_place_gift_auction_bid(gift_id, star_count, user_id, std::move(text), is_private, std::move(promise)); } void StarGiftManager::do_place_gift_auction_bid(int64 gift_id, int64 star_count, UserId user_id, td_api::object_ptr text, bool is_private, Promise &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); auto it = gift_auction_infos_.find(gift_id); CHECK(it != gift_auction_infos_.end()); auto old_star_count = it->second.user_state_.get_bid_amount(); if (star_count <= old_star_count) { return promise.set_error(400, "Too small bid specified"); } if (!td_->star_manager_->has_owned_star_count(star_count - old_star_count)) { return promise.set_error(400, "Have not enough Telegram Stars"); } auto dialog_id = DialogId(user_id); auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); auto send_input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); if (input_peer == nullptr || send_input_peer == nullptr) { return promise.set_error(400, "Have no access to the gift receiver"); } TRY_RESULT_PROMISE( promise, message, get_formatted_text(td_, td_->dialog_manager_->get_my_dialog_id(), std::move(text), false, true, true, false)); remove_unallowed_quote_entities(message); int32 flags = telegram_api::inputInvoiceStarGiftAuctionBid::PEER_MASK; auto input_invoice = telegram_api::make_object( flags, is_private, old_star_count != 0, std::move(input_peer), gift_id, star_count, nullptr); auto send_input_invoice = telegram_api::make_object( flags, is_private, old_star_count != 0, std::move(send_input_peer), gift_id, star_count, nullptr); if (!message.text.empty()) { input_invoice->flags_ |= telegram_api::inputInvoiceStarGiftAuctionBid::MESSAGE_MASK; input_invoice->message_ = get_input_text_with_entities(td_->user_manager_.get(), message, "do_place_gift_auction_bid"); send_input_invoice->flags_ |= telegram_api::inputInvoiceStarGiftAuctionBid::MESSAGE_MASK; send_input_invoice->message_ = get_input_text_with_entities(td_->user_manager_.get(), message, "do_place_gift_auction_bid"); } td_->create_handler(std::move(promise)) ->send(std::move(input_invoice), std::move(send_input_invoice), star_count, old_star_count); } void StarGiftManager::update_gift_auction_bid(int64 gift_id, int64 star_count, Promise &&promise) { if (gift_auction_infos_.count(gift_id) == 0) { return reload_gift_auction_state( telegram_api::make_object(gift_id), 0, PromiseCreator::lambda([actor_id = actor_id(this), gift_id, star_count, promise = std::move(promise)]( Result> &&r_state) mutable { if (r_state.is_error()) { return promise.set_error(r_state.move_as_error()); } else { send_closure(actor_id, &StarGiftManager::do_update_gift_auction_bid, gift_id, star_count, std::move(promise)); } })); } do_update_gift_auction_bid(gift_id, star_count, std::move(promise)); } void StarGiftManager::do_update_gift_auction_bid(int64 gift_id, int64 star_count, Promise &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); auto it = gift_auction_infos_.find(gift_id); CHECK(it != gift_auction_infos_.end()); auto old_star_count = it->second.user_state_.get_bid_amount(); if (!old_star_count) { return promise.set_error(400, "There is no bid to update"); } if (star_count <= old_star_count) { return promise.set_error(400, "Too small bid specified"); } if (!td_->star_manager_->has_owned_star_count(star_count - old_star_count)) { return promise.set_error(400, "Have not enough Telegram Stars"); } auto input_invoice = telegram_api::make_object( 0, false, true, nullptr, gift_id, star_count, nullptr); auto send_input_invoice = telegram_api::make_object( 0, false, true, nullptr, gift_id, star_count, nullptr); td_->create_handler(std::move(promise)) ->send(std::move(input_invoice), std::move(send_input_invoice), star_count, old_star_count); } void StarGiftManager::convert_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, Promise &&promise) { if (business_connection_id.is_valid()) { TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); } if (star_gift_id.get_input_saved_star_gift(td_) == nullptr) { return promise.set_error(400, "Invalid gift identifier specified"); } auto dialog_id = business_connection_id.is_valid() ? DialogId(td_->business_connection_manager_->get_business_connection_user_id(business_connection_id)) : star_gift_id.get_dialog_id(td_); auto query_promise = PromiseCreator::lambda( [actor_id = actor_id(this), dialog_id, promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(actor_id, &StarGiftManager::on_dialog_gift_transferred, dialog_id, DialogId(), std::move(promise)); }); td_->create_handler(std::move(query_promise)) ->send(business_connection_id, star_gift_id, dialog_id); } void StarGiftManager::save_gift(StarGiftId star_gift_id, bool is_saved, Promise &&promise) { if (star_gift_id.get_input_saved_star_gift(td_) == nullptr) { return promise.set_error(400, "Invalid gift identifier specified"); } td_->create_handler(std::move(promise))->send(star_gift_id, is_saved); } Status StarGiftManager::check_star_gift_id(const StarGiftId &star_gift_id, DialogId dialog_id) const { if (star_gift_id.get_input_saved_star_gift(td_) == nullptr) { return Status::Error(400, "Invalid gift identifier specified"); } if (dialog_id != DialogId() && star_gift_id.get_dialog_id(td_) != dialog_id) { return Status::Error(400, "The gift is not from the chat"); } return Status::OK(); } Status StarGiftManager::check_star_gift_ids(const vector &star_gift_ids, DialogId dialog_id) const { for (const auto &star_gift_id : star_gift_ids) { TRY_STATUS(check_star_gift_id(star_gift_id, dialog_id)); } return Status::OK(); } void StarGiftManager::set_dialog_pinned_gifts(DialogId dialog_id, const vector &star_gift_ids, Promise &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "set_dialog_pinned_gifts")); TRY_STATUS_PROMISE(promise, check_star_gift_ids(star_gift_ids, dialog_id)); td_->create_handler(std::move(promise))->send(dialog_id, star_gift_ids); } void StarGiftManager::toggle_chat_star_gift_notifications(DialogId dialog_id, bool are_enabled, Promise &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "toggle_chat_star_gift_notifications")); if (!td_->dialog_manager_->is_broadcast_channel(dialog_id) || !td_->chat_manager_->get_channel_status(dialog_id.get_channel_id()).can_post_messages()) { return promise.set_error(400, "Wrong chat specified"); } td_->create_handler(std::move(promise))->send(dialog_id, are_enabled); } void StarGiftManager::get_gift_upgrade_preview(int64 gift_id, Promise> &&promise) { td_->create_handler(std::move(promise))->send(gift_id); } void StarGiftManager::get_gift_upgrade_variants(int64 gift_id, bool return_upgrade_models, bool return_craft_models, Promise> &&promise) { td_->create_handler(std::move(promise)) ->send(gift_id, return_upgrade_models, return_craft_models); } void StarGiftManager::upgrade_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, bool keep_original_details, int64 star_count, Promise> &&promise) { bool as_business = business_connection_id.is_valid(); if (as_business) { TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); } auto input_saved_star_gift = star_gift_id.get_input_saved_star_gift(td_); if (input_saved_star_gift == nullptr) { return promise.set_error(400, "Invalid gift identifier specified"); } if (star_count < 0) { return promise.set_error(400, "Invalid amount of Telegram Stars specified"); } if (star_count != 0) { if (!as_business && !td_->star_manager_->has_owned_star_count(star_count)) { return promise.set_error(400, "Have not enough Telegram Stars"); } auto input_invoice = telegram_api::make_object( 0, keep_original_details, std::move(input_saved_star_gift)); auto upgrade_input_invoice = telegram_api::make_object( 0, keep_original_details, star_gift_id.get_input_saved_star_gift(td_)); td_->create_handler(std::move(promise)) ->send(business_connection_id, std::move(input_invoice), std::move(upgrade_input_invoice), star_count); } else { td_->create_handler(std::move(promise)) ->send(business_connection_id, star_gift_id, keep_original_details); } } void StarGiftManager::buy_gift_upgrade(DialogId dialog_id, const string &prepaid_upgrade_hash, int64 star_count, Promise &&promise) { auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); auto upgrade_input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); if (input_peer == nullptr || upgrade_input_peer == nullptr) { return promise.set_error(400, "Have no access to the gift owner"); } if (star_count <= 0) { return promise.set_error(400, "Invalid amount of Telegram Stars specified"); } if (!td_->star_manager_->has_owned_star_count(star_count)) { return promise.set_error(400, "Have not enough Telegram Stars"); } auto input_invoice = telegram_api::make_object( std::move(input_peer), prepaid_upgrade_hash); auto upgrade_input_invoice = telegram_api::make_object( std::move(upgrade_input_peer), prepaid_upgrade_hash); td_->create_handler(std::move(promise)) ->send(std::move(input_invoice), std::move(upgrade_input_invoice), star_count); } void StarGiftManager::craft_gift(const vector &star_gift_ids, Promise> &&promise) { TRY_STATUS_PROMISE(promise, check_star_gift_ids(star_gift_ids, td_->dialog_manager_->get_my_dialog_id())); td_->create_handler(std::move(promise))->send(star_gift_ids); } void StarGiftManager::transfer_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, DialogId receiver_dialog_id, int64 star_count, Promise &&promise) { bool as_business = business_connection_id.is_valid(); if (as_business) { TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); } auto access_rights = as_business ? AccessRights::Know : AccessRights::Read; auto input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, access_rights); auto transfer_input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, access_rights); if (input_peer == nullptr || transfer_input_peer == nullptr) { return promise.set_error(400, "Have no access to the new gift owner"); } auto input_saved_star_gift = star_gift_id.get_input_saved_star_gift(td_); if (input_saved_star_gift == nullptr) { return promise.set_error(400, "Invalid gift identifier specified"); } if (star_count < 0) { return promise.set_error(400, "Invalid amount of Telegram Stars specified"); } auto dialog_id = as_business ? DialogId(td_->business_connection_manager_->get_business_connection_user_id(business_connection_id)) : star_gift_id.get_dialog_id(td_); auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, receiver_dialog_id, promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(actor_id, &StarGiftManager::on_dialog_gift_transferred, dialog_id, receiver_dialog_id, std::move(promise)); }); if (star_count != 0) { if (!as_business && !td_->star_manager_->has_owned_star_count(star_count)) { return query_promise.set_error(400, "Have not enough Telegram Stars"); } auto input_invoice = telegram_api::make_object( std::move(input_saved_star_gift), std::move(input_peer)); auto transfer_input_invoice = telegram_api::make_object( star_gift_id.get_input_saved_star_gift(td_), std::move(transfer_input_peer)); td_->create_handler(std::move(query_promise)) ->send(business_connection_id, std::move(input_invoice), std::move(transfer_input_invoice), star_count); } else { td_->create_handler(std::move(query_promise)) ->send(business_connection_id, star_gift_id, std::move(input_peer)); } } void StarGiftManager::on_dialog_gift_transferred(DialogId from_dialog_id, DialogId to_dialog_id, Promise &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); if (from_dialog_id == td_->dialog_manager_->get_my_dialog_id()) { td_->user_manager_->on_update_my_gift_count(-1); } else if (from_dialog_id.get_type() == DialogType::Channel) { td_->chat_manager_->on_update_channel_gift_count(from_dialog_id.get_channel_id(), -1, true); } if (to_dialog_id == td_->dialog_manager_->get_my_dialog_id()) { td_->user_manager_->on_update_my_gift_count(1); } else if (to_dialog_id.get_type() == DialogType::Channel && td_->chat_manager_->get_channel_status(to_dialog_id.get_channel_id()).can_post_messages()) { td_->chat_manager_->on_update_channel_gift_count(to_dialog_id.get_channel_id(), 1, true); } promise.set_value(Unit()); } void StarGiftManager::drop_gift_original_details(StarGiftId star_gift_id, int64 star_count, Promise &&promise) { auto input_saved_star_gift = star_gift_id.get_input_saved_star_gift(td_); if (input_saved_star_gift == nullptr) { return promise.set_error(400, "Invalid gift identifier specified"); } if (star_count <= 0) { return promise.set_error(400, "Invalid amount of Telegram Stars specified"); } if (!td_->star_manager_->has_owned_star_count(star_count)) { return promise.set_error(400, "Have not enough Telegram Stars"); } auto input_invoice = telegram_api::make_object( std::move(input_saved_star_gift)); auto drop_input_invoice = telegram_api::make_object( star_gift_id.get_input_saved_star_gift(td_)); td_->create_handler(std::move(promise)) ->send(std::move(input_invoice), std::move(drop_input_invoice), star_count); } void StarGiftManager::send_resold_gift(const string &gift_name, DialogId receiver_dialog_id, StarGiftResalePrice price, Promise> &&promise) { auto input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, AccessRights::Read); auto resale_input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, AccessRights::Read); if (input_peer == nullptr || resale_input_peer == nullptr) { return promise.set_error(400, "Have no access to the new gift owner"); } if (!td_->star_manager_->has_owned_amount(price)) { return promise.set_error(400, "Have not enough funds"); } auto input_invoice = telegram_api::make_object(0, price.is_ton(), gift_name, std::move(input_peer)); auto resale_input_invoice = telegram_api::make_object( 0, price.is_ton(), gift_name, std::move(resale_input_peer)); StarGiftId star_gift_id; if (receiver_dialog_id == td_->dialog_manager_->get_my_dialog_id()) { star_gift_id = StarGiftId::from_slug(gift_name); } td_->create_handler(std::move(promise)) ->send(std::move(input_invoice), std::move(resale_input_invoice), price, std::move(star_gift_id)); } void StarGiftManager::send_gift_offer(DialogId owner_dialog_id, const string &gift_name, StarGiftResalePrice price, int32 duration, int64 paid_message_star_count, Promise &&promise) { if (!td_->dialog_manager_->have_input_peer(owner_dialog_id, false, AccessRights::Read)) { return promise.set_error(400, "Can't access the chat"); } if (paid_message_star_count < 0) { return promise.set_error(400, "Invalid amount of Telegram Stars specified"); } if (!td_->star_manager_->has_owned_star_count(paid_message_star_count)) { return promise.set_error(400, "Have not enough Telegram Stars"); } td_->star_manager_->add_pending_owned_star_count(-paid_message_star_count, false); if (!td_->star_manager_->has_owned_amount(price)) { td_->star_manager_->add_pending_owned_star_count(paid_message_star_count, false); return promise.set_error(400, "Have not enough funds"); } td_->star_manager_->add_pending_owned_amount(price, -1, false); td_->create_handler(std::move(promise)) ->send(owner_dialog_id, gift_name, price, duration, paid_message_star_count); } void StarGiftManager::process_gift_offer(MessageId message_id, bool decline, Promise &&promise) { if (!message_id.is_server()) { return promise.set_error(400, "Invalid offer message identifier specified"); } td_->create_handler(std::move(promise))->send(message_id, decline); } void StarGiftManager::get_saved_star_gifts(BusinessConnectionId business_connection_id, DialogId dialog_id, StarGiftCollectionId collection_id, bool exclude_unsaved, bool exclude_saved, bool exclude_unlimited, bool exclude_upgradable, bool exclude_non_upgradable, bool exclude_unique, bool peer_color_available, bool exclude_hosted, bool sort_by_value, const string &offset, int32 limit, Promise> &&promise) { if (limit < 0) { return promise.set_error(400, "Limit must be non-negative"); } if (business_connection_id.is_valid()) { TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); } td_->create_handler(std::move(promise)) ->send(business_connection_id, dialog_id, collection_id, exclude_unsaved, exclude_saved, exclude_unlimited, exclude_upgradable, exclude_non_upgradable, exclude_unique, peer_color_available, exclude_hosted, sort_by_value, offset, limit); } void StarGiftManager::get_saved_star_gift(StarGiftId star_gift_id, Promise> &&promise) { if (!star_gift_id.is_valid()) { return promise.set_error(400, "Invalid gift identifier specified"); } td_->create_handler(std::move(promise))->send(star_gift_id); } void StarGiftManager::get_craft_star_gifts(int64 gift_id, const string &offset, int32 limit, Promise> &&promise) { if (limit < 0) { return promise.set_error(400, "Limit must be non-negative"); } td_->create_handler(std::move(promise))->send(gift_id, offset, limit); } void StarGiftManager::get_upgraded_gift(const string &name, Promise> &&promise) { td_->create_handler(std::move(promise))->send(name); } void StarGiftManager::get_upgraded_gift_value_info( const string &name, Promise> &&promise) { td_->create_handler(std::move(promise))->send(name); } void StarGiftManager::get_star_gift_withdrawal_url(StarGiftId star_gift_id, const string &password, Promise &&promise) { if (!star_gift_id.is_valid()) { return promise.set_error(400, "Invalid gift identifier specified"); } if (password.empty()) { return promise.set_error(400, "PASSWORD_HASH_INVALID"); } send_closure( td_->password_manager_, &PasswordManager::get_input_check_password_srp, password, PromiseCreator::lambda([actor_id = actor_id(this), star_gift_id, promise = std::move(promise)]( Result> result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(actor_id, &StarGiftManager::send_get_star_gift_withdrawal_url_query, star_gift_id, result.move_as_ok(), std::move(promise)); })); } void StarGiftManager::send_get_star_gift_withdrawal_url_query( StarGiftId star_gift_id, telegram_api::object_ptr input_check_password, Promise &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); td_->create_handler(std::move(promise)) ->send(star_gift_id, std::move(input_check_password)); } void StarGiftManager::set_star_gift_price(StarGiftId star_gift_id, StarGiftResalePrice price, Promise &&promise) { if (!star_gift_id.is_valid()) { return promise.set_error(400, "Invalid gift identifier specified"); } td_->create_handler(std::move(promise))->send(star_gift_id, price); } void StarGiftManager::get_resale_star_gifts( int64 gift_id, const td_api::object_ptr &order, bool for_craft, bool for_stars, const vector> &attributes, const string &offset, int32 limit, Promise> &&promise) { if (limit < 0) { return promise.set_error(400, "Limit must be non-negative"); } if (order == nullptr) { return promise.set_error(400, "Gift sort order must be non-empty"); } TRY_RESULT_PROMISE(promise, attribute_ids, StarGiftAttributeId::get_star_gift_attribute_ids(attributes)); td_->create_handler(std::move(promise)) ->send(gift_id, order, for_craft, for_stars, attribute_ids, offset, limit); } void StarGiftManager::get_gift_collections(DialogId dialog_id, Promise> &&promise) { TRY_STATUS_PROMISE( promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "get_gift_collections")); td_->create_handler(std::move(promise))->send(dialog_id); } void StarGiftManager::create_gift_collection(DialogId dialog_id, const string &title, const vector &star_gift_ids, Promise> &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "create_gift_collection")); TRY_STATUS_PROMISE(promise, check_star_gift_ids(star_gift_ids, dialog_id)); td_->create_handler(std::move(promise))->send(dialog_id, title, star_gift_ids); } void StarGiftManager::reorder_gift_collections(DialogId dialog_id, const vector &collection_ids, Promise &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "reorder_gift_collections")); for (auto collection_id : collection_ids) { if (!collection_id.is_valid()) { return promise.set_error(400, "Invalid collection identifier specified"); } } td_->create_handler(std::move(promise))->send(dialog_id, collection_ids); } void StarGiftManager::delete_gift_collection(DialogId dialog_id, StarGiftCollectionId collection_id, Promise &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "delete_gift_collection")); if (!collection_id.is_valid()) { return promise.set_error(400, "Invalid collection identifier specified"); } td_->create_handler(std::move(promise))->send(dialog_id, collection_id); } void StarGiftManager::set_gift_collection_title(DialogId dialog_id, StarGiftCollectionId collection_id, const string &title, Promise> &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "set_gift_collection_title")); if (!collection_id.is_valid()) { return promise.set_error(400, "Invalid collection identifier specified"); } if (title.empty()) { return promise.set_error(400, "Gift collection name must be non-empty"); } td_->create_handler(std::move(promise)) ->send(dialog_id, collection_id, title, {}, {}, {}); } void StarGiftManager::add_gift_collection_gifts(DialogId dialog_id, StarGiftCollectionId collection_id, const vector &star_gift_ids, Promise> &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "add_gift_collection_gifts")); if (!collection_id.is_valid()) { return promise.set_error(400, "Invalid collection identifier specified"); } if (star_gift_ids.empty()) { return promise.set_error(400, "Gift list must be non-empty"); } td_->create_handler(std::move(promise)) ->send(dialog_id, collection_id, {}, {}, star_gift_ids, {}); } void StarGiftManager::remove_gift_collection_gifts(DialogId dialog_id, StarGiftCollectionId collection_id, const vector &star_gift_ids, Promise> &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "remove_gift_collection_gifts")); if (!collection_id.is_valid()) { return promise.set_error(400, "Invalid collection identifier specified"); } if (star_gift_ids.empty()) { return promise.set_error(400, "Gift list must be non-empty"); } td_->create_handler(std::move(promise)) ->send(dialog_id, collection_id, {}, star_gift_ids, {}, {}); } void StarGiftManager::reorder_gift_collection_gifts(DialogId dialog_id, StarGiftCollectionId collection_id, const vector &star_gift_ids, Promise> &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, "reorder_gift_collection_gifts")); if (!collection_id.is_valid()) { return promise.set_error(400, "Invalid collection identifier specified"); } if (star_gift_ids.empty()) { return promise.set_error(400, "Gift list must be non-empty"); } td_->create_handler(std::move(promise)) ->send(dialog_id, collection_id, {}, {}, {}, star_gift_ids); } void StarGiftManager::get_star_gift_promo_animation(Promise> &&promise) { td_->create_handler(std::move(promise))->send(); } void StarGiftManager::register_gift(MessageFullId message_full_id, const char *source) { auto message_id = message_full_id.get_message_id(); if (message_id.is_scheduled()) { return; } CHECK(!td_->auth_manager_->is_bot()); CHECK(message_id.is_server()); LOG(INFO) << "Register gift in " << message_full_id << " from " << source; auto gift_message_number = ++gift_message_count_; gift_message_full_ids_.set(message_full_id, gift_message_number); gift_message_full_ids_by_id_.set(gift_message_number, message_full_id); update_gift_message_timeout_.add_timeout_in(gift_message_number, 0); } void StarGiftManager::unregister_gift(MessageFullId message_full_id, const char *source) { auto message_id = message_full_id.get_message_id(); if (message_id.is_scheduled()) { return; } CHECK(!td_->auth_manager_->is_bot()); CHECK(message_id.is_server()); LOG(INFO) << "Unregister gift in " << message_full_id << " from " << source; auto message_number = gift_message_full_ids_[message_full_id]; LOG_CHECK(message_number != 0) << source << ' ' << message_full_id; gift_message_full_ids_by_id_.erase(message_number); if (!G()->close_flag()) { update_gift_message_timeout_.cancel_timeout(message_number); } gift_message_full_ids_.erase(message_full_id); } double StarGiftManager::get_gift_message_polling_timeout() const { double result = td_->online_manager_->is_online() ? 60 : 30 * 60; return result * Random::fast(70, 100) * 0.01; } void StarGiftManager::on_online() { if (td_->auth_manager_->is_bot()) { return; } gift_message_full_ids_.foreach([&](MessageFullId, int64 message_number) { if (update_gift_message_timeout_.has_timeout(message_number)) { update_gift_message_timeout_.set_timeout_in(message_number, Random::fast(3, 30)); } }); } void StarGiftManager::on_update_gift_message_timeout_callback(void *star_gift_manager_ptr, int64 message_number) { if (G()->close_flag()) { return; } auto star_gift_manager = static_cast(star_gift_manager_ptr); send_closure_later(star_gift_manager->actor_id(star_gift_manager), &StarGiftManager::on_update_gift_message_timeout, message_number); } void StarGiftManager::on_update_gift_message_timeout(int64 message_number) { if (G()->close_flag()) { return; } CHECK(!td_->auth_manager_->is_bot()); auto message_full_id = gift_message_full_ids_by_id_.get(message_number); if (message_full_id.get_message_id() == MessageId()) { return; } if (!being_reloaded_gift_messages_.insert(message_full_id).second) { return; } LOG(INFO) << "Fetching gift from " << message_full_id; auto promise = PromiseCreator::lambda([actor_id = actor_id(this), message_full_id](Unit result) { send_closure(actor_id, &StarGiftManager::on_update_gift_message, message_full_id); }); td_->messages_manager_->get_message_from_server(message_full_id, std::move(promise), "on_update_gift_message_timeout"); } void StarGiftManager::on_update_gift_message(MessageFullId message_full_id) { if (G()->close_flag()) { return; } auto is_erased = being_reloaded_gift_messages_.erase(message_full_id) > 0; CHECK(is_erased); auto message_number = gift_message_full_ids_.get(message_full_id); if (message_number == 0) { return; } auto timeout = get_gift_message_polling_timeout(); LOG(INFO) << "Schedule updating of gift in " << message_full_id << " in " << timeout; update_gift_message_timeout_.add_timeout_in(message_number, timeout); } void StarGiftManager::on_reload_gift_auction_timeout_callback(void *star_gift_manager_ptr, int64 gift_id) { if (G()->close_flag()) { return; } auto star_gift_manager = static_cast(star_gift_manager_ptr); send_closure_later(star_gift_manager->actor_id(star_gift_manager), &StarGiftManager::on_reload_gift_auction_timeout, gift_id); } void StarGiftManager::on_reload_gift_auction_timeout(int64 gift_id) { if (G()->close_flag()) { return; } CHECK(!td_->auth_manager_->is_bot()); int32 version = 0; auto it = gift_auction_infos_.find(gift_id); if (it != gift_auction_infos_.end()) { version = it->second.state_.get_version(); } reload_gift_auction_state(telegram_api::make_object(gift_id), version, Auto()); } } // namespace td