diff --git a/CMakeLists.txt b/CMakeLists.txt index 276606d..628b0bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 7.3 LANGUAGES CXX) +project(TelegramBotApi VERSION 7.7 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/build.html b/build.html index 0ecaa80..32f4948 100644 --- a/build.html +++ b/build.html @@ -208,6 +208,7 @@ +

@@ -260,7 +261,7 @@
- +

@@ -446,7 +447,7 @@ function onOptionsChanged() { pre_text.push('Install Git, ' + compiler + ', make, CMake >= 3.0.2, OpenSSL-dev, zlib-dev, gperf using your package manager.'); } if (os_freebsd) { - pre_text.push('Note that the following instruction is for FreeBSD 11.'); + pre_text.push('Note that the following instruction is for FreeBSD 13.'); pre_text.push('Note that the following calls to pkg needs to be run as root.'); } if (os_openbsd) { @@ -496,6 +497,19 @@ function onOptionsChanged() { return '-10'; case 'Ubuntu 22': return '-14'; + case 'Ubuntu 24': + return '-18'; + default: + return ''; // use default version + } + } + + function getLibcplusplusVersionSuffix() { + switch (linux_distro) { + case 'Ubuntu 20': + case 'Ubuntu 22': + case 'Ubuntu 24': + return getClangVersionSuffix(); default: return ''; // use default version } @@ -514,7 +528,7 @@ function onOptionsChanged() { commands.push(sudo + 'apk update'); commands.push(sudo + 'apk upgrade'); var packages = 'alpine-sdk linux-headers git zlib-dev openssl-dev gperf cmake'; - commands.push(sudo + 'apk add --update ' + packages); + commands.push(sudo + 'apk add ' + packages); break; case 'CentOS 7': case 'CentOS 8': @@ -538,6 +552,7 @@ function onOptionsChanged() { case 'Ubuntu 18': case 'Ubuntu 20': case 'Ubuntu 22': + case 'Ubuntu 24': if (linux_distro.includes('Debian') && !use_root) { commands.push('su -'); } @@ -553,9 +568,9 @@ function onOptionsChanged() { packages += ' cmake'; } if (use_clang) { - packages += ' clang' + getClangVersionSuffix() + ' libc++-dev'; - if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Ubuntu 22') { - packages += ' libc++abi-dev'; + packages += ' clang' + getClangVersionSuffix() + ' libc++' + getLibcplusplusVersionSuffix() + '-dev'; + if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Ubuntu 22' || linux_distro === 'Ubuntu 24') { + packages += ' libc++abi' + getLibcplusplusVersionSuffix() + '-dev'; } } else { packages += ' g++'; @@ -587,7 +602,7 @@ function onOptionsChanged() { commands.push('su -'); } commands.push('export PKG_PATH=http://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r)/All'); - var packages = 'git gperf cmake openssl gcc12-libs mozilla-rootcerts-openssl'; + var packages = 'git gperf pcre2 cmake openssl gcc12-libs mozilla-rootcerts-openssl'; commands.push('pkg_add ' + packages); if (!use_root) { commands.push('exit'); diff --git a/td b/td index af69dd4..cb16492 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit af69dd4397b6dc1bf23ba0fd0bf429fcba6454f6 +Subproject commit cb164927417f22811c74cd8678ed4a5ab7cb80ba diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 2502cdb..adf3a7a 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) @@ -243,6 +243,7 @@ bool Client::init_methods() { methods_.emplace("sendvideo", &Client::process_send_video_query); methods_.emplace("sendvideonote", &Client::process_send_video_note_query); methods_.emplace("sendvoice", &Client::process_send_voice_query); + methods_.emplace("sendpaidmedia", &Client::process_send_paid_media_query); methods_.emplace("sendgame", &Client::process_send_game_query); methods_.emplace("sendinvoice", &Client::process_send_invoice_query); methods_.emplace("sendlocation", &Client::process_send_location_query); @@ -266,6 +267,8 @@ bool Client::init_methods() { methods_.emplace("deletemessage", &Client::process_delete_message_query); methods_.emplace("deletemessages", &Client::process_delete_messages_query); methods_.emplace("createinvoicelink", &Client::process_create_invoice_link_query); + methods_.emplace("getstartransactions", &Client::process_get_star_transactions_query); + methods_.emplace("refundstarpayment", &Client::process_refund_star_payment_query); methods_.emplace("setgamescore", &Client::process_set_game_score_query); methods_.emplace("getgamehighscores", &Client::process_get_game_high_scores_query); methods_.emplace("answerwebappquery", &Client::process_answer_web_app_query_query); @@ -590,6 +593,9 @@ class Client::JsonEntity final : public td::Jsonable { case td_api::textEntityTypeBlockQuote::ID: object("type", "blockquote"); break; + case td_api::textEntityTypeExpandableBlockQuote::ID: + object("type", "expandable_blockquote"); + break; default: UNREACHABLE(); } @@ -967,7 +973,8 @@ class Client::JsonMessage final : public td::Jsonable { const td::string &source_; const Client *client_; - void add_caption(td::JsonObjectScope &object, const object_ptr &caption) const { + void add_caption(td::JsonObjectScope &object, const object_ptr &caption, + bool show_caption_above_media) const { CHECK(caption != nullptr); if (!caption->text_.empty()) { object("caption", caption->text_); @@ -975,6 +982,10 @@ class Client::JsonMessage final : public td::Jsonable { if (!caption->entities_.empty()) { object("caption_entities", JsonVectorEntities(caption->entities_, client_)); } + + if (show_caption_above_media) { + object("show_caption_above_media", td::JsonTrue()); + } } } @@ -1081,7 +1092,7 @@ class Client::JsonChat final : public td::Jsonable { permissions->can_send_basic_messages_ && permissions->can_send_audios_ && permissions->can_send_documents_ && permissions->can_send_photos_ && permissions->can_send_videos_ && permissions->can_send_video_notes_ && permissions->can_send_voice_notes_ && permissions->can_send_polls_ && - permissions->can_send_other_messages_ && permissions->can_add_web_page_previews_ && + permissions->can_send_other_messages_ && permissions->can_add_link_previews_ && permissions->can_change_info_ && permissions->can_invite_users_ && permissions->can_pin_messages_; object("all_members_are_administrators", td::JsonBool(everyone_is_administrator)); photo = group_info->photo.get(); @@ -1174,6 +1185,9 @@ class Client::JsonChat final : public td::Jsonable { if (supergroup_info->location != nullptr) { object("location", JsonChatLocation(supergroup_info->location.get())); } + if (supergroup_info->has_paid_media_allowed && !supergroup_info->is_supergroup) { + object("can_send_paid_media", td::JsonTrue()); + } } photo = supergroup_info->photo.get(); break; @@ -1614,6 +1628,70 @@ class Client::JsonVoiceNote final : public td::Jsonable { const Client *client_; }; +class Client::JsonPaidMedia final : public td::Jsonable { + public: + JsonPaidMedia(const td_api::PaidMedia *paid_media, const Client *client) : paid_media_(paid_media), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + switch (paid_media_->get_id()) { + case td_api::paidMediaPreview::ID: { + auto media = static_cast(paid_media_); + object("type", "preview"); + if (media->width_) { + object("width", media->width_); + } + if (media->height_) { + object("height", media->height_); + } + if (media->duration_) { + object("duration", media->duration_); + } + break; + } + case td_api::paidMediaPhoto::ID: { + auto media = static_cast(paid_media_); + object("type", "photo"); + object("photo", JsonPhoto(media->photo_.get(), client_)); + break; + } + case td_api::paidMediaVideo::ID: { + auto media = static_cast(paid_media_); + object("type", "video"); + object("video", JsonVideo(media->video_.get(), client_)); + break; + } + case td_api::paidMediaUnsupported::ID: + object("type", "other"); + break; + default: + UNREACHABLE(); + } + } + + private: + const td_api::PaidMedia *paid_media_; + const Client *client_; +}; + +class Client::JsonPaidMediaInfo final : public td::Jsonable { + public: + JsonPaidMediaInfo(const td_api::messagePaidMedia *paid_media, const Client *client) + : paid_media_(paid_media), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("star_count", paid_media_->star_count_); + object("paid_media", td::json_array(paid_media_->media_, [client = client_](auto &media) { + return JsonPaidMedia(media.get(), client); + })); + } + + private: + const td_api::messagePaidMedia *paid_media_; + const Client *client_; +}; + class Client::JsonVenue final : public td::Jsonable { public: explicit JsonVenue(const td_api::venue *venue) : venue_(venue) { @@ -1715,12 +1793,12 @@ class Client::JsonInvoice final : public td::Jsonable { } void store(td::JsonValueScope *scope) const { auto object = scope->enter_object(); - object("title", invoice_->title_); - object("description", invoice_->description_->text_); + object("title", invoice_->product_info_->title_); + object("description", invoice_->product_info_->description_->text_); object("start_parameter", invoice_->start_parameter_); object("currency", invoice_->currency_); object("total_amount", invoice_->total_amount_); - // skip photo + // skip product_info_->photo // skip is_test // skip need_shipping_address // skip receipt_message_id @@ -2091,7 +2169,6 @@ class Client::JsonSuccessfulPaymentBot final : public td::Jsonable { if (successful_payment_->order_info_ != nullptr) { object("order_info", JsonOrderInfo(successful_payment_->order_info_.get())); } - object("telegram_payment_charge_id", successful_payment_->telegram_payment_charge_id_); object("provider_payment_charge_id", successful_payment_->provider_payment_charge_id_); } @@ -2100,6 +2177,31 @@ class Client::JsonSuccessfulPaymentBot final : public td::Jsonable { const td_api::messagePaymentSuccessfulBot *successful_payment_; }; +class Client::JsonRefundedPayment final : public td::Jsonable { + public: + explicit JsonRefundedPayment(const td_api::messagePaymentRefunded *refunded_payment) + : refunded_payment_(refunded_payment) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("currency", refunded_payment_->currency_); + object("total_amount", refunded_payment_->total_amount_); + if (!td::check_utf8(refunded_payment_->invoice_payload_)) { + LOG(WARNING) << "Receive non-UTF-8 invoice payload"; + object("invoice_payload", td::JsonRawString(refunded_payment_->invoice_payload_)); + } else { + object("invoice_payload", refunded_payment_->invoice_payload_); + } + object("telegram_payment_charge_id", refunded_payment_->telegram_payment_charge_id_); + if (!refunded_payment_->provider_payment_charge_id_.empty()) { + object("provider_payment_charge_id", refunded_payment_->provider_payment_charge_id_); + } + } + + private: + const td_api::messagePaymentRefunded *refunded_payment_; +}; + class Client::JsonEncryptedPassportElement final : public td::Jsonable { public: JsonEncryptedPassportElement(const td_api::encryptedPassportElement *element, const Client *client) @@ -2681,6 +2783,11 @@ class Client::JsonExternalReplyInfo final : public td::Jsonable { object("document", JsonDocument(content->document_.get(), client_)); break; } + case td_api::messagePaidMedia::ID: { + auto content = static_cast(reply_->content_.get()); + object("paid_media", JsonPaidMediaInfo(content, client_)); + break; + } case td_api::messagePhoto::ID: { auto content = static_cast(reply_->content_.get()); CHECK(content->photo_ != nullptr); @@ -2930,27 +3037,33 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { auto content = static_cast(message_->content.get()); object("animation", JsonAnimation(content->animation_.get(), false, client_)); object("document", JsonAnimation(content->animation_.get(), true, client_)); - add_caption(object, content->caption_); + add_caption(object, content->caption_, content->show_caption_above_media_); add_media_spoiler(object, content->has_spoiler_); break; } case td_api::messageAudio::ID: { auto content = static_cast(message_->content.get()); object("audio", JsonAudio(content->audio_.get(), client_)); - add_caption(object, content->caption_); + add_caption(object, content->caption_, false); break; } case td_api::messageDocument::ID: { auto content = static_cast(message_->content.get()); object("document", JsonDocument(content->document_.get(), client_)); - add_caption(object, content->caption_); + add_caption(object, content->caption_, false); + break; + } + case td_api::messagePaidMedia::ID: { + auto content = static_cast(message_->content.get()); + object("paid_media", JsonPaidMediaInfo(content, client_)); + add_caption(object, content->caption_, content->show_caption_above_media_); break; } case td_api::messagePhoto::ID: { auto content = static_cast(message_->content.get()); CHECK(content->photo_ != nullptr); object("photo", JsonPhoto(content->photo_.get(), client_)); - add_caption(object, content->caption_); + add_caption(object, content->caption_, content->show_caption_above_media_); add_media_spoiler(object, content->has_spoiler_); break; } @@ -2962,7 +3075,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { case td_api::messageVideo::ID: { auto content = static_cast(message_->content.get()); object("video", JsonVideo(content->video_.get(), client_)); - add_caption(object, content->caption_); + add_caption(object, content->caption_, content->show_caption_above_media_); add_media_spoiler(object, content->has_spoiler_); break; } @@ -2974,7 +3087,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { case td_api::messageVoiceNote::ID: { auto content = static_cast(message_->content.get()); object("voice", JsonVoiceNote(content->voice_note_.get(), client_)); - add_caption(object, content->caption_); + add_caption(object, content->caption_, false); break; } case td_api::messageContact::ID: { @@ -3283,6 +3396,11 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("boost_added", JsonChatBoostAdded(content)); break; } + case td_api::messagePaymentRefunded::ID: { + auto content = static_cast(message_->content.get()); + object("refunded_payment", JsonRefundedPayment(content)); + break; + } default: UNREACHABLE(); } @@ -3301,6 +3419,9 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { if (message_->is_from_offline) { object("is_from_offline", td::JsonTrue()); } + if (message_->effect_id != 0) { + object("effect_id", td::to_string(message_->effect_id)); + } } class Client::JsonMessageId final : public td::Jsonable { @@ -4053,6 +4174,128 @@ class Client::JsonBusinessMessagesDeleted final : public td::Jsonable { const Client *client_; }; +class Client::JsonRevenueWithdrawalState final : public td::Jsonable { + public: + explicit JsonRevenueWithdrawalState(const td_api::RevenueWithdrawalState *state) : state_(state) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + switch (state_->get_id()) { + case td_api::revenueWithdrawalStatePending::ID: + object("type", "pending"); + break; + case td_api::revenueWithdrawalStateSucceeded::ID: { + auto state = static_cast(state_); + object("type", "succeeded"); + object("date", state->date_); + object("url", state->url_); + break; + } + case td_api::revenueWithdrawalStateFailed::ID: + object("type", "failed"); + break; + default: + UNREACHABLE(); + } + } + + private: + const td_api::RevenueWithdrawalState *state_; +}; + +class Client::JsonStarTransactionPartner final : public td::Jsonable { + public: + JsonStarTransactionPartner(const td_api::StarTransactionPartner *source, const Client *client) + : source_(source), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + switch (source_->get_id()) { + case td_api::starTransactionPartnerFragment::ID: { + auto source_fragment = static_cast(source_); + object("type", "fragment"); + if (source_fragment->withdrawal_state_ != nullptr) { + object("withdrawal_state", JsonRevenueWithdrawalState(source_fragment->withdrawal_state_.get())); + } + break; + } + case td_api::starTransactionPartnerBot::ID: { + auto source_user = static_cast(source_); + object("type", "user"); + object("user", JsonUser(source_user->bot_user_id_, client_)); + if (!source_user->invoice_payload_.empty()) { + if (!td::check_utf8(source_user->invoice_payload_)) { + LOG(WARNING) << "Receive non-UTF-8 invoice payload"; + object("invoice_payload", td::JsonRawString(source_user->invoice_payload_)); + } else { + object("invoice_payload", source_user->invoice_payload_); + } + } + break; + } + case td_api::starTransactionPartnerTelegramAds::ID: + object("type", "telegram_ads"); + break; + case td_api::starTransactionPartnerTelegram::ID: + case td_api::starTransactionPartnerAppStore::ID: + case td_api::starTransactionPartnerGooglePlay::ID: + case td_api::starTransactionPartnerChannel::ID: + LOG(ERROR) << "Receive " << to_string(*source_); + object("type", "other"); + break; + case td_api::starTransactionPartnerUnsupported::ID: + object("type", "other"); + break; + default: + UNREACHABLE(); + } + } + + private: + const td_api::StarTransactionPartner *source_; + const Client *client_; +}; + +class Client::JsonStarTransaction final : public td::Jsonable { + public: + JsonStarTransaction(const td_api::starTransaction *transaction, const Client *client) + : transaction_(transaction), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("id", transaction_->id_); + object("date", transaction_->date_); + if (transaction_->star_count_ > 0) { + object("amount", transaction_->star_count_); + object("source", JsonStarTransactionPartner(transaction_->partner_.get(), client_)); + } else { + object("amount", -transaction_->star_count_); + object("receiver", JsonStarTransactionPartner(transaction_->partner_.get(), client_)); + } + } + + private: + const td_api::starTransaction *transaction_; + const Client *client_; +}; + +class Client::JsonStarTransactions final : public td::Jsonable { + public: + JsonStarTransactions(const td_api::starTransactions *transactions, const Client *client) + : transactions_(transactions), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("transactions", td::json_array(transactions_->transactions_, [client = client_](const auto &transaction) { + return JsonStarTransaction(transaction.get(), client); + })); + } + + private: + const td_api::starTransactions *transactions_; + const Client *client_; +}; + class Client::JsonUpdateTypes final : public td::Jsonable { public: explicit JsonUpdateTypes(td::uint32 update_types) : update_types_(update_types) { @@ -4503,9 +4746,9 @@ class Client::TdOnSendMessageCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; -class Client::TdOnSendBusinessMessageCallback final : public TdQueryCallback { +class Client::TdOnReturnBusinessMessageCallback final : public TdQueryCallback { public: - TdOnSendBusinessMessageCallback(Client *client, td::string business_connection_id, PromisedQueryPtr query) + TdOnReturnBusinessMessageCallback(Client *client, td::string business_connection_id, PromisedQueryPtr query) : client_(client), business_connection_id_(std::move(business_connection_id)), query_(std::move(query)) { } @@ -4517,7 +4760,7 @@ class Client::TdOnSendBusinessMessageCallback final : public TdQueryCallback { CHECK(result->get_id() == td_api::businessMessage::ID); auto message = client_->create_business_message(std::move(business_connection_id_), move_object_as(result)); - answer_query(JsonMessage(message.get(), true, "sent business message", client_), std::move(query_)); + answer_query(JsonMessage(message.get(), true, "business message", client_), std::move(query_)); } private: @@ -4733,6 +4976,34 @@ class Client::TdOnStopPollCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; +class Client::TdOnStopBusinessPollCallback final : public TdQueryCallback { + public: + TdOnStopBusinessPollCallback(Client *client, const td::string &business_connection_id, PromisedQueryPtr query) + : client_(client), business_connection_id_(business_connection_id), query_(std::move(query)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + return fail_query_with_error(std::move(query_), move_object_as(result)); + } + + CHECK(result->get_id() == td_api::businessMessage::ID); + auto message = client_->create_business_message(std::move(business_connection_id_), + move_object_as(result)); + if (message->content->get_id() != td_api::messagePoll::ID) { + LOG(ERROR) << "Poll not found in a business message from connection " << business_connection_id_; + return fail_query_with_error(std::move(query_), 400, "message poll not found"); + } + auto message_poll = static_cast(message->content.get()); + answer_query(JsonPoll(message_poll->poll_.get(), client_), std::move(query_)); + } + + private: + Client *client_; + td::string business_connection_id_; + PromisedQueryPtr query_; +}; + class Client::TdOnOkQueryCallback final : public TdQueryCallback { public: explicit TdOnOkQueryCallback(PromisedQueryPtr query) : query_(std::move(query)) { @@ -5133,7 +5404,7 @@ class Client::TdOnGetChatMemberCallback final : public TdQueryCallback { void on_result(object_ptr result) final { if (result->get_id() == td_api::error::ID) { - return fail_query_with_error(std::move(query_), move_object_as(result), "user not found"); + return fail_query_with_error(std::move(query_), move_object_as(result)); } CHECK(result->get_id() == td_api::chatMember::ID); @@ -5247,12 +5518,14 @@ class Client::TdOnGetCallbackQueryMessageCallback final : public TdQueryCallback class Client::TdOnGetStickerSetCallback final : public TdQueryCallback { public: TdOnGetStickerSetCallback(Client *client, int64 set_id, int64 new_callback_query_user_id, int64 new_message_chat_id, - const td::string &new_message_business_connection_id) + const td::string &new_message_business_connection_id, + int64 new_business_callback_query_user_id) : client_(client) , set_id_(set_id) , new_callback_query_user_id_(new_callback_query_user_id) , new_message_chat_id_(new_message_chat_id) - , new_message_business_connection_id_(new_message_business_connection_id) { + , new_message_business_connection_id_(new_message_business_connection_id) + , new_business_callback_query_user_id_(new_business_callback_query_user_id) { } void on_result(object_ptr result) final { @@ -5264,12 +5537,14 @@ class Client::TdOnGetStickerSetCallback final : public TdQueryCallback { << td::oneline(to_string(error)); } return client_->on_get_sticker_set(set_id_, new_callback_query_user_id_, new_message_chat_id_, - new_message_business_connection_id_, nullptr); + new_message_business_connection_id_, new_business_callback_query_user_id_, + nullptr); } CHECK(result->get_id() == td_api::stickerSet::ID); client_->on_get_sticker_set(set_id_, new_callback_query_user_id_, new_message_chat_id_, - new_message_business_connection_id_, move_object_as(result)); + new_message_business_connection_id_, new_business_callback_query_user_id_, + move_object_as(result)); } private: @@ -5278,6 +5553,7 @@ class Client::TdOnGetStickerSetCallback final : public TdQueryCallback { int64 new_callback_query_user_id_; int64 new_message_chat_id_; td::string new_message_business_connection_id_; + int64 new_business_callback_query_user_id_; }; class Client::TdOnGetChatCustomEmojiStickerSetCallback final : public TdQueryCallback { @@ -5460,7 +5736,7 @@ class Client::TdOnGetChatPinnedMessageToUnpinCallback final : public TdQueryCall if (error->code_ == 429) { return fail_query_with_error(std::move(query_), std::move(error)); } else { - return fail_query_with_error(std::move(query_), make_object(400, "Message to unpin not found")); + return fail_query_with_error(std::move(query_), 400, "Message to unpin not found"); } } @@ -5515,8 +5791,7 @@ class Client::TdOnGetMyDefaultAdministratorRightsCallback final : public TdQuery auto full_info = move_object_as(result); if (full_info->bot_info_ == nullptr) { LOG(ERROR) << "Have no bot info for self"; - return fail_query_with_error(std::move(query_), - make_object(500, "Requested data is inaccessible")); + return fail_query_with_error(std::move(query_), 500, "Requested data is inaccessible"); } auto bot_info = std::move(full_info->bot_info_); const auto *rights = for_channels_ ? bot_info->default_channel_administrator_rights_.get() @@ -5763,12 +6038,12 @@ class Client::TdOnGetChatInviteLinkCallback final : public TdQueryCallback { if (result->get_id() == td_api::chatInviteLink::ID) { auto invite_link = move_object_as(result); - return answer_query(JsonChatInviteLink(invite_link.get(), client_), std::move(query_)); + answer_query(JsonChatInviteLink(invite_link.get(), client_), std::move(query_)); } else { CHECK(result->get_id() == td_api::chatInviteLinks::ID); auto invite_links = move_object_as(result); CHECK(!invite_links->invite_links_.empty()); - return answer_query(JsonChatInviteLink(invite_links->invite_links_[0].get(), client_), std::move(query_)); + answer_query(JsonChatInviteLink(invite_links->invite_links_[0].get(), client_), std::move(query_)); } } @@ -5777,6 +6052,27 @@ class Client::TdOnGetChatInviteLinkCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; +class Client::TdOnGetStarTransactionsQueryCallback final : public TdQueryCallback { + public: + TdOnGetStarTransactionsQueryCallback(const Client *client, PromisedQueryPtr query) + : client_(client), query_(std::move(query)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + return fail_query_with_error(std::move(query_), move_object_as(result)); + } + + CHECK(result->get_id() == td_api::starTransactions::ID); + auto transactions = move_object_as(result); + answer_query(JsonStarTransactions(transactions.get(), client_), std::move(query_)); + } + + private: + const Client *client_; + PromisedQueryPtr query_; +}; + class Client::TdOnGetGameHighScoresCallback final : public TdQueryCallback { public: TdOnGetGameHighScoresCallback(const Client *client, PromisedQueryPtr query) @@ -6441,7 +6737,7 @@ void Client::on_get_callback_query_message(object_ptr message, void Client::on_get_sticker_set(int64 set_id, int64 new_callback_query_user_id, int64 new_message_chat_id, const td::string &new_message_business_connection_id, - object_ptr sticker_set) { + int64 new_business_callback_query_user_id, object_ptr sticker_set) { if (new_callback_query_user_id != 0) { auto &queue = new_callback_query_queues_[new_callback_query_user_id]; CHECK(queue.has_active_request_); @@ -6463,6 +6759,13 @@ void Client::on_get_sticker_set(int64 set_id, int64 new_callback_query_user_id, CHECK(!queue.queue_.empty()); } + if (new_business_callback_query_user_id != 0) { + auto &queue = new_business_callback_query_queues_[new_business_callback_query_user_id]; + CHECK(queue.has_active_request_); + queue.has_active_request_ = false; + + CHECK(!queue.queue_.empty()); + } CHECK(set_id != 0); if (set_id != GREAT_MINDS_SET_ID) { @@ -6481,6 +6784,9 @@ void Client::on_get_sticker_set(int64 set_id, int64 new_callback_query_user_id, if (!new_message_business_connection_id.empty()) { process_new_business_message_queue(new_message_business_connection_id); } + if (new_business_callback_query_user_id != 0) { + process_new_business_callback_query_queue(new_business_callback_query_user_id); + } } void Client::on_get_sticker_set_name(int64 set_id, const td::string &name) { @@ -6672,6 +6978,21 @@ void Client::check_business_connection(const td::string &business_connection_id, td::make_unique>(this, std::move(query), std::move(on_success))); } +template +void Client::check_business_connection_chat_id(const td::string &business_connection_id, const td::string &chat_id_str, + PromisedQueryPtr query, OnSuccess on_success) { + auto r_chat_id = get_business_connection_chat_id(chat_id_str); + if (r_chat_id.is_error()) { + return fail_query_with_error(std::move(query), 400, r_chat_id.error().message()); + } + auto chat_id = r_chat_id.move_as_ok(); + check_business_connection(business_connection_id, std::move(query), + [this, chat_id, on_success = std::move(on_success)]( + const BusinessConnection *business_connection, PromisedQueryPtr query) mutable { + on_success(business_connection, chat_id, std::move(query)); + }); +} + template void Client::check_bot_command_scope(BotCommandScope &&scope, PromisedQueryPtr query, OnSuccess on_success) { CHECK(scope.scope_ != nullptr); @@ -7424,6 +7745,7 @@ void Client::on_update(object_ptr result) { supergroup_info->location = std::move(full_info->location_); supergroup_info->has_hidden_members = full_info->has_hidden_members_; supergroup_info->has_aggressive_anti_spam_enabled = full_info->has_aggressive_anti_spam_enabled_; + supergroup_info->has_paid_media_allowed = full_info->has_paid_media_allowed_; break; } case td_api::updateOption::ID: { @@ -7497,6 +7819,9 @@ void Client::on_update(object_ptr result) { case td_api::updateNewCallbackQuery::ID: add_new_callback_query(move_object_as(result)); break; + case td_api::updateNewBusinessCallbackQuery::ID: + add_new_business_callback_query(move_object_as(result)); + break; case td_api::updateNewInlineCallbackQuery::ID: add_new_inline_callback_query(move_object_as(result)); break; @@ -7687,8 +8012,12 @@ bool Client::to_bool(td::MutableSlice value) { td_api::object_ptr Client::get_input_message_reply_to( CheckedReplyParameters &&reply_parameters) { if (reply_parameters.reply_to_message_id > 0) { - return make_object( - reply_parameters.reply_in_chat_id, reply_parameters.reply_to_message_id, std::move(reply_parameters.quote)); + if (reply_parameters.reply_in_chat_id != 0) { + return make_object( + reply_parameters.reply_in_chat_id, reply_parameters.reply_to_message_id, std::move(reply_parameters.quote)); + } + return make_object(reply_parameters.reply_to_message_id, + std::move(reply_parameters.quote)); } return nullptr; } @@ -7696,7 +8025,7 @@ td_api::object_ptr Client::get_input_message_reply_ td_api::object_ptr Client::get_input_message_reply_to( InputReplyParameters &&reply_parameters) { if (reply_parameters.reply_in_chat_id.empty() && reply_parameters.reply_to_message_id > 0) { - return make_object(0, reply_parameters.reply_to_message_id, + return make_object(reply_parameters.reply_to_message_id, std::move(reply_parameters.quote)); } return nullptr; @@ -8364,7 +8693,7 @@ td::Result> Client::get_input_me if (!td::check_utf8(payload)) { return td::Status::Error(400, "InputInvoiceMessageContent payload must be encoded in UTF-8"); } - TRY_RESULT(provider_token, object.get_required_string_field("provider_token")); + TRY_RESULT(provider_token, object.get_optional_string_field("provider_token")); TRY_RESULT(currency, object.get_required_string_field("currency")); TRY_RESULT(prices_object, object.extract_required_field("prices", td::JsonValue::Type::Array)); TRY_RESULT(prices, get_labeled_price_parts(prices_object)); @@ -8394,7 +8723,7 @@ td::Result> Client::get_input_me need_email_address, need_shipping_address, send_phone_number_to_provider, send_email_address_to_provider, is_flexible), title, description, photo_url, photo_size, photo_width, photo_height, payload, provider_token, provider_data, - td::string(), nullptr); + td::string(), nullptr, nullptr); } if (is_input_message_content_required) { @@ -8404,9 +8733,9 @@ td::Result> Client::get_input_me } td_api::object_ptr Client::get_message_send_options(bool disable_notification, - bool protect_content, + bool protect_content, int64 effect_id, td_api::object_ptr &&scheduling_state) { - return make_object(disable_notification, false, protect_content, false, std::move(scheduling_state), 0, false); + return make_object(disable_notification, false, protect_content, false, std::move(scheduling_state), effect_id, 0, false); } td::Result> Client::get_inline_query_results_button( @@ -8558,6 +8887,7 @@ td::Result> Client::get_inlin TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); auto entities = object.extract_field("caption_entities"); TRY_RESULT(caption, get_formatted_text(std::move(input_caption), std::move(parse_mode), std::move(entities))); + TRY_RESULT(show_caption_above_media, object.get_optional_bool_field("show_caption_above_media")); TRY_RESULT(reply_markup_object, object.extract_optional_field("reply_markup", td::JsonValue::Type::Object)); object_ptr reply_markup; @@ -8658,8 +8988,9 @@ td::Result> Client::get_inlin } if (input_message_content == nullptr) { - input_message_content = make_object( - nullptr, nullptr, td::vector(), gif_duration, gif_width, gif_height, std::move(caption), false); + input_message_content = + make_object(nullptr, nullptr, td::vector(), gif_duration, gif_width, + gif_height, std::move(caption), show_caption_above_media, false); } return make_object( id, title, thumbnail_url, thumbnail_mime_type, gif_url, "image/gif", gif_duration, gif_width, gif_height, @@ -8699,8 +9030,9 @@ td::Result> Client::get_inlin } if (input_message_content == nullptr) { - input_message_content = make_object( - nullptr, nullptr, td::vector(), mpeg4_duration, mpeg4_width, mpeg4_height, std::move(caption), false); + input_message_content = + make_object(nullptr, nullptr, td::vector(), mpeg4_duration, mpeg4_width, + mpeg4_height, std::move(caption), show_caption_above_media, false); } return make_object( id, title, thumbnail_url, thumbnail_mime_type, mpeg4_url, "video/mp4", mpeg4_duration, mpeg4_width, @@ -8717,8 +9049,8 @@ td::Result> Client::get_inlin } if (input_message_content == nullptr) { - input_message_content = make_object(nullptr, nullptr, td::vector(), 0, 0, - std::move(caption), nullptr, false); + input_message_content = make_object( + nullptr, nullptr, td::vector(), 0, 0, std::move(caption), show_caption_above_media, nullptr, false); } return make_object(id, title, description, thumbnail_url, photo_url, photo_width, photo_height, std::move(reply_markup), @@ -8783,9 +9115,9 @@ td::Result> Client::get_inlin } if (input_message_content == nullptr) { - input_message_content = - make_object(nullptr, nullptr, td::vector(), video_duration, video_width, - video_height, false, std::move(caption), nullptr, false); + input_message_content = make_object( + nullptr, nullptr, td::vector(), video_duration, video_width, video_height, false, std::move(caption), + show_caption_above_media, nullptr, false); } return make_object(id, title, description, thumbnail_url, video_url, mime_type, video_width, video_height, video_duration, @@ -9468,6 +9800,9 @@ td::Result> Client::get_text_entity_t if (type == "blockquote") { return make_object(); } + if (type == "expandable_blockquote") { + return make_object(); + } if (type == "mention" || type == "hashtag" || type == "cashtag" || type == "bot_command" || type == "url" || type == "email" || type == "phone_number" || type == "bank_card_number") { return nullptr; @@ -9748,6 +10083,7 @@ td::Result> Client::get_input_me TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); auto entities = object.extract_field("caption_entities"); TRY_RESULT(caption, get_formatted_text(std::move(input_caption), std::move(parse_mode), std::move(entities))); + TRY_RESULT(show_caption_above_media, object.get_optional_bool_field("show_caption_above_media")); TRY_RESULT(has_spoiler, object.get_optional_bool_field("has_spoiler")); TRY_RESULT(media, object.get_optional_string_field("media")); @@ -9775,7 +10111,7 @@ td::Result> Client::get_input_me TRY_RESULT(type, object.get_required_string_field("type")); if (type == "photo") { return make_object(std::move(input_file), nullptr, td::vector(), 0, 0, - std::move(caption), nullptr, has_spoiler); + std::move(caption), show_caption_above_media, nullptr, has_spoiler); } if (type == "video") { TRY_RESULT(width, object.get_optional_int_field("width")); @@ -9788,12 +10124,12 @@ td::Result> Client::get_input_me return make_object(std::move(input_file), std::move(input_thumbnail), td::vector(), duration, width, height, supports_streaming, - std::move(caption), nullptr, has_spoiler); - } - if (for_album && type == "animation") { - return td::Status::Error(PSLICE() << "type \"" << type << "\" can't be used in sendMediaGroup"); + std::move(caption), show_caption_above_media, nullptr, has_spoiler); } if (type == "animation") { + if (for_album) { + return td::Status::Error(PSLICE() << "type \"" << type << "\" can't be used in sendMediaGroup"); + } TRY_RESULT(width, object.get_optional_int_field("width")); TRY_RESULT(height, object.get_optional_int_field("height")); TRY_RESULT(duration, object.get_optional_int_field("duration")); @@ -9802,7 +10138,7 @@ td::Result> Client::get_input_me duration = td::clamp(duration, 0, MAX_DURATION); return make_object(std::move(input_file), std::move(input_thumbnail), td::vector(), duration, width, height, std::move(caption), - has_spoiler); + show_caption_above_media, has_spoiler); } if (type == "audio") { TRY_RESULT(duration, object.get_optional_int_field("duration")); @@ -9871,6 +10207,98 @@ td::Result>> Client:: return std::move(contents); } +td::Result> Client::get_input_paid_media(const Query *query, + td::JsonValue &&input_media) const { + if (input_media.type() != td::JsonValue::Type::Object) { + return td::Status::Error("expected an Object"); + } + + auto &object = input_media.get_object(); + TRY_RESULT(media, object.get_optional_string_field("media")); + + auto input_file = get_input_file(query, td::Slice(), media, false); + if (input_file == nullptr) { + return td::Status::Error("media not found"); + } + + object_ptr input_thumbnail; + TRY_RESULT(thumbnail, object.get_optional_string_field("thumbnail")); + auto thumbnail_input_file = get_input_file(query, "thumbnail", thumbnail, true); + if (thumbnail_input_file != nullptr) { + input_thumbnail = make_object(std::move(thumbnail_input_file), 0, 0); + } + + TRY_RESULT(width, object.get_optional_int_field("width")); + TRY_RESULT(height, object.get_optional_int_field("height")); + width = td::clamp(width, 0, MAX_LENGTH); + height = td::clamp(height, 0, MAX_LENGTH); + + object_ptr media_type; + TRY_RESULT(type, object.get_required_string_field("type")); + if (type == "photo") { + media_type = make_object(); + } else if (type == "video") { + TRY_RESULT(duration, object.get_optional_int_field("duration")); + TRY_RESULT(supports_streaming, object.get_optional_bool_field("supports_streaming")); + duration = td::clamp(duration, 0, MAX_DURATION); + media_type = make_object(duration, supports_streaming); + } else { + return td::Status::Error(PSLICE() << "type \"" << type << "\" is unsupported"); + } + + return make_object(std::move(media_type), std::move(input_file), std::move(input_thumbnail), + td::vector(), width, height); +} + +td::Result> Client::get_input_paid_media(const Query *query, + td::Slice field_name) const { + TRY_RESULT(media, get_required_string_arg(query, field_name)); + + LOG(INFO) << "Parsing JSON object: " << media; + auto r_value = json_decode(media); + if (r_value.is_error()) { + LOG(INFO) << "Can't parse JSON object: " << r_value.error(); + return td::Status::Error(400, "Can't parse input paid media JSON object"); + } + + auto r_input_paid_media = get_input_paid_media(query, r_value.move_as_ok()); + if (r_input_paid_media.is_error()) { + return td::Status::Error(400, PSLICE() << "Can't parse InputPaidMedia: " << r_input_paid_media.error().message()); + } + return r_input_paid_media.move_as_ok(); +} + +td::Result>> Client::get_paid_media(const Query *query, + td::Slice field_name) const { + TRY_RESULT(media, get_required_string_arg(query, field_name)); + + LOG(INFO) << "Parsing JSON object: " << media; + auto r_value = json_decode(media); + if (r_value.is_error()) { + LOG(INFO) << "Can't parse JSON object: " << r_value.error(); + return td::Status::Error(400, "Can't parse paid media JSON object"); + } + + return get_paid_media(query, r_value.move_as_ok()); +} + +td::Result>> Client::get_paid_media(const Query *query, + td::JsonValue &&value) const { + if (value.type() != td::JsonValue::Type::Array) { + return td::Status::Error(400, "Expected an Array of InputPaidMedia"); + } + + td::vector> paid_media; + for (auto &input_media : value.get_array()) { + auto r_paid_media = get_input_paid_media(query, std::move(input_media)); + if (r_paid_media.is_error()) { + return td::Status::Error(400, PSLICE() << "Can't parse InputPaidMedia: " << r_paid_media.error().message()); + } + paid_media.push_back(r_paid_media.move_as_ok()); + } + return std::move(paid_media); +} + td::Result> Client::get_input_message_invoice( const Query *query) const { TRY_RESULT(title, get_required_string_arg(query, "title")); @@ -9879,7 +10307,7 @@ td::Result> Client::get_input_me if (!td::check_utf8(payload.str())) { return td::Status::Error(400, "The payload must be encoded in UTF-8"); } - TRY_RESULT(provider_token, get_required_string_arg(query, "provider_token")); + auto provider_token = query->arg("provider_token"); auto provider_data = query->arg("provider_data"); auto start_parameter = query->arg("start_parameter"); TRY_RESULT(currency, get_required_string_arg(query, "currency")); @@ -9928,10 +10356,15 @@ td::Result> Client::get_input_me auto send_email_address_to_provider = to_bool(query->arg("send_email_to_provider")); auto is_flexible = to_bool(query->arg("is_flexible")); - object_ptr extended_media; - if (!query->arg("extended_media").empty()) { - TRY_RESULT_ASSIGN(extended_media, get_input_media(query, "extended_media")); + object_ptr paid_media; + if (!query->arg("paid_media").empty()) { + TRY_RESULT_ASSIGN(paid_media, get_input_paid_media(query, "paid_media")); + } else if (!query->arg("extended_media").empty()) { + TRY_RESULT_ASSIGN(paid_media, get_input_paid_media(query, "extended_media")); } + TRY_RESULT(paid_media_caption, get_formatted_text(query->arg("paid_media_caption").str(), + query->arg("paid_media_caption_parse_mode").str(), + get_input_entities(query, "paid_media_caption_entities"))); return make_object( make_object(currency.str(), std::move(prices), max_tip_amount, std::move(suggested_tip_amounts), @@ -9939,7 +10372,8 @@ td::Result> Client::get_input_me need_shipping_address, send_phone_number_to_provider, send_email_address_to_provider, is_flexible), title.str(), description.str(), photo_url.str(), photo_size, photo_width, photo_height, payload.str(), - provider_token.str(), provider_data.str(), start_parameter.str(), std::move(extended_media)); + provider_token.str(), provider_data.str(), start_parameter.str(), std::move(paid_media), + std::move(paid_media_caption)); } td::Result>> Client::get_poll_options(const Query *query) { @@ -10592,11 +11026,12 @@ td::Status Client::process_send_animation_query(PromisedQueryPtr &query) { int32 width = get_integer_arg(query.get(), "width", 0, 0, MAX_LENGTH); int32 height = get_integer_arg(query.get(), "height", 0, 0, MAX_LENGTH); TRY_RESULT(caption, get_caption(query.get())); + auto show_caption_above_media = to_bool(query->arg("show_caption_above_media")); auto has_spoiler = to_bool(query->arg("has_spoiler")); - do_send_message( - make_object(std::move(animation), std::move(thumbnail), td::vector(), - duration, width, height, std::move(caption), has_spoiler), - std::move(query)); + do_send_message(make_object(std::move(animation), std::move(thumbnail), + td::vector(), duration, width, height, + std::move(caption), show_caption_above_media, has_spoiler), + std::move(query)); return td::Status::OK(); } @@ -10642,10 +11077,12 @@ td::Status Client::process_send_photo_query(PromisedQueryPtr &query) { return td::Status::Error(400, "There is no photo in the request"); } TRY_RESULT(caption, get_caption(query.get())); + auto show_caption_above_media = to_bool(query->arg("show_caption_above_media")); auto has_spoiler = to_bool(query->arg("has_spoiler")); - do_send_message(make_object(std::move(photo), nullptr, td::vector(), 0, 0, - std::move(caption), nullptr, has_spoiler), - std::move(query)); + do_send_message( + make_object(std::move(photo), nullptr, td::vector(), 0, 0, std::move(caption), + show_caption_above_media, nullptr, has_spoiler), + std::move(query)); return td::Status::OK(); } @@ -10671,10 +11108,11 @@ td::Status Client::process_send_video_query(PromisedQueryPtr &query) { int32 height = get_integer_arg(query.get(), "height", 0, 0, MAX_LENGTH); bool supports_streaming = to_bool(query->arg("supports_streaming")); TRY_RESULT(caption, get_caption(query.get())); + auto show_caption_above_media = to_bool(query->arg("show_caption_above_media")); auto has_spoiler = to_bool(query->arg("has_spoiler")); - do_send_message(make_object(std::move(video), std::move(thumbnail), td::vector(), - duration, width, height, supports_streaming, - std::move(caption), nullptr, has_spoiler), + do_send_message(make_object( + std::move(video), std::move(thumbnail), td::vector(), duration, width, height, + supports_streaming, std::move(caption), show_caption_above_media, nullptr, has_spoiler), std::move(query)); return td::Status::OK(); } @@ -10706,6 +11144,17 @@ td::Status Client::process_send_voice_query(PromisedQueryPtr &query) { return td::Status::OK(); } +td::Status Client::process_send_paid_media_query(PromisedQueryPtr &query) { + int32 star_count = get_integer_arg(query.get(), "star_count", 0, 0, 1000000000); + TRY_RESULT(paid_media, get_paid_media(query.get(), "media")); + TRY_RESULT(caption, get_caption(query.get())); + auto show_caption_above_media = to_bool(query->arg("show_caption_above_media")); + do_send_message(make_object(star_count, std::move(paid_media), std::move(caption), + show_caption_above_media), + std::move(query)); + return td::Status::OK(); +} + td::Status Client::process_send_game_query(PromisedQueryPtr &query) { CHECK_IS_BOT(); TRY_RESULT(game_short_name, get_required_string_arg(query.get(), "game_short_name")); @@ -10806,6 +11255,7 @@ td::Status Client::process_send_poll_query(PromisedQueryPtr &query) { } td::Status Client::process_stop_poll_query(PromisedQueryPtr &query) { + auto business_connection_id = query->arg("business_connection_id"); auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); @@ -10813,9 +11263,21 @@ td::Status Client::process_stop_poll_query(PromisedQueryPtr &query) { resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_id](object_ptr reply_markup, - PromisedQueryPtr query) { - check_message(chat_id, message_id, false, AccessRights::Edit, "message with poll to stop", std::move(query), + [this, business_connection_id = business_connection_id.str(), chat_id_str = chat_id.str(), message_id]( + object_ptr reply_markup, PromisedQueryPtr query) { + if (!business_connection_id.empty()) { + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, business_connection_id, message_id, reply_markup = std::move(reply_markup)]( + const BusinessConnection *business_connection, int64 chat_id, PromisedQueryPtr query) mutable { + send_request( + make_object(business_connection_id, chat_id, message_id, + std::move(reply_markup)), + td::make_unique(this, business_connection_id, std::move(query))); + }); + } + + check_message(chat_id_str, message_id, false, AccessRights::Edit, "message with poll to stop", std::move(query), [this, reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { send_request( @@ -10834,7 +11296,9 @@ td::Status Client::process_copy_message_query(PromisedQueryPtr &query) { if (replace_caption) { TRY_RESULT_ASSIGN(caption, get_caption(query.get())); } - auto options = make_object(true, replace_caption, std::move(caption)); + auto show_caption_above_media = to_bool(query->arg("show_caption_above_media")); + auto options = + make_object(true, replace_caption, std::move(caption), show_caption_above_media); check_message( from_chat_id, message_id, false, AccessRights::Read, "message to copy", std::move(query), @@ -10881,7 +11345,7 @@ td::Status Client::process_copy_messages_query(PromisedQueryPtr &query) { send_request(make_object( chat_id, message_thread_id, from_chat_id, std::move(message_ids), - get_message_send_options(disable_notification, protect_content, std::move(send_at)), true, remove_caption), + get_message_send_options(disable_notification, protect_content, 0, std::move(send_at)), true, remove_caption), td::make_unique(this, chat_id, message_count, std::move(query))); }); }; @@ -10937,7 +11401,7 @@ td::Status Client::process_forward_messages_query(PromisedQueryPtr &query) { send_request(make_object( chat_id, message_thread_id, from_chat_id, std::move(message_ids), - get_message_send_options(disable_notification, protect_content, std::move(send_at)), false, false), + get_message_send_options(disable_notification, protect_content, 0, std::move(send_at)), false, false), td::make_unique(this, chat_id, message_count, std::move(query))); }); }; @@ -10953,6 +11417,7 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { auto business_connection_id = query->arg("business_connection_id"); auto disable_notification = to_bool(query->arg("disable_notification")); auto protect_content = to_bool(query->arg("protect_content")); + auto effect_id = td::to_integer(query->arg("message_effect_id")); // TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); auto reply_markup = nullptr; TRY_RESULT(input_message_contents, get_input_message_contents(query.get(), "media")); @@ -10962,30 +11427,25 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { std::move(reply_markup), std::move(query), [this, chat_id_str = chat_id.str(), message_thread_id, business_connection_id = business_connection_id.str(), send_at = std::move(send_at), - reply_parameters = std::move(reply_parameters), disable_notification, protect_content, + reply_parameters = std::move(reply_parameters), disable_notification, protect_content, effect_id, input_message_contents = std::move(input_message_contents)](object_ptr reply_markup, PromisedQueryPtr query) mutable { if (!business_connection_id.empty()) { - auto r_chat_id = get_business_connection_chat_id(chat_id_str); - if (r_chat_id.is_error()) { - return fail_query_with_error(std::move(query), 400, r_chat_id.error().message()); - } - auto chat_id = r_chat_id.move_as_ok(); - return check_business_connection( - business_connection_id, std::move(query), - [this, chat_id, reply_parameters = std::move(reply_parameters), disable_notification, protect_content, + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, reply_parameters = std::move(reply_parameters), disable_notification, protect_content, effect_id, input_message_contents = std::move(input_message_contents), reply_markup = std::move(reply_markup)]( - const BusinessConnection *business_connection, PromisedQueryPtr query) mutable { + const BusinessConnection *business_connection, int64 chat_id, PromisedQueryPtr query) mutable { send_request( make_object( business_connection->id_, chat_id, get_input_message_reply_to(std::move(reply_parameters)), - disable_notification, protect_content, std::move(input_message_contents)), + disable_notification, protect_content, effect_id, std::move(input_message_contents)), td::make_unique(this, business_connection->id_, std::move(query))); }); } - auto on_success = [this, disable_notification, protect_content, + auto on_success = [this, disable_notification, protect_content, effect_id, input_message_contents = std::move(input_message_contents), send_at = std::move(send_at), reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_thread_id, @@ -10998,11 +11458,11 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { auto message_count = input_message_contents.size(); count += static_cast(message_count); - send_request( - make_object( - chat_id, message_thread_id, get_input_message_reply_to(std::move(reply_parameters)), - get_message_send_options(disable_notification, protect_content, std::move(send_at)), std::move(input_message_contents)), - td::make_unique(this, chat_id, message_count, std::move(query))); + send_request(make_object( + chat_id, message_thread_id, get_input_message_reply_to(std::move(reply_parameters)), + get_message_send_options(disable_notification, protect_content, effect_id, std::move(send_at)), + std::move(input_message_contents)), + td::make_unique(this, chat_id, message_count, std::move(query))); }; check_reply_parameters(chat_id_str, std::move(reply_parameters), message_thread_id, std::move(query), std::move(on_success)); @@ -11019,11 +11479,10 @@ td::Status Client::process_send_chat_action_query(PromisedQueryPtr &query) { return td::Status::Error(400, "Wrong parameter action in request"); } if (!business_connection_id.empty()) { - TRY_RESULT(chat_id, get_business_connection_chat_id(chat_id_str)); - check_business_connection( - business_connection_id, std::move(query), - [this, chat_id, action = std::move(action)](const BusinessConnection *business_connection, - PromisedQueryPtr query) mutable { + check_business_connection_chat_id( + business_connection_id, chat_id_str.str(), std::move(query), + [this, action = std::move(action)](const BusinessConnection *business_connection, int64 chat_id, + PromisedQueryPtr query) mutable { send_request(make_object(chat_id, 0, business_connection->id_, std::move(action)), td::make_unique(std::move(query))); }); @@ -11057,6 +11516,7 @@ td::Status Client::process_set_message_reaction_query(PromisedQueryPtr &query) { td::Status Client::process_edit_message_text_query(PromisedQueryPtr &query) { TRY_RESULT(input_message_text, get_input_message_text(query.get())); + auto business_connection_id = query->arg("business_connection_id"); auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); @@ -11075,10 +11535,25 @@ td::Status Client::process_edit_message_text_query(PromisedQueryPtr &query) { } else { resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_id, input_message_text = std::move(input_message_text)]( - object_ptr reply_markup, PromisedQueryPtr query) mutable { + [this, business_connection_id = business_connection_id.str(), chat_id_str = chat_id.str(), message_id, + input_message_text = std::move(input_message_text)](object_ptr reply_markup, + PromisedQueryPtr query) mutable { + if (!business_connection_id.empty()) { + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, business_connection_id, message_id, input_message_text = std::move(input_message_text), + reply_markup = std::move(reply_markup)](const BusinessConnection *business_connection, int64 chat_id, + PromisedQueryPtr query) mutable { + send_request(make_object(business_connection_id, chat_id, message_id, + std::move(reply_markup), + std::move(input_message_text)), + td::make_unique(this, business_connection_id, + std::move(query))); + }); + } + check_message( - chat_id, message_id, false, AccessRights::Edit, "message to edit", std::move(query), + chat_id_str, message_id, false, AccessRights::Edit, "message to edit", std::move(query), [this, input_message_text = std::move(input_message_text), reply_markup = std::move(reply_markup)]( int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, message_id, std::move(reply_markup), @@ -11098,6 +11573,7 @@ td::Status Client::process_edit_message_live_location_query(PromisedQueryPtr &qu if (query->method() == "editmessagelivelocation") { TRY_RESULT_ASSIGN(location, get_location(query.get())); } + auto business_connection_id = query->arg("business_connection_id"); auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); @@ -11117,9 +11593,24 @@ td::Status Client::process_edit_message_live_location_query(PromisedQueryPtr &qu } else { resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_id, location = std::move(location), live_period, heading, + [this, business_connection_id = business_connection_id.str(), chat_id_str = chat_id.str(), message_id, + location = std::move(location), live_period, heading, proximity_alert_radius](object_ptr reply_markup, PromisedQueryPtr query) mutable { - check_message(chat_id, message_id, false, AccessRights::Edit, "message to edit", std::move(query), + if (!business_connection_id.empty()) { + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, business_connection_id, message_id, location = std::move(location), live_period, heading, + proximity_alert_radius, reply_markup = std::move(reply_markup)]( + const BusinessConnection *business_connection, int64 chat_id, PromisedQueryPtr query) mutable { + send_request(make_object( + business_connection_id, chat_id, message_id, std::move(reply_markup), + std::move(location), live_period, heading, proximity_alert_radius), + td::make_unique(this, business_connection_id, + std::move(query))); + }); + } + + check_message(chat_id_str, message_id, false, AccessRights::Edit, "message to edit", std::move(query), [this, location = std::move(location), live_period, heading, proximity_alert_radius, reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { @@ -11134,6 +11625,7 @@ td::Status Client::process_edit_message_live_location_query(PromisedQueryPtr &qu } td::Status Client::process_edit_message_media_query(PromisedQueryPtr &query) { + auto business_connection_id = query->arg("business_connection_id"); auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); @@ -11153,10 +11645,25 @@ td::Status Client::process_edit_message_media_query(PromisedQueryPtr &query) { } else { resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_id, input_message_content = std::move(input_media)]( - object_ptr reply_markup, PromisedQueryPtr query) mutable { + [this, business_connection_id = business_connection_id.str(), chat_id_str = chat_id.str(), message_id, + input_message_content = std::move(input_media)](object_ptr reply_markup, + PromisedQueryPtr query) mutable { + if (!business_connection_id.empty()) { + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, business_connection_id, message_id, input_message_content = std::move(input_message_content), + reply_markup = std::move(reply_markup)](const BusinessConnection *business_connection, int64 chat_id, + PromisedQueryPtr query) mutable { + send_request(make_object(business_connection_id, chat_id, + message_id, std::move(reply_markup), + std::move(input_message_content)), + td::make_unique(this, business_connection_id, + std::move(query))); + }); + } + check_message( - chat_id, message_id, false, AccessRights::Edit, "message to edit", std::move(query), + chat_id_str, message_id, false, AccessRights::Edit, "message to edit", std::move(query), [this, reply_markup = std::move(reply_markup), input_message_content = std::move(input_message_content)]( int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, message_id, std::move(reply_markup), @@ -11169,33 +11676,51 @@ td::Status Client::process_edit_message_media_query(PromisedQueryPtr &query) { } td::Status Client::process_edit_message_caption_query(PromisedQueryPtr &query) { + auto business_connection_id = query->arg("business_connection_id"); auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); CHECK_USER_REPLY_MARKUP(); TRY_RESULT(caption, get_caption(query.get())); + auto show_caption_above_media = to_bool(query->arg("show_caption_above_media")); if (chat_id.empty() && message_id == 0) { TRY_RESULT(inline_message_id, get_inline_message_id(query.get())); resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, inline_message_id = inline_message_id.str(), caption = std::move(caption)]( + [this, inline_message_id = inline_message_id.str(), caption = std::move(caption), show_caption_above_media]( object_ptr reply_markup, PromisedQueryPtr query) mutable { send_request(make_object(inline_message_id, std::move(reply_markup), - std::move(caption)), + std::move(caption), show_caption_above_media), td::make_unique(std::move(query))); }); } else { resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_id, caption = std::move(caption)]( - object_ptr reply_markup, PromisedQueryPtr query) mutable { - check_message(chat_id, message_id, false, AccessRights::Edit, "message to edit", std::move(query), - [this, reply_markup = std::move(reply_markup), caption = std::move(caption)]( - int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { - send_request(make_object( - chat_id, message_id, std::move(reply_markup), std::move(caption)), - td::make_unique(this, std::move(query))); + [this, business_connection_id = business_connection_id.str(), chat_id_str = chat_id.str(), message_id, + caption = std::move(caption), + show_caption_above_media](object_ptr reply_markup, PromisedQueryPtr query) mutable { + if (!business_connection_id.empty()) { + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, business_connection_id, message_id, reply_markup = std::move(reply_markup), + caption = std::move(caption), show_caption_above_media]( + const BusinessConnection *business_connection, int64 chat_id, PromisedQueryPtr query) mutable { + send_request(make_object( + business_connection_id, chat_id, message_id, std::move(reply_markup), + std::move(caption), show_caption_above_media), + td::make_unique(this, business_connection_id, + std::move(query))); + }); + } + + check_message(chat_id_str, message_id, false, AccessRights::Edit, "message to edit", std::move(query), + [this, reply_markup = std::move(reply_markup), caption = std::move(caption), + show_caption_above_media](int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { + send_request( + make_object(chat_id, message_id, std::move(reply_markup), + std::move(caption), show_caption_above_media), + td::make_unique(this, std::move(query))); }); }); } @@ -11204,6 +11729,7 @@ td::Status Client::process_edit_message_caption_query(PromisedQueryPtr &query) { td::Status Client::process_edit_message_reply_markup_query(PromisedQueryPtr &query) { CHECK_IS_BOT(); + auto business_connection_id = query->arg("business_connection_id"); auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); @@ -11221,9 +11747,21 @@ td::Status Client::process_edit_message_reply_markup_query(PromisedQueryPtr &que } else { resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_id](object_ptr reply_markup, - PromisedQueryPtr query) { - check_message(chat_id, message_id, false, AccessRights::Edit, "message to edit", std::move(query), + [this, business_connection_id = business_connection_id.str(), chat_id_str = chat_id.str(), message_id]( + object_ptr reply_markup, PromisedQueryPtr query) { + if (!business_connection_id.empty()) { + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, business_connection_id, message_id, reply_markup = std::move(reply_markup)]( + const BusinessConnection *business_connection, int64 chat_id, PromisedQueryPtr query) mutable { + send_request(make_object(business_connection_id, chat_id, + message_id, std::move(reply_markup)), + td::make_unique(this, business_connection_id, + std::move(query))); + }); + } + + check_message(chat_id_str, message_id, false, AccessRights::Edit, "message to edit", std::move(query), [this, reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { send_request( @@ -11277,6 +11815,27 @@ td::Status Client::process_create_invoice_link_query(PromisedQueryPtr &query) { return td::Status::OK(); } +td::Status Client::process_get_star_transactions_query(PromisedQueryPtr &query) { + auto offset = get_integer_arg(query.get(), "offset", 0, 0); + auto limit = get_integer_arg(query.get(), "limit", 100, 1, 100); + send_request(make_object(make_object(my_id_), nullptr, + td::to_string(offset), limit), + td::make_unique(this, std::move(query))); + return td::Status::OK(); +} + +td::Status Client::process_refund_star_payment_query(PromisedQueryPtr &query) { + TRY_RESULT(user_id, get_user_id(query.get())); + TRY_RESULT(telegram_payment_charge_id, get_required_string_arg(query.get(), "telegram_payment_charge_id")); + check_user_no_fail( + user_id, std::move(query), + [this, telegram_payment_charge_id = telegram_payment_charge_id.str(), user_id](PromisedQueryPtr query) { + send_request(make_object(user_id, telegram_payment_charge_id), + td::make_unique(std::move(query))); + }); + return td::Status::OK(); +} + td::Status Client::process_set_game_score_query(PromisedQueryPtr &query) { CHECK_IS_BOT(); auto chat_id = query->arg("chat_id"); @@ -13299,6 +13858,7 @@ void Client::do_send_message(object_ptr input_messa auto reply_parameters = r_reply_parameters.move_as_ok(); auto disable_notification = to_bool(query->arg("disable_notification")); auto protect_content = to_bool(query->arg("protect_content")); + auto effect_id = td::to_integer(query->arg("message_effect_id")); auto r_reply_markup = get_reply_markup(query.get(), bot_user_ids_); if (r_reply_markup.is_error()) { return fail_query_with_error(std::move(query), 400, r_reply_markup.error().message()); @@ -13318,30 +13878,25 @@ void Client::do_send_message(object_ptr input_messa std::move(reply_markup), std::move(query), [this, chat_id_str = chat_id.str(), message_thread_id, business_connection_id = business_connection_id.str(), send_at = std::move(send_at), - reply_parameters = std::move(reply_parameters), disable_notification, protect_content, + reply_parameters = std::move(reply_parameters), disable_notification, protect_content, effect_id, input_message_content = std::move(input_message_content)](object_ptr reply_markup, PromisedQueryPtr query) mutable { if (!business_connection_id.empty()) { - auto r_chat_id = get_business_connection_chat_id(chat_id_str); - if (r_chat_id.is_error()) { - return fail_query_with_error(std::move(query), 400, r_chat_id.error().message()); - } - auto chat_id = r_chat_id.move_as_ok(); - return check_business_connection( - business_connection_id, std::move(query), - [this, chat_id, reply_parameters = std::move(reply_parameters), disable_notification, protect_content, + return check_business_connection_chat_id( + business_connection_id, chat_id_str, std::move(query), + [this, reply_parameters = std::move(reply_parameters), disable_notification, protect_content, effect_id, reply_markup = std::move(reply_markup), input_message_content = std::move(input_message_content)]( - const BusinessConnection *business_connection, PromisedQueryPtr query) mutable { - send_request( - make_object(business_connection->id_, chat_id, - get_input_message_reply_to(std::move(reply_parameters)), - disable_notification, protect_content, - std::move(reply_markup), std::move(input_message_content)), - td::make_unique(this, business_connection->id_, std::move(query))); + const BusinessConnection *business_connection, int64 chat_id, PromisedQueryPtr query) mutable { + send_request(make_object( + business_connection->id_, chat_id, + get_input_message_reply_to(std::move(reply_parameters)), disable_notification, + protect_content, effect_id, std::move(reply_markup), std::move(input_message_content)), + td::make_unique(this, business_connection->id_, + std::move(query))); }); } - auto on_success = [this, disable_notification, protect_content, + auto on_success = [this, disable_notification, protect_content, effect_id, input_message_content = std::move(input_message_content), send_at = std::move(send_at), reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_thread_id, @@ -13353,10 +13908,10 @@ void Client::do_send_message(object_ptr input_messa } count++; - send_request(make_object(chat_id, message_thread_id, - get_input_message_reply_to(std::move(reply_parameters)), - get_message_send_options(disable_notification, protect_content, std::move(send_at)), - std::move(reply_markup), std::move(input_message_content)), + send_request(make_object( + chat_id, message_thread_id, get_input_message_reply_to(std::move(reply_parameters)), + get_message_send_options(disable_notification, protect_content, effect_id, std::move(send_at)), + std::move(reply_markup), std::move(input_message_content)), td::make_unique(this, chat_id, std::move(query))); }; check_reply_parameters(chat_id_str, std::move(reply_parameters), message_thread_id, std::move(query), @@ -13529,7 +14084,7 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu CHECK(total_size >= updates.size()); total_size -= updates.size(); - bool need_warning = total_size > 0 && (query->start_timestamp() - previous_get_updates_finish_time_ > 10.0); + bool need_warning = total_size > 0 && (query->start_timestamp() - previous_get_updates_finish_time_ > 5.0); if (total_size <= MIN_PENDING_UPDATES_WARNING / 2) { if (last_pending_update_count_ > MIN_PENDING_UPDATES_WARNING) { need_warning = true; @@ -13541,7 +14096,8 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu last_pending_update_count_ *= 2; } } - if (need_warning && previous_get_updates_finish_time_ > 0) { + if (need_warning && previous_get_updates_finish_time_ > 0 && + query->start_timestamp() > previous_get_updates_finish_time_) { LOG(WARNING) << "Found " << updates.size() << " updates out of " << (total_size + updates.size()) << " after last getUpdates call " << (query->start_timestamp() - previous_get_updates_finish_time_) << " seconds ago in " << (td::Time::now() - query->start_timestamp()) << " seconds from " @@ -13937,7 +14493,7 @@ void Client::json_store_permissions(td::JsonObjectScope &object, const td_api::c object("can_send_voice_notes", td::JsonBool(permissions->can_send_voice_notes_)); object("can_send_polls", td::JsonBool(permissions->can_send_polls_)); object("can_send_other_messages", td::JsonBool(permissions->can_send_other_messages_)); - object("can_add_web_page_previews", td::JsonBool(permissions->can_add_web_page_previews_)); + object("can_add_web_page_previews", td::JsonBool(permissions->can_add_link_previews_)); object("can_change_info", td::JsonBool(permissions->can_change_info_)); object("can_invite_users", td::JsonBool(permissions->can_invite_users_)); object("can_pin_messages", td::JsonBool(permissions->can_pin_messages_)); @@ -14235,7 +14791,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { queue.has_active_request_ = true; return send_request( make_object(message_sticker_set_id), - td::make_unique(this, message_sticker_set_id, user_id, 0, td::string())); + td::make_unique(this, message_sticker_set_id, user_id, 0, td::string(), 0)); } auto reply_to_message_id = get_same_chat_reply_to_message_id(message_info); if (reply_to_message_id > 0) { @@ -14246,7 +14802,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { queue.has_active_request_ = true; return send_request( make_object(reply_sticker_set_id), - td::make_unique(this, reply_sticker_set_id, user_id, 0, td::string())); + td::make_unique(this, reply_sticker_set_id, user_id, 0, td::string(), 0)); } } } @@ -14264,6 +14820,63 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { new_callback_query_queues_.erase(user_id); } +void Client::add_new_business_callback_query(object_ptr &&query) { + CHECK(query != nullptr); + auto user_id = query->sender_user_id_; + CHECK(user_id != 0); + new_business_callback_query_queues_[user_id].queue_.push(std::move(query)); + process_new_business_callback_query_queue(user_id); +} + +void Client::process_new_business_callback_query_queue(int64 user_id) { + auto &queue = new_business_callback_query_queues_[user_id]; + if (queue.has_active_request_) { + CHECK(!queue.queue_.empty()); + LOG(INFO) << "Have an active request in business callback query queue of size " << queue.queue_.size() + << " for user " << user_id; + return; + } + if (logging_out_ || closing_) { + LOG(INFO) << "Ignore business callback query while closing for user " << user_id; + new_business_callback_query_queues_.erase(user_id); + return; + } + while (!queue.queue_.empty()) { + auto &query = queue.queue_.front(); + auto &message_ref = query->message_; + LOG(INFO) << "Process business callback query from user " << user_id; + + drop_internal_reply_to_message_in_another_chat(message_ref->message_); + + auto message_sticker_set_id = get_sticker_set_id(message_ref->message_->content_); + if (!have_sticker_set_name(message_sticker_set_id)) { + queue.has_active_request_ = true; + return send_request( + make_object(message_sticker_set_id), + td::make_unique(this, message_sticker_set_id, 0, 0, td::string(), user_id)); + } + if (message_ref->reply_to_message_ != nullptr) { + drop_internal_reply_to_message_in_another_chat(message_ref->reply_to_message_); + auto reply_sticker_set_id = get_sticker_set_id(message_ref->reply_to_message_->content_); + if (!have_sticker_set_name(reply_sticker_set_id)) { + queue.has_active_request_ = true; + return send_request( + make_object(reply_sticker_set_id), + td::make_unique(this, reply_sticker_set_id, 0, 0, td::string(), user_id)); + } + } + + CHECK(user_id == query->sender_user_id_); + auto message_info = create_business_message(query->connection_id_, std::move(message_ref)); + add_update(UpdateType::CallbackQuery, + JsonCallbackQuery(query->id_, user_id, 0, 0, message_info.get(), query->chat_instance_, + query->payload_.get(), this), + 150, user_id + (static_cast(3) << 33)); + queue.queue_.pop(); + } + new_callback_query_queues_.erase(user_id); +} + void Client::add_new_inline_callback_query(object_ptr &&query) { CHECK(query != nullptr); add_update(UpdateType::CallbackQuery, @@ -14306,7 +14919,7 @@ void Client::add_update_chat_member(object_ptr &&updat } auto user_id = static_cast(update->old_chat_member_->member_id_.get())->user_id_; bool is_my = (user_id == my_id_); - auto webhook_queue_id = update->chat_id_ + (static_cast(is_my ? 5 : 6) << 33); + auto webhook_queue_id = (is_my ? update->chat_id_ : user_id) + (static_cast(is_my ? 5 : 6) << 33); auto update_type = is_my ? UpdateType::MyChatMember : UpdateType::ChatMember; add_update(update_type, JsonChatMemberUpdated(update.get(), this), left_time, webhook_queue_id); } else { @@ -14320,7 +14933,7 @@ void Client::add_update_chat_join_request(object_ptrrequest_ != nullptr); auto left_time = update->request_->date_ + 86400 - get_unix_time(); if (left_time > 0) { - auto webhook_queue_id = update->chat_id_ + (static_cast(6) << 33); + auto webhook_queue_id = update->request_->user_id_ + (static_cast(6) << 33); add_update(UpdateType::ChatJoinRequest, JsonChatJoinRequest(update.get(), this), left_time, webhook_queue_id); } else { LOG(DEBUG) << "Skip updateNewChatJoinRequest with date " << update->request_->date_ << ", because current date is " @@ -14443,6 +15056,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr(message_sticker_set_id), - td::make_unique(this, message_sticker_set_id, 0, chat_id, td::string())); + td::make_unique(this, message_sticker_set_id, 0, chat_id, td::string(), 0)); } if (reply_to_message_id > 0) { auto reply_to_message_info = get_message(chat_id, reply_to_message_id, true); @@ -14830,7 +15444,7 @@ void Client::process_new_message_queue(int64 chat_id, int state) { queue.has_active_request_ = true; return send_request( make_object(reply_sticker_set_id), - td::make_unique(this, reply_sticker_set_id, 0, chat_id, td::string())); + td::make_unique(this, reply_sticker_set_id, 0, chat_id, td::string(), 0)); } } } @@ -14920,7 +15534,7 @@ void Client::process_new_business_message_queue(const td::string &connection_id) queue.has_active_request_ = true; return send_request( make_object(message_sticker_set_id), - td::make_unique(this, message_sticker_set_id, 0, 0, connection_id)); + td::make_unique(this, message_sticker_set_id, 0, 0, connection_id, 0)); } if (message_ref->reply_to_message_ != nullptr) { drop_internal_reply_to_message_in_another_chat(message_ref->reply_to_message_); @@ -14929,7 +15543,7 @@ void Client::process_new_business_message_queue(const td::string &connection_id) queue.has_active_request_ = true; return send_request( make_object(reply_sticker_set_id), - td::make_unique(this, reply_sticker_set_id, 0, 0, connection_id)); + td::make_unique(this, reply_sticker_set_id, 0, 0, connection_id, 0)); } } @@ -15072,6 +15686,7 @@ void Client::init_message(MessageInfo *message_info, object_ptr message_info->is_topic_message = message->is_topic_message_; message_info->author_signature = std::move(message->author_signature_); message_info->sender_boost_count = message->sender_boost_count_; + message_info->effect_id = message->effect_id_; drop_internal_reply_to_message_in_another_chat(message); @@ -15093,7 +15708,7 @@ void Client::init_message(MessageInfo *message_info, object_ptr auto sticker_set_id = get_sticker_set_id(message_info->content); if (!have_sticker_set_name(sticker_set_id)) { send_request(make_object(sticker_set_id), - td::make_unique(this, sticker_set_id, 0, 0, td::string())); + td::make_unique(this, sticker_set_id, 0, 0, td::string(), 0)); } } else if (message->content_->get_id() == td_api::messagePoll::ID) { message_info->content = std::move(message->content_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 6a531cd..8c41682 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) @@ -134,6 +134,8 @@ class Client final : public WebhookActor::Callback { class JsonVideo; class JsonVideoNote; class JsonVoiceNote; + class JsonPaidMedia; + class JsonPaidMediaInfo; class JsonContact; class JsonDice; class JsonGame; @@ -190,6 +192,7 @@ class Client final : public WebhookActor::Callback { class JsonBackgroundType; class JsonChatBackground; class JsonSuccessfulPaymentBot; + class JsonRefundedPayment; class JsonEncryptedPassportElement; class JsonEncryptedCredentials; class JsonPassportData; @@ -208,6 +211,10 @@ class Client final : public WebhookActor::Callback { class JsonGiveawayWinners; class JsonGiveawayCompleted; class JsonChatBoostAdded; + class JsonRevenueWithdrawalState; + class JsonStarTransactionPartner; + class JsonStarTransaction; + class JsonStarTransactions; class JsonUpdateTypes; class JsonWebhookInfo; class JsonStickerSet; @@ -231,7 +238,7 @@ class Client final : public WebhookActor::Callback { class TdOnInitCallback; class TdOnGetUserProfilePhotosCallback; class TdOnSendMessageCallback; - class TdOnSendBusinessMessageCallback; + class TdOnReturnBusinessMessageCallback; class TdOnSendMessageAlbumCallback; class TdOnSendBusinessMessageAlbumCallback; class TdOnForwardMessagesCallback; @@ -239,6 +246,7 @@ class Client final : public WebhookActor::Callback { class TdOnEditMessageCallback; class TdOnEditInlineMessageCallback; class TdOnStopPollCallback; + class TdOnStopBusinessPollCallback; class TdOnOkQueryCallback; class TdOnGetReplyMessageCallback; class TdOnGetEditedMessageCallback; @@ -262,6 +270,7 @@ class Client final : public WebhookActor::Callback { class TdOnGetSupergroupMemberCountCallback; class TdOnGetUserChatBoostsCallback; class TdOnCreateInvoiceLinkCallback; + class TdOnGetStarTransactionsQueryCallback; class TdOnReplacePrimaryChatInviteLinkCallback; class TdOnGetChatInviteLinkCallback; class TdOnGetGameHighScoresCallback; @@ -296,7 +305,7 @@ class Client final : public WebhookActor::Callback { void on_get_sticker_set(int64 set_id, int64 new_callback_query_user_id, int64 new_message_chat_id, const td::string &new_message_business_connection_id, - object_ptr sticker_set); + int64 new_business_callback_query_user_id, object_ptr sticker_set); void on_get_sticker_set_name(int64 set_id, const td::string &name); @@ -388,6 +397,10 @@ class Client final : public WebhookActor::Callback { void check_business_connection(const td::string &business_connection_id, PromisedQueryPtr query, OnSuccess on_success); + template + void check_business_connection_chat_id(const td::string &business_connection_id, const td::string &chat_id_str, + PromisedQueryPtr query, OnSuccess on_success); + template void check_bot_command_scope(BotCommandScope &&scope, PromisedQueryPtr query, OnSuccess on_success); @@ -603,10 +616,21 @@ class Client final : public WebhookActor::Callback { td::Result>> get_input_message_contents( const Query *query, td::JsonValue &&value) const; + td::Result> get_input_paid_media(const Query *query, + td::JsonValue &&input_media) const; + + td::Result> get_input_paid_media(const Query *query, td::Slice field_name) const; + + td::Result>> get_paid_media(const Query *query, + td::Slice field_name) const; + + td::Result>> get_paid_media(const Query *query, + td::JsonValue &&value) const; + td::Result> get_input_message_invoice(const Query *query) const; static object_ptr get_message_send_options(bool disable_notification, - bool protect_content, + bool protect_content, int64 effect_id, object_ptr &&scheduling_state); static td::Result>> get_poll_options(const Query *query); @@ -689,6 +713,7 @@ class Client final : public WebhookActor::Callback { td::Status process_send_video_query(PromisedQueryPtr &query); td::Status process_send_video_note_query(PromisedQueryPtr &query); td::Status process_send_voice_query(PromisedQueryPtr &query); + td::Status process_send_paid_media_query(PromisedQueryPtr &query); td::Status process_send_game_query(PromisedQueryPtr &query); td::Status process_send_invoice_query(PromisedQueryPtr &query); td::Status process_send_location_query(PromisedQueryPtr &query); @@ -711,6 +736,8 @@ class Client final : public WebhookActor::Callback { td::Status process_delete_message_query(PromisedQueryPtr &query); td::Status process_delete_messages_query(PromisedQueryPtr &query); td::Status process_create_invoice_link_query(PromisedQueryPtr &query); + td::Status process_get_star_transactions_query(PromisedQueryPtr &query); + td::Status process_refund_star_payment_query(PromisedQueryPtr &query); td::Status process_set_game_score_query(PromisedQueryPtr &query); td::Status process_get_game_high_scores_query(PromisedQueryPtr &query); td::Status process_answer_web_app_query_query(PromisedQueryPtr &query); @@ -971,6 +998,7 @@ class Client final : public WebhookActor::Callback { bool join_by_request = false; bool has_hidden_members = false; bool has_aggressive_anti_spam_enabled = false; + bool has_paid_media_allowed = false; // start custom properties bool is_verified = false; @@ -1036,6 +1064,7 @@ class Client final : public WebhookActor::Callback { object_ptr reply_markup; td::string business_connection_id; int64 sender_business_bot_user_id = 0; + int64 effect_id = 0; // start custom properties int32 views = 0; @@ -1190,6 +1219,10 @@ class Client final : public WebhookActor::Callback { void process_new_callback_query_queue(int64 user_id, int state); + void add_new_business_callback_query(object_ptr &&query); + + void process_new_business_callback_query_queue(int64 user_id); + void add_new_inline_callback_query(object_ptr &&query); void add_new_shipping_query(object_ptr &&query); @@ -1369,6 +1402,12 @@ class Client final : public WebhookActor::Callback { }; td::FlatHashMap new_callback_query_queues_; // sender_user_id -> queue + struct NewBusinessCallbackQueryQueue { + std::queue> queue_; + bool has_active_request_ = false; + }; + td::FlatHashMap new_business_callback_query_queues_; // sender_user_id -> queue + td::WaitFreeHashMap sticker_set_names_; td::WaitFreeHashMap last_send_message_time_; diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 53c891a..4cba52c 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021, Luckydonald (tdlight-telegram-bot-api+code@luckydonald.de) 2020 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024, Luckydonald (tdlight-telegram-bot-api+code@luckydonald.de) 2020 // // 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) diff --git a/telegram-bot-api/ClientManager.h b/telegram-bot-api/ClientManager.h index ff96f72..e2d7059 100644 --- a/telegram-bot-api/ClientManager.h +++ b/telegram-bot-api/ClientManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/ClientParameters.h b/telegram-bot-api/ClientParameters.h index e41ad50..4773907 100644 --- a/telegram-bot-api/ClientParameters.h +++ b/telegram-bot-api/ClientParameters.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/HttpConnection.cpp b/telegram-bot-api/HttpConnection.cpp index fd8166a..ef5e009 100644 --- a/telegram-bot-api/HttpConnection.cpp +++ b/telegram-bot-api/HttpConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/HttpConnection.h b/telegram-bot-api/HttpConnection.h index 0e3b39c..b66b062 100644 --- a/telegram-bot-api/HttpConnection.h +++ b/telegram-bot-api/HttpConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/HttpServer.h b/telegram-bot-api/HttpServer.h index c2398ee..21c26a8 100644 --- a/telegram-bot-api/HttpServer.h +++ b/telegram-bot-api/HttpServer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/HttpStatConnection.cpp b/telegram-bot-api/HttpStatConnection.cpp index e6e678e..df02bed 100644 --- a/telegram-bot-api/HttpStatConnection.cpp +++ b/telegram-bot-api/HttpStatConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/HttpStatConnection.h b/telegram-bot-api/HttpStatConnection.h index a249aea..2aa89c3 100644 --- a/telegram-bot-api/HttpStatConnection.h +++ b/telegram-bot-api/HttpStatConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/Query.cpp b/telegram-bot-api/Query.cpp index d2b6a60..abfc728 100644 --- a/telegram-bot-api/Query.cpp +++ b/telegram-bot-api/Query.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/Query.h b/telegram-bot-api/Query.h index 9096e93..d93bdea 100644 --- a/telegram-bot-api/Query.h +++ b/telegram-bot-api/Query.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/Stats.cpp b/telegram-bot-api/Stats.cpp index 31d2300..9a7d7cd 100644 --- a/telegram-bot-api/Stats.cpp +++ b/telegram-bot-api/Stats.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/Stats.h b/telegram-bot-api/Stats.h index 1b979d7..14bdb8c 100644 --- a/telegram-bot-api/Stats.h +++ b/telegram-bot-api/Stats.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/Watchdog.cpp b/telegram-bot-api/Watchdog.cpp index 9b5c88d..98c7ff2 100644 --- a/telegram-bot-api/Watchdog.cpp +++ b/telegram-bot-api/Watchdog.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/Watchdog.h b/telegram-bot-api/Watchdog.h index 89ac3c6..c34eb2d 100644 --- a/telegram-bot-api/Watchdog.h +++ b/telegram-bot-api/Watchdog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index e86845e..500f094 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/WebhookActor.h b/telegram-bot-api/WebhookActor.h index 4c101a3..610a30b 100644 --- a/telegram-bot-api/WebhookActor.h +++ b/telegram-bot-api/WebhookActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index b82f33e..52684f6 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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) @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared(); auto parameters = std::make_unique(); - parameters->version_ = "7.3"; + parameters->version_ = "7.7"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats();