diff --git a/CMakeLists.txt b/CMakeLists.txt index b73a401..76ef36a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 6.0.1 LANGUAGES CXX) +project(TelegramBotApi VERSION 6.1 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/td b/td index 782670c..b393215 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 782670c7dbf278e0ba07fc7e168f39ac154c7238 +Subproject commit b393215d6671863b6baf2a589d343cff9474f6ba diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 18ad2e7..85204f6 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -245,6 +245,7 @@ bool Client::init_methods() { methods_.emplace("editmessagecaption", &Client::process_edit_message_caption_query); methods_.emplace("editmessagereplymarkup", &Client::process_edit_message_reply_markup_query); methods_.emplace("deletemessage", &Client::process_delete_message_query); + methods_.emplace("createinvoicelink", &Client::process_create_invoice_link_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); @@ -336,16 +337,18 @@ bool Client::init_methods() { class Client::JsonFile final : public Jsonable { public: - JsonFile(const td_api::file *file, const Client *client) : file_(file), client_(client) { + JsonFile(const td_api::file *file, const Client *client, bool with_path) + : file_(file), client_(client), with_path_(with_path) { } void store(JsonValueScope *scope) const { auto object = scope->enter_object(); - client_->json_store_file(object, file_, true); + client_->json_store_file(object, file_, with_path_); } private: const td_api::file *file_; const Client *client_; + bool with_path_; }; class Client::JsonDatedFile final : public Jsonable { @@ -403,6 +406,12 @@ class Client::JsonUser final : public Jsonable { if (user_info != nullptr && !user_info->language_code.empty()) { object("language_code", user_info->language_code); } + if (user_info != nullptr && user_info->is_premium) { + object("is_premium", td::JsonTrue()); + } + if (user_info != nullptr && user_info->added_to_attachment_menu) { + object("added_to_attachment_menu", td::JsonTrue()); + } // start custom properties impl if (user_info != nullptr && user_info->is_verified) { @@ -803,6 +812,12 @@ class Client::JsonChat final : public Jsonable { if (supergroup_info->is_supergroup) { object("permissions", JsonChatPermissions(chat_info->permissions.get())); } + if (supergroup_info->is_supergroup && supergroup_info->join_to_send_messages) { + object("join_to_send_messages", td::JsonTrue()); + } + if (supergroup_info->is_supergroup && supergroup_info->join_by_request) { + object("join_by_request", td::JsonTrue()); + } if (supergroup_info->slow_mode_delay != 0) { object("slow_mode_delay", supergroup_info->slow_mode_delay); } @@ -1111,6 +1126,9 @@ class Client::JsonSticker final : public Jsonable { object("mask_position", JsonMaskPosition(mask_position.get())); } } + if (sticker_->premium_animation_ != nullptr) { + object("premium_animation", JsonFile(sticker_->premium_animation_.get(), client_, false)); + } client_->json_store_thumbnail(object, sticker_->thumbnail_.get()); client_->json_store_file(object, sticker_->sticker_.get()); } @@ -1298,7 +1316,7 @@ class Client::JsonInvoice final : public Jsonable { void store(JsonValueScope *scope) const { auto object = scope->enter_object(); object("title", invoice_->title_); - object("description", invoice_->description_); + object("description", invoice_->description_->text_); object("start_parameter", invoice_->start_parameter_); object("currency", invoice_->currency_); object("total_amount", invoice_->total_amount_); @@ -1982,10 +2000,7 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { } case td_api::messageChatChangePhoto::ID: { auto content = static_cast(message_->content.get()); - if (content->photo_ == nullptr) { - LOG(ERROR) << "Got empty messageChatChangePhoto"; - break; - } + CHECK(content->photo_ != nullptr); object("new_chat_photo", JsonChatPhoto(content->photo_.get(), client_)); break; } @@ -3906,6 +3921,25 @@ class Client::TdOnGetSupergroupMembersCountCallback final : public TdQueryCallba PromisedQueryPtr query_; }; +class Client::TdOnCreateInvoiceLinkCallback final : public TdQueryCallback { + public: + explicit TdOnCreateInvoiceLinkCallback(PromisedQueryPtr query) : 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::httpUrl::ID); + auto http_url = move_object_as(result); + return answer_query(td::VirtuallyJsonableString(http_url->url_), std::move(query_)); + } + + private: + PromisedQueryPtr query_; +}; + class Client::TdOnReplacePrimaryChatInviteLinkCallback final : public TdQueryCallback { public: explicit TdOnReplacePrimaryChatInviteLinkCallback(PromisedQueryPtr query) : query_(std::move(query)) { @@ -4006,7 +4040,7 @@ class Client::TdOnReturnFileCallback final : public TdQueryCallback { CHECK(result->get_id() == td_api::file::ID); auto file = move_object_as(result); - answer_query(JsonFile(file.get(), client_), std::move(query_)); + answer_query(JsonFile(file.get(), client_, false), std::move(query_)); } private: @@ -5270,7 +5304,9 @@ void Client::on_update(object_ptr result) { auto user_id = update->user_id_; auto full_info = update->user_full_info_.get(); set_user_photo(user_id, std::move(full_info->photo_)); - set_user_bio(user_id, std::move(full_info->bio_)); + if (full_info->bio_ != nullptr) { + set_user_bio(user_id, std::move(full_info->bio_->text_)); + } set_user_has_private_forwards(user_id, full_info->has_private_forwards_); break; } @@ -6152,8 +6188,9 @@ td::Result> Client::get_input_me return make_object( make_object(currency, std::move(prices), max_tip_amount, std::move(suggested_tip_amounts), - false, need_name, need_phone_number, need_email_address, need_shipping_address, - send_phone_number_to_provider, send_email_address_to_provider, is_flexible), + td::string(), false, need_name, need_phone_number, 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()); } @@ -7284,6 +7321,71 @@ td::Result>> Client:: return std::move(contents); } +td::Result> Client::get_input_message_invoice(const Query *query) { + TRY_RESULT(title, get_required_string_arg(query, "title")); + TRY_RESULT(description, get_required_string_arg(query, "description")); + TRY_RESULT(payload, get_required_string_arg(query, "payload")); + if (!td::check_utf8(payload.str())) { + return Status::Error(400, "The payload must be encoded in UTF-8"); + } + TRY_RESULT(provider_token, get_required_string_arg(query, "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")); + + TRY_RESULT(labeled_price_parts, get_required_string_arg(query, "prices")); + auto r_labeled_price_parts_value = json_decode(labeled_price_parts); + if (r_labeled_price_parts_value.is_error()) { + return Status::Error(400, "Can't parse prices JSON object"); + } + + TRY_RESULT(prices, get_labeled_price_parts(r_labeled_price_parts_value.ok_ref())); + + int64 max_tip_amount = 0; + td::vector suggested_tip_amounts; + { + auto max_tip_amount_str = query->arg("max_tip_amount"); + if (!max_tip_amount_str.empty()) { + auto r_max_tip_amount = td::to_integer_safe(max_tip_amount_str); + if (r_max_tip_amount.is_error()) { + return Status::Error(400, "Can't parse \"max_tip_amount\" as Number"); + } + max_tip_amount = r_max_tip_amount.ok(); + } + + auto suggested_tip_amounts_str = query->arg("suggested_tip_amounts"); + if (!suggested_tip_amounts_str.empty()) { + auto r_suggested_tip_amounts_value = json_decode(suggested_tip_amounts_str); + if (r_suggested_tip_amounts_value.is_error()) { + return Status::Error(400, "Can't parse suggested_tip_amounts JSON object"); + } + + TRY_RESULT_ASSIGN(suggested_tip_amounts, get_suggested_tip_amounts(r_suggested_tip_amounts_value.ok_ref())); + } + } + + auto photo_url = query->arg("photo_url"); + int32 photo_size = get_integer_arg(query, "photo_size", 0, 0, 1000000000); + int32 photo_width = get_integer_arg(query, "photo_width", 0, 0, MAX_LENGTH); + int32 photo_height = get_integer_arg(query, "photo_height", 0, 0, MAX_LENGTH); + + auto need_name = to_bool(query->arg("need_name")); + auto need_phone_number = to_bool(query->arg("need_phone_number")); + auto need_email_address = to_bool(query->arg("need_email")); + auto need_shipping_address = to_bool(query->arg("need_shipping_address")); + auto send_phone_number_to_provider = to_bool(query->arg("send_phone_number_to_provider")); + auto send_email_address_to_provider = to_bool(query->arg("send_email_to_provider")); + auto is_flexible = to_bool(query->arg("is_flexible")); + + return make_object( + make_object(currency.str(), std::move(prices), max_tip_amount, std::move(suggested_tip_amounts), + td::string(), false, need_name, need_phone_number, need_email_address, + 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()); +} + td::Result> Client::get_poll_options(const Query *query) { auto input_options = query->arg("options"); LOG(INFO) << "Parsing JSON object: " << input_options; @@ -7555,8 +7657,15 @@ void Client::on_message_send_failed(int64 chat_id, int64 old_message_id, int64 n auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id, nullptr); auto &query = *pending_send_message_queries_[query_id]; if (query.is_multisend) { - if (query.error == nullptr) { - query.error = std::move(error); + if (query.error == nullptr || query.error->message_ == "Group send failed") { + if (error->code_ == 429 || error->message_ == "Group send failed") { + query.error = std::move(error); + } else { + auto pos = (query.total_message_count - query.awaited_message_count + 1); + query.error = make_object(error->code_, PSTRING() << "Failed to send message #" << pos + << " with the error message \"" + << error->message_ << '"'); + } } query.awaited_message_count--; @@ -7870,69 +7979,8 @@ td::Status Client::process_send_game_query(PromisedQueryPtr &query) { td::Status Client::process_send_invoice_query(PromisedQueryPtr &query) { CHECK_IS_BOT(); - TRY_RESULT(title, get_required_string_arg(query.get(), "title")); - TRY_RESULT(description, get_required_string_arg(query.get(), "description")); - TRY_RESULT(payload, get_required_string_arg(query.get(), "payload")); - if (!td::check_utf8(payload.str())) { - return Status::Error(400, "The payload must be encoded in UTF-8"); - } - TRY_RESULT(provider_token, get_required_string_arg(query.get(), "provider_token")); - auto provider_data = query->arg("provider_data"); - auto start_parameter = query->arg("start_parameter"); - TRY_RESULT(currency, get_required_string_arg(query.get(), "currency")); - - TRY_RESULT(labeled_price_parts, get_required_string_arg(query.get(), "prices")); - auto r_labeled_price_parts_value = json_decode(labeled_price_parts); - if (r_labeled_price_parts_value.is_error()) { - return Status::Error(400, "Can't parse prices JSON object"); - } - - TRY_RESULT(prices, get_labeled_price_parts(r_labeled_price_parts_value.ok_ref())); - - int64 max_tip_amount = 0; - td::vector suggested_tip_amounts; - { - auto max_tip_amount_str = query->arg("max_tip_amount"); - if (!max_tip_amount_str.empty()) { - auto r_max_tip_amount = td::to_integer_safe(max_tip_amount_str); - if (r_max_tip_amount.is_error()) { - return Status::Error(400, "Can't parse \"max_tip_amount\" as Number"); - } - max_tip_amount = r_max_tip_amount.ok(); - } - - auto suggested_tip_amounts_str = query->arg("suggested_tip_amounts"); - if (!suggested_tip_amounts_str.empty()) { - auto r_suggested_tip_amounts_value = json_decode(suggested_tip_amounts_str); - if (r_suggested_tip_amounts_value.is_error()) { - return Status::Error(400, "Can't parse suggested_tip_amounts JSON object"); - } - - TRY_RESULT_ASSIGN(suggested_tip_amounts, get_suggested_tip_amounts(r_suggested_tip_amounts_value.ok_ref())); - } - } - - auto photo_url = query->arg("photo_url"); - int32 photo_size = get_integer_arg(query.get(), "photo_size", 0, 0, 1000000000); - int32 photo_width = get_integer_arg(query.get(), "photo_width", 0, 0, MAX_LENGTH); - int32 photo_height = get_integer_arg(query.get(), "photo_height", 0, 0, MAX_LENGTH); - - auto need_name = to_bool(query->arg("need_name")); - auto need_phone_number = to_bool(query->arg("need_phone_number")); - auto need_email_address = to_bool(query->arg("need_email")); - auto need_shipping_address = to_bool(query->arg("need_shipping_address")); - auto send_phone_number_to_provider = to_bool(query->arg("send_phone_number_to_provider")); - auto send_email_address_to_provider = to_bool(query->arg("send_email_to_provider")); - auto is_flexible = to_bool(query->arg("is_flexible")); - - do_send_message(make_object( - make_object( - currency.str(), std::move(prices), max_tip_amount, std::move(suggested_tip_amounts), false, - need_name, need_phone_number, need_email_address, 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(query)); + TRY_RESULT(input_message_invoice, get_input_message_invoice(query.get())); + do_send_message(std::move(input_message_invoice), std::move(query)); return Status::OK(); } @@ -8324,6 +8372,13 @@ td::Status Client::process_delete_message_query(PromisedQueryPtr &query) { return Status::OK(); } +td::Status Client::process_create_invoice_link_query(PromisedQueryPtr &query) { + TRY_RESULT(input_message_invoice, get_input_message_invoice(query.get())); + send_request(make_object(std::move(input_message_invoice)), + td::make_unique(std::move(query))); + return Status::OK(); +} + td::Status Client::process_set_game_score_query(PromisedQueryPtr &query) { CHECK_IS_BOT(); auto chat_id = query->arg("chat_id"); @@ -9212,16 +9267,20 @@ td::Status Client::process_set_webhook_query(PromisedQueryPtr &query) { // do not send warning just after webhook was deleted or set next_bot_updates_warning_time_ = td::max(next_bot_updates_warning_time_, now + BOT_UPDATES_WARNING_DELAY); + bool new_has_certificate = new_url.empty() ? false + : (get_webhook_certificate(query.get()) != nullptr || + (query->is_internal() && query->arg("certificate") == "previous")); int32 new_max_connections = new_url.empty() ? 0 : get_webhook_max_connections(query.get()); Slice new_ip_address = new_url.empty() ? Slice() : query->arg("ip_address"); bool new_fix_ip_address = new_url.empty() ? false : get_webhook_fix_ip_address(query.get()); + Slice new_secret_token = new_url.empty() ? Slice() : query->arg("secret_token"); bool drop_pending_updates = to_bool(query->arg("drop_pending_updates")); if (webhook_set_query_) { // already updating webhook. Cancel previous request fail_query_conflict("Conflict: terminated by other setWebhook", std::move(webhook_set_query_)); - } else if (webhook_url_ == new_url && !has_webhook_certificate_ && query->file("certificate") == nullptr && - query->arg("certificate").empty() && new_max_connections == webhook_max_connections_ && - new_fix_ip_address == webhook_fix_ip_address_ && + } else if (webhook_url_ == new_url && !has_webhook_certificate_ && !new_has_certificate && + new_max_connections == webhook_max_connections_ && new_fix_ip_address == webhook_fix_ip_address_ && + new_secret_token == webhook_secret_token_ && (!new_fix_ip_address || new_ip_address == webhook_ip_address_) && !drop_pending_updates) { if (update_allowed_update_types(query.get())) { save_webhook(); @@ -9790,7 +9849,7 @@ void Client::on_file_download(int32 file_id, td::Result const auto &error = r_file.error(); fail_query_with_error(std::move(query), error.code(), error.public_message()); } else { - answer_query(JsonFile(r_file.ok().get(), this), std::move(query)); + answer_query(JsonFile(r_file.ok().get(), this, true), std::move(query)); } } } @@ -9822,6 +9881,9 @@ void Client::save_webhook() const { if (webhook_fix_ip_address_) { value += "#fix_ip/"; } + if (!webhook_secret_token_.empty()) { + value += PSTRING() << "#secret" << webhook_secret_token_ << '/'; + } if (allowed_update_types_ != DEFAULT_ALLOWED_UPDATE_TYPES) { value += PSTRING() << "#allow" << allowed_update_types_ << '/'; } @@ -9865,6 +9927,7 @@ void Client::webhook_closed(Status status) { webhook_max_connections_ = 0; webhook_ip_address_ = td::string(); webhook_fix_ip_address_ = false; + webhook_secret_token_ = td::string(); webhook_set_time_ = td::Time::now(); last_webhook_error_date_ = 0; last_webhook_error_ = Status::OK(); @@ -9887,6 +9950,18 @@ td::string Client::get_webhook_certificate_path() const { return dir_ + "cert.pem"; } +const td::HttpFile *Client::get_webhook_certificate(const Query *query) const { + auto file = query->file("certificate"); + if (file == nullptr) { + auto attach_name = query->arg("certificate"); + Slice attach_protocol{"attach://"}; + if (td::begins_with(attach_name, attach_protocol)) { + file = query->file(attach_name.substr(attach_protocol.size())); + } + } + return file; +} + td::int32 Client::get_webhook_max_connections(const Query *query) const { auto default_value = parameters_->default_max_webhook_connections_; auto max_value = parameters_->local_mode_ ? 100000 : 100; @@ -9914,8 +9989,16 @@ void Client::do_set_webhook(PromisedQueryPtr query, bool was_deleted) { if (url.is_error()) { return fail_query(400, "Bad Request: invalid webhook URL specified", std::move(query)); } - auto *cert_file_ptr = query->file("certificate"); + auto secret_token = query->arg("secret_token"); + if (secret_token.size() > 256) { + return fail_query(400, "Bad Request: secret token is too long", std::move(query)); + } + if (!td::is_base64url_characters(secret_token)) { + return fail_query(400, "Bad Request: secret token contains unallowed characters", std::move(query)); + } + has_webhook_certificate_ = false; + auto *cert_file_ptr = get_webhook_certificate(query.get()); if (cert_file_ptr != nullptr) { auto size = cert_file_ptr->size; if (size > MAX_CERTIFICATE_FILE_SIZE) { @@ -9929,14 +10012,14 @@ void Client::do_set_webhook(PromisedQueryPtr query, bool was_deleted) { return fail_query(500, "Internal Server Error: failed to save certificate", std::move(query)); } has_webhook_certificate_ = true; - } - - if (query->is_internal() && query->arg("certificate") == "previous") { + } else if (query->is_internal() && query->arg("certificate") == "previous") { has_webhook_certificate_ = true; } + webhook_url_ = new_url.str(); webhook_set_time_ = td::Time::now(); webhook_max_connections_ = get_webhook_max_connections(query.get()); + webhook_secret_token_ = secret_token.str(); webhook_ip_address_ = query->arg("ip_address").str(); webhook_fix_ip_address_ = get_webhook_fix_ip_address(query.get()); last_webhook_error_date_ = 0; @@ -9949,7 +10032,7 @@ void Client::do_set_webhook(PromisedQueryPtr query, bool was_deleted) { webhook_id_ = td::create_actor( webhook_actor_name, actor_shared(this, webhook_generation_), tqueue_id_, url.move_as_ok(), has_webhook_certificate_ ? get_webhook_certificate_path() : "", webhook_max_connections_, query->is_internal(), - webhook_ip_address_, webhook_fix_ip_address_, parameters_); + webhook_ip_address_, webhook_fix_ip_address_, webhook_secret_token_, parameters_); // wait for webhook verified or webhook callback webhook_query_type_ = WebhookQueryType::Verify; webhook_set_query_ = std::move(query); @@ -10031,7 +10114,10 @@ void Client::on_sent_message(object_ptr &&message, int64 query_ auto emplace_result = yet_unsent_messages_.emplace(yet_unsent_message_id, yet_unsent_message); CHECK(emplace_result.second); yet_unsent_message_count_[chat_id]++; - pending_send_message_queries_[query_id]->awaited_message_count++; + + auto &query = *pending_send_message_queries_[query_id]; + query.awaited_message_count++; + query.total_message_count++; } void Client::abort_long_poll(bool from_set_webhook) { @@ -10210,6 +10296,8 @@ void Client::add_user(UserInfo *user_info, object_ptr &&user) { //end custom properties user_info->have_access = user->have_access_; + user_info->is_premium = user->is_premium_; + user_info->added_to_attachment_menu = user->added_to_attachment_menu_; switch (user->type_->get_id()) { case td_api::userTypeRegular::ID: @@ -10312,6 +10400,8 @@ void Client::add_supergroup(SupergroupInfo *supergroup_info, object_ptrstatus = std::move(supergroup->status_); supergroup_info->is_supergroup = !supergroup->is_channel_; supergroup_info->has_location = supergroup->has_location_; + supergroup_info->join_to_send_messages = supergroup->join_to_send_messages_; + supergroup_info->join_by_request = supergroup->join_by_request_; // start custom properties supergroup_info->is_verified = supergroup->is_verified_; @@ -10968,14 +11058,6 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptrcontent_->get_id()) { - case td_api::messagePhoto::ID: { - auto content = static_cast(message->content_.get()); - if (content->photo_ == nullptr) { - LOG(ERROR) << "Got empty messagePhoto"; - return true; - } - break; - } case td_api::messageChatAddMembers::ID: { auto content = static_cast(message->content_.get()); if (content->member_user_ids_.empty()) { @@ -10984,14 +11066,6 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr(message->content_.get()); - if (content->photo_ == nullptr) { - LOG(ERROR) << "Got empty messageChatChangePhoto"; - return true; - } - break; - } case td_api::messageSupergroupChatCreate::ID: { if (chat->type != ChatInfo::Type::Supergroup) { LOG(ERROR) << "Receive messageSupergroupChatCreate in the non-supergroup chat " << chat_id; @@ -11007,7 +11081,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr>> get_input_message_contents( const Query *query, td::JsonValue &&value) const; + static td::Result> get_input_message_invoice(const Query *query); + static object_ptr get_message_send_options(bool disable_notification, bool protect_content, object_ptr &&scheduling_state); @@ -556,6 +561,7 @@ class Client final : public WebhookActor::Callback { Status process_edit_message_caption_query(PromisedQueryPtr &query); Status process_edit_message_reply_markup_query(PromisedQueryPtr &query); Status process_delete_message_query(PromisedQueryPtr &query); + Status process_create_invoice_link_query(PromisedQueryPtr &query); Status process_set_game_score_query(PromisedQueryPtr &query); Status process_get_game_high_scores_query(PromisedQueryPtr &query); Status process_answer_web_app_query_query(PromisedQueryPtr &query); @@ -650,6 +656,7 @@ class Client final : public WebhookActor::Callback { void webhook_error(Status status) final; void webhook_closed(Status status) final; void hangup_shared() final; + const td::HttpFile *get_webhook_certificate(const Query *query) const; int32 get_webhook_max_connections(const Query *query) const; static bool get_webhook_fix_ip_address(const Query *query); void do_set_webhook(PromisedQueryPtr query, bool was_deleted); @@ -719,6 +726,8 @@ class Client final : public WebhookActor::Callback { bool can_read_all_group_messages = false; bool is_inline_bot = false; bool has_private_forwards = false; + bool is_premium = false; + bool added_to_attachment_menu = false; }; static void add_user(UserInfo *user_info, object_ptr &&user); void set_user_photo(int64 user_id, object_ptr &&photo); @@ -759,6 +768,8 @@ class Client final : public WebhookActor::Callback { bool is_supergroup = false; bool can_set_sticker_set = false; bool has_location = false; + bool join_to_send_messages = false; + bool join_by_request = false; // start custom properties bool is_verified = false; @@ -1054,6 +1065,7 @@ class Client final : public WebhookActor::Callback { struct PendingSendMessageQuery { PromisedQueryPtr query; bool is_multisend = false; + int32 total_message_count = 0; int32 awaited_message_count = 0; td::vector messages; object_ptr error; @@ -1131,6 +1143,7 @@ class Client final : public WebhookActor::Callback { int32 webhook_max_connections_ = 0; td::string webhook_ip_address_; bool webhook_fix_ip_address_ = false; + td::string webhook_secret_token_; int32 last_webhook_error_date_ = 0; Status last_webhook_error_; double next_allowed_set_webhook_time_ = 0; diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 6bf7900..6350a33 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -528,6 +528,11 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool parser.skip('/'); } + if (parser.try_skip("#secret")) { + args.emplace_back(add_string("secret_token"), add_string(parser.read_till('/'))); + parser.skip('/'); + } + if (parser.try_skip("#allow")) { args.emplace_back(add_string("allowed_updates"), add_string(parser.read_till('/'))); parser.skip('/'); diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index 97a2f79..d6cd7fe 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -42,7 +42,7 @@ std::atomic WebhookActor::total_connections_count_{0}; WebhookActor::WebhookActor(td::ActorShared callback, td::int64 tqueue_id, td::HttpUrl url, td::string cert_path, td::int32 max_connections, bool from_db_flag, - td::string cached_ip_address, bool fix_ip_address, + td::string cached_ip_address, bool fix_ip_address, td::string secret_token, std::shared_ptr parameters) : callback_(std::move(callback)) , tqueue_id_(tqueue_id) @@ -51,7 +51,8 @@ WebhookActor::WebhookActor(td::ActorShared callback, td::int64 tqueue_ , parameters_(std::move(parameters)) , fix_ip_address_(fix_ip_address) , from_db_flag_(from_db_flag) - , max_connections_(max_connections) { + , max_connections_(max_connections) + , secret_token_(std::move(secret_token)) { CHECK(max_connections_ > 0); if (!cached_ip_address.empty()) { @@ -539,6 +540,9 @@ td::Status WebhookActor::send_update() { if (!url_.userinfo_.empty()) { hc.add_header("Authorization", PSLICE() << "Basic " << td::base64_encode(url_.userinfo_)); } + if (!secret_token_.empty()) { + hc.add_header("X-Telegram-Bot-Api-Secret-Token", secret_token_); + } hc.set_content_type("application/json"); hc.set_content_size(body.size()); hc.set_keep_alive(); diff --git a/telegram-bot-api/WebhookActor.h b/telegram-bot-api/WebhookActor.h index 71d702e..d5e2499 100644 --- a/telegram-bot-api/WebhookActor.h +++ b/telegram-bot-api/WebhookActor.h @@ -54,7 +54,7 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback { WebhookActor(td::ActorShared callback, td::int64 tqueue_id, td::HttpUrl url, td::string cert_path, td::int32 max_connections, bool from_db_flag, td::string cached_ip_address, bool fix_ip_address, - std::shared_ptr parameters); + td::string secret_token, std::shared_ptr parameters); void update(); @@ -163,6 +163,7 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback { td::vector> ready_sockets_; td::int32 max_connections_ = 0; + td::string secret_token_; td::Container connections_; td::ListNode ready_connections_; td::FloodControlFast active_new_connection_flood_; diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 438f812..8c159a9 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -200,7 +200,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_ = "6.0.1"; + parameters->version_ = "6.1"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats();