diff options
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | build.html | 10 | ||||
| m--------- | td | 0 | ||||
| -rw-r--r-- | telegram-bot-api/Client.cpp | 1417 | ||||
| -rw-r--r-- | telegram-bot-api/Client.h | 156 | ||||
| -rw-r--r-- | telegram-bot-api/telegram-bot-api.cpp | 2 |
6 files changed, 1038 insertions, 549 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bed12b..6cd450d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) -project(TelegramBotApi VERSION 10.1 LANGUAGES CXX) +project(TelegramBotApi VERSION 10.2 LANGUAGES CXX) add_subdirectory(td EXCLUDE_FROM_ALL) @@ -208,6 +208,7 @@ <option>Ubuntu 20</option> <option>Ubuntu 22</option> <option>Ubuntu 24</option> + <option>Ubuntu 26</option> <option>Other</option> </select> <p></p> @@ -496,6 +497,8 @@ function onOptionsChanged() { return '-14'; case 'Ubuntu 24': return '-18'; + case 'Ubuntu 26': + return '-21'; default: return ''; // use default version } @@ -506,6 +509,7 @@ function onOptionsChanged() { case 'Ubuntu 20': case 'Ubuntu 22': case 'Ubuntu 24': + case 'Ubuntu 26': return getClangVersionSuffix(); default: return ''; // use default version @@ -556,6 +560,7 @@ function onOptionsChanged() { case 'Ubuntu 20': case 'Ubuntu 22': case 'Ubuntu 24': + case 'Ubuntu 26': if (linux_distro.includes('Debian') && !use_root) { commands.push('su -'); } @@ -563,10 +568,7 @@ function onOptionsChanged() { commands.push(sudo + 'apt-get upgrade'); var packages = 'make git zlib1g-dev libssl-dev gperf cmake'; if (use_clang) { - 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'; - } + packages += ' clang' + getClangVersionSuffix() + ' libc++' + getLibcplusplusVersionSuffix() + '-dev libc++abi' + getLibcplusplusVersionSuffix() + '-dev'; } else { packages += ' g++'; } diff --git a/td b/td -Subproject a8f21f5230172634becc1739050ef23ecd6ea29 +Subproject a9966eb3704a3351568c28013fed67d797c1782 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 068a505..0d16d48 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -54,7 +54,7 @@ Client::Client(td::ActorShared<> parent, const td::string &bot_token, bool is_te Client::~Client() { td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), messages_, users_, groups_, - supergroups_, chats_, sticker_set_names_); + supergroups_, chats_, communities_, sticker_set_names_); } int Client::get_retry_after_time(td::Slice error_message) { @@ -269,8 +269,13 @@ bool Client::init_methods() { methods_.emplace("editmessagecaption", &Client::process_edit_message_caption_query); methods_.emplace("editmessagechecklist", &Client::process_edit_message_checklist_query); methods_.emplace("editmessagereplymarkup", &Client::process_edit_message_reply_markup_query); + methods_.emplace("editephemeralmessagetext", &Client::process_edit_ephemeral_message_text_query); + methods_.emplace("editephemeralmessagemedia", &Client::process_edit_ephemeral_message_media_query); + methods_.emplace("editephemeralmessagecaption", &Client::process_edit_ephemeral_message_caption_query); + methods_.emplace("editephemeralmessagereplymarkup", &Client::process_edit_ephemeral_message_reply_markup_query); methods_.emplace("deletemessage", &Client::process_delete_message_query); methods_.emplace("deletemessages", &Client::process_delete_messages_query); + methods_.emplace("deleteephemeralmessage", &Client::process_delete_ephemeral_message_query); methods_.emplace("deletemessagereaction", &Client::process_delete_message_reaction_query); methods_.emplace("deleteallmessagereactions", &Client::process_delete_all_message_reactions_query); methods_.emplace("poststory", &Client::process_post_story_query); @@ -460,6 +465,23 @@ class Client::JsonDatedFiles final : public td::Jsonable { const Client *client_; }; +class Client::JsonCommunity final : public td::Jsonable { + public: + JsonCommunity(int64 community_id, const Client *client) : community_id_(community_id), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + auto community_info = client_->get_community_info(community_id_); + CHECK(community_info != nullptr); + object("id", community_id_); + object("name", community_info->name); + } + + private: + int64 community_id_; + const Client *client_; +}; + class Client::JsonUser final : public td::Jsonable { public: JsonUser(int64 user_id, const Client *client, bool full_bot_info = false) @@ -684,8 +706,8 @@ class Client::JsonRichText final : public td::Jsonable { *scope << td::JsonString(text->text_); return; } - if (text_id == td_api::richTextIcon::ID) { - LOG(ERROR) << "Receive richTextIcon"; + if (text_id == td_api::richTextIcon::ID || text_id == td_api::richTextDiff::ID) { + LOG(ERROR) << "Receive " << to_string(*text_); *scope << td::JsonString(td::Slice()); return; } @@ -1579,6 +1601,9 @@ class Client::JsonChat final : public td::Jsonable { if (user_info->personal_chat_id != 0) { object("personal_chat", JsonChat(user_info->personal_chat_id, client_)); } + if (user_info->community_id != 0) { + object("community", JsonCommunity(user_info->community_id, client_)); + } if (user_info->rating != nullptr) { object("rating", JsonUserRating(user_info->rating.get())); } @@ -1588,19 +1613,25 @@ class Client::JsonChat final : public td::Jsonable { if (user_info->paid_message_star_count > 0) { object("paid_message_star_count", user_info->paid_message_star_count); } + photo = user_info->photo.get(); } - photo = user_info->photo.get(); break; } case ChatInfo::Type::Group: { object("title", chat_info->title); object("type", "group"); - const auto *permissions = chat_info->permissions.get(); - - auto group_info = client_->get_group_info(chat_info->group_id); - CHECK(group_info != nullptr); + auto everyone_is_administrator = + 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_link_previews_ && + permissions->can_react_to_messages_ && permissions->can_edit_tag_ && permissions->can_change_info_ && + permissions->can_invite_users_ && permissions->can_pin_messages_; + object("all_members_are_administrators", td::JsonBool(everyone_is_administrator)); if (is_full_) { + auto group_info = client_->get_group_info(chat_info->group_id); + CHECK(group_info != nullptr); if (!group_info->description.empty()) { object("description", group_info->description); } @@ -1608,17 +1639,9 @@ class Client::JsonChat final : public td::Jsonable { object("invite_link", group_info->invite_link); } object("permissions", JsonChatPermissions(permissions)); + object("accepted_gift_types", JsonAcceptedGiftTypes(false, false, false, false, false)); + photo = group_info->photo.get(); } - auto everyone_is_administrator = - 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_link_previews_ && - permissions->can_react_to_messages_ && permissions->can_edit_tag_ && permissions->can_change_info_ && - permissions->can_invite_users_ && permissions->can_pin_messages_; - object("all_members_are_administrators", td::JsonBool(everyone_is_administrator)); - object("accepted_gift_types", JsonAcceptedGiftTypes(false, false, false, false, false)); - photo = group_info->photo.get(); break; } case ChatInfo::Type::Supergroup: { @@ -1705,6 +1728,9 @@ class Client::JsonChat final : public td::Jsonable { if (supergroup_info->guard_bot_user_id != 0) { object("guard_bot", JsonUser(supergroup_info->guard_bot_user_id, client_)); } + if (supergroup_info->community_id != 0) { + object("community", JsonCommunity(supergroup_info->community_id, client_)); + } if (supergroup_info->location != nullptr) { object("location", JsonChatLocation(supergroup_info->location.get())); } @@ -1718,8 +1744,8 @@ class Client::JsonChat final : public td::Jsonable { if (supergroup_info->paid_message_star_count) { object("paid_message_star_count", supergroup_info->paid_message_star_count); } + photo = supergroup_info->photo.get(); } - photo = supergroup_info->photo.get(); break; } case ChatInfo::Type::Unknown: @@ -2004,11 +2030,10 @@ class Client::JsonSuggestedPostPrice final : public td::Jsonable { object("currency", "XTR"); object("amount", static_cast<const td_api::suggestedPostPriceStar *>(suggested_post_price_)->star_count_); break; - case td_api::suggestedPostPriceTon::ID: + case td_api::suggestedPostPriceGram::ID: object("currency", "TON"); - object( - "amount", - static_cast<const td_api::suggestedPostPriceTon *>(suggested_post_price_)->toncoin_cent_count_ * 10000000); + object("amount", + static_cast<const td_api::suggestedPostPriceGram *>(suggested_post_price_)->gram_cent_count_ * 10000000); break; default: UNREACHABLE(); @@ -3067,7 +3092,7 @@ class Client::JsonSuggestedPostPaid final : public td::Jsonable { object("star_amount", JsonStarAmount(suggested_post_paid_->star_amount_.get())); } else { object("currency", "TON"); - object("amount", suggested_post_paid_->ton_amount_); + object("amount", suggested_post_paid_->gram_amount_); } } @@ -3534,10 +3559,10 @@ class Client::JsonUniqueGiftMessage final : public td::Jsonable { object("last_resale_amount", price->star_count_); break; } - case td_api::giftResalePriceTon::ID: { - auto price = static_cast<const td_api::giftResalePriceTon *>(origin->price_.get()); + case td_api::giftResalePriceGram::ID: { + auto price = static_cast<const td_api::giftResalePriceGram *>(origin->price_.get()); object("last_resale_currency", "TON"); - object("last_resale_amount", price->toncoin_cent_count_ * 10000000); + object("last_resale_amount", price->gram_cent_count_ * 10000000); break; } default: @@ -3904,6 +3929,46 @@ class Client::JsonManagedBotUpdated final : public td::Jsonable { const Client *client_; }; +class Client::JsonBotSubscriptionUpdated final : public td::Jsonable { + public: + JsonBotSubscriptionUpdated(const td_api::updateUserSubscription *update_user_subscription, const Client *client) + : update_user_subscription_(update_user_subscription), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("user", JsonUser(update_user_subscription_->user_id_, client_)); + object("invoice_payload", update_user_subscription_->payload_); + if (update_user_subscription_->is_canceled_) { + object("state", "canceled"); + } else if (update_user_subscription_->is_restored_) { + object("state", "active"); + } else if (update_user_subscription_->is_payment_failed_) { + object("state", "failed"); + } else { + UNREACHABLE(); + } + } + + private: + const td_api::updateUserSubscription *update_user_subscription_; + const Client *client_; +}; + +class Client::JsonCommunityChatAdded final : public td::Jsonable { + public: + JsonCommunityChatAdded(const td_api::messageChatAddedToCommunity *chat_added_to_community, const Client *client) + : chat_added_to_community_(chat_added_to_community), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("community", JsonCommunity(chat_added_to_community_->community_id_, client_)); + } + + private: + const td_api::messageChatAddedToCommunity *chat_added_to_community_; + const Client *client_; +}; + class Client::JsonGiveawayCreated final : public td::Jsonable { public: explicit JsonGiveawayCreated(const td_api::messageGiveawayCreated *giveaway_created) @@ -4676,7 +4741,10 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("sender_business_bot", JsonUser(message_->sender_business_bot_user_id, client_)); } } - object("message_id", message_->is_scheduled ? 0 : as_client_message_id(message_->id)); + object("message_id", as_client_message_id_unchecked(message_->id)); + if (message_->ephemeral_message_id != 0) { + object("ephemeral_message_id", message_->ephemeral_message_id); + } if (message_->sender_user_id != 0) { object("from", JsonUser(message_->sender_user_id, client_)); } @@ -4695,6 +4763,9 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { if (forum_topic_id != 0) { object("message_thread_id", forum_topic_id); } + if (message_->receiver_id != nullptr) { + client_->json_store_message_sender(object, message_->receiver_id, "receiver_user", "receiver_chat"); + } if (message_->initial_send_date > 0) { CHECK(message_->forward_origin != nullptr); object("forward_origin", JsonMessageOrigin(message_->forward_origin.get(), message_->initial_send_date, client_)); @@ -5285,6 +5356,14 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("rich_message", JsonRichMessage(content->message_.get(), client_)); break; } + case td_api::messageChatAddedToCommunity::ID: { + auto content = static_cast<const td_api::messageChatAddedToCommunity *>(message_->content.get()); + object("community_chat_added", JsonCommunityChatAdded(content, client_)); + break; + } + case td_api::messageChatRemovedFromCommunity::ID: + object("community_chat_removed", JsonEmptyObject()); + break; default: UNREACHABLE(); } @@ -5612,6 +5691,9 @@ class Client::JsonBotCommand final : public td::Jsonable { auto object = scope->enter_object(); object("command", command_->command_); object("description", command_->description_); + if (command_->is_ephemeral_) { + object("is_ephemeral", td::JsonTrue()); + } } private: @@ -6504,7 +6586,7 @@ class Client::JsonStarTransactions final : public td::Jsonable { class Client::JsonUpdateTypes final : public td::Jsonable { public: - explicit JsonUpdateTypes(td::uint32 update_types) : update_types_(update_types) { + explicit JsonUpdateTypes(td::uint64 update_types) : update_types_(update_types) { } void store(td::JsonValueScope *scope) const { auto array = scope->enter_array(); @@ -6519,7 +6601,7 @@ class Client::JsonUpdateTypes final : public td::Jsonable { } private: - td::uint32 update_types_; + td::uint64 update_types_; }; class Client::JsonWebhookInfo final : public td::Jsonable { @@ -9060,6 +9142,11 @@ void Client::check_reply_parameters(td::Slice chat_id_str, InputReplyParameters chat_id_str, forum_topic_id, direct_messages_topic_id, std::move(query), [this, reply_parameters = std::move(reply_parameters), on_success = std::move(on_success)]( int64 chat_id, object_ptr<td_api::MessageTopic> &&topic_id, PromisedQueryPtr query) mutable { + if (reply_parameters.reply_to_ephemeral_message_id > 0) { + CheckedReplyParameters checked_reply_parameters; + checked_reply_parameters.reply_to_ephemeral_message_id = reply_parameters.reply_to_ephemeral_message_id; + return on_success(chat_id, std::move(topic_id), std::move(checked_reply_parameters), std::move(query)); + } auto on_reply_message_resolved = [this, chat_id, topic_id = std::move(topic_id), quote = std::move(reply_parameters.quote), checklist_task_id = reply_parameters.checklist_task_id, @@ -9622,6 +9709,7 @@ void Client::on_update(object_ptr<td_api::Object> result) { user_info->personal_chat_id = full_info->personal_chat_id_; user_info->has_private_forwards = full_info->has_private_forwards_; user_info->has_restricted_voice_and_video_messages = full_info->has_restricted_voice_and_video_note_messages_; + user_info->community_id = full_info->community_id_; break; } case td_api::updateBasicGroup::ID: { @@ -9647,6 +9735,12 @@ void Client::on_update(object_ptr<td_api::Object> result) { add_supergroup(supergroup_info, std::move(update->supergroup_)); break; } + case td_api::updateCommunity::ID: { + auto update = move_object_as<td_api::updateCommunity>(result); + auto *community_info = add_community_info(update->community_->id_); + add_community(community_info, std::move(update->community_)); + break; + } case td_api::updateSupergroupFullInfo::ID: { auto update = move_object_as<td_api::updateSupergroupFullInfo>(result); auto supergroup_id = update->supergroup_id_; @@ -9666,6 +9760,7 @@ void Client::on_update(object_ptr<td_api::Object> result) { supergroup_info->linked_chat_id = full_info->linked_chat_id_; supergroup_info->direct_messages_chat_id = full_info->direct_messages_chat_id_; supergroup_info->guard_bot_user_id = full_info->guard_bot_user_id_; + supergroup_info->community_id = full_info->community_id_; 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_; @@ -9709,7 +9804,7 @@ void Client::on_update(object_ptr<td_api::Object> result) { } else { CHECK(update->value_->get_id() == td_api::optionValueInteger::ID); allowed_update_types_ = - static_cast<td::uint32>(move_object_as<td_api::optionValueInteger>(update->value_)->value_); + static_cast<td::uint64>(move_object_as<td_api::optionValueInteger>(update->value_)->value_); } } if (name == "unix_time" && update->value_->get_id() != td_api::optionValueEmpty::ID) { @@ -9761,6 +9856,9 @@ void Client::on_update(object_ptr<td_api::Object> result) { case td_api::updateManagedBot::ID: add_update_managed_bot(move_object_as<td_api::updateManagedBot>(result)); break; + case td_api::updateUserSubscription::ID: + add_update_subscription(move_object_as<td_api::updateUserSubscription>(result)); + break; case td_api::updateNewCustomEvent::ID: add_new_custom_event(move_object_as<td_api::updateNewCustomEvent>(result)); break; @@ -9941,8 +10039,56 @@ bool Client::to_bool(td::MutableSlice value) { return value == "true" || value == "yes" || value == "1"; } +template <class T> +td::Result<td::vector<T>> Client::get_array(td::JsonValue &&values, td::Slice class_name, + td::Result<T> (*func)(td::JsonValue &&value)) { + td::vector<T> items; + if (values.type() != td::JsonValue::Type::Array) { + if (values.type() == td::JsonValue::Type::Null) { + return std::move(items); + } + return td::Status::Error(400, PSLICE() << "Expected an Array of " << class_name); + } + + for (auto &value : values.get_array()) { + auto r_item = func(std::move(value)); + if (r_item.is_error()) { + return td::Status::Error(400, PSLICE() << "Can't parse " << class_name << ": " << r_item.error().message()); + } + items.push_back(r_item.move_as_ok()); + } + + return std::move(items); +} + +template <class T> +td::Result<td::vector<td_api::object_ptr<T>>> Client::get_array_generic( + td::JsonValue &&values, td::Slice class_name, + const std::function<td::Result<object_ptr<T>>(td::JsonValue &&value)> &func) { + td::vector<object_ptr<T>> items; + if (values.type() != td::JsonValue::Type::Array) { + if (values.type() == td::JsonValue::Type::Null) { + return std::move(items); + } + return td::Status::Error(400, PSLICE() << "Expected an Array of " << class_name); + } + + for (auto &value : values.get_array()) { + auto r_item = func(std::move(value)); + if (r_item.is_error()) { + return td::Status::Error(400, PSLICE() << "Can't parse " << class_name << ": " << r_item.error().message()); + } + items.push_back(r_item.move_as_ok()); + } + + return std::move(items); +} + td_api::object_ptr<td_api::InputMessageReplyTo> Client::get_input_message_reply_to( CheckedReplyParameters &&reply_parameters) { + if (reply_parameters.reply_to_ephemeral_message_id > 0) { + return make_object<td_api::inputMessageReplyToEphemeralMessage>(reply_parameters.reply_to_ephemeral_message_id); + } if (reply_parameters.reply_to_message_id > 0) { if (reply_parameters.reply_in_chat_id != 0) { return make_object<td_api::inputMessageReplyToExternalMessage>( @@ -9956,7 +10102,7 @@ td_api::object_ptr<td_api::InputMessageReplyTo> Client::get_input_message_reply_ return nullptr; } -td_api::object_ptr<td_api::InputMessageReplyTo> Client::get_input_message_reply_to( +td_api::object_ptr<td_api::InputMessageReplyTo> Client::get_input_message_reply_to_unchecked( InputReplyParameters &&reply_parameters) { if (reply_parameters.reply_in_chat_id.empty() && reply_parameters.reply_to_message_id > 0) { return make_object<td_api::inputMessageReplyToMessage>( @@ -9998,7 +10144,7 @@ td::Result<Client::InputReplyParameters> Client::get_reply_parameters(td::JsonVa return InputReplyParameters(); } TRY_RESULT(chat_id, object.get_optional_string_field("chat_id")); - TRY_RESULT(message_id, object.get_required_int_field("message_id")); + TRY_RESULT(message_id, object.get_optional_int_field("message_id")); TRY_RESULT(allow_sending_without_reply, object.get_optional_bool_field("allow_sending_without_reply")); TRY_RESULT(input_quote, object.get_optional_string_field("quote")); TRY_RESULT(parse_mode, object.get_optional_string_field("quote_parse_mode")); @@ -10007,10 +10153,12 @@ td::Result<Client::InputReplyParameters> Client::get_reply_parameters(td::JsonVa TRY_RESULT(quote_position, object.get_optional_int_field("quote_position")); TRY_RESULT(checklist_task_id, object.get_optional_int_field("checklist_task_id")); TRY_RESULT(poll_option_id, object.get_optional_string_field("poll_option_id")); + TRY_RESULT(ephemeral_message_id, object.get_optional_int_field("ephemeral_message_id")); InputReplyParameters result; result.reply_in_chat_id = std::move(chat_id); result.reply_to_message_id = as_tdlib_message_id(td::max(message_id, 0)); + result.reply_to_ephemeral_message_id = td::max(ephemeral_message_id, 0); result.allow_sending_without_reply = allow_sending_without_reply; result.quote = make_object<td_api::inputTextQuote>(std::move(quote), quote_position); result.checklist_task_id = checklist_task_id; @@ -10135,7 +10283,7 @@ td::Result<td_api::object_ptr<td_api::KeyboardButtonType>> Client::get_keyboard_ return nullptr; } -td::Result<td_api::object_ptr<td_api::keyboardButton>> Client::get_keyboard_button(td::JsonValue &button) { +td::Result<td_api::object_ptr<td_api::keyboardButton>> Client::get_keyboard_button(td::JsonValue &&button) { if (button.type() == td::JsonValue::Type::Object) { auto &object = button.get_object(); @@ -10153,7 +10301,7 @@ td::Result<td_api::object_ptr<td_api::keyboardButton>> Client::get_keyboard_butt } td::Result<td_api::object_ptr<td_api::inlineKeyboardButton>> Client::get_inline_keyboard_button( - td::JsonValue &button, BotUserIds &bot_user_ids) { + td::JsonValue &&button, BotUserIds &bot_user_ids) { if (button.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "InlineKeyboardButton must be an Object"); } @@ -10303,18 +10451,7 @@ td::Result<td_api::object_ptr<td_api::ReplyMarkup>> Client::get_reply_markup(td: TRY_RESULT(keyboard, object.extract_optional_field("keyboard", td::JsonValue::Type::Array)); if (keyboard.type() == td::JsonValue::Type::Array) { for (auto &row : keyboard.get_array()) { - td::vector<object_ptr<td_api::keyboardButton>> new_row; - if (row.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Field \"keyboard\" must be an Array of Arrays"); - } - for (auto &button : row.get_array()) { - auto r_button = get_keyboard_button(button); - if (r_button.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse keyboard button: " << r_button.error().message()); - } - new_row.push_back(r_button.move_as_ok()); - } - + TRY_RESULT(new_row, get_array(std::move(row), "KeyboardButton", get_keyboard_button)); rows.push_back(std::move(new_row)); } } @@ -10322,22 +10459,12 @@ td::Result<td_api::object_ptr<td_api::ReplyMarkup>> Client::get_reply_markup(td: td::vector<td::vector<object_ptr<td_api::inlineKeyboardButton>>> inline_rows; TRY_RESULT(inline_keyboard, object.extract_optional_field("inline_keyboard", td::JsonValue::Type::Array)); if (inline_keyboard.type() == td::JsonValue::Type::Array) { - for (auto &inline_row : inline_keyboard.get_array()) { - td::vector<object_ptr<td_api::inlineKeyboardButton>> new_inline_row; - if (inline_row.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, - "Field \"inline_keyboard\" of the InlineKeyboardMarkup must be an Array of Arrays"); - } - for (auto &button : inline_row.get_array()) { - auto r_button = get_inline_keyboard_button(button, bot_user_ids); - if (r_button.is_error()) { - return td::Status::Error(400, PSLICE() - << "Can't parse inline keyboard button: " << r_button.error().message()); - } - new_inline_row.push_back(r_button.move_as_ok()); - } - - inline_rows.push_back(std::move(new_inline_row)); + for (auto &row : inline_keyboard.get_array()) { + TRY_RESULT(new_row, get_array_generic<td_api::inlineKeyboardButton>( + std::move(row), "InlineKeyboardButton", [&](td::JsonValue &&button) { + return get_inline_keyboard_button(std::move(button), bot_user_ids); + })); + inline_rows.push_back(std::move(new_row)); } } @@ -10385,9 +10512,9 @@ td::Result<td_api::object_ptr<td_api::SuggestedPostPrice>> Client::get_suggested } if (currency == "TON") { if (amount % 10000000 != 0) { - return td::Status::Error(400, "Suggested post price must be divisible by 10000000 nanotoncoins"); + return td::Status::Error(400, "Suggested post price must be divisible by 10000000 nanograms"); } - return make_object<td_api::suggestedPostPriceTon>(amount / 10000000); + return make_object<td_api::suggestedPostPriceGram>(amount / 10000000); } return td::Status::Error(400, "Invalid suggested post price currency specified"); } @@ -10426,7 +10553,7 @@ td::Result<td_api::object_ptr<td_api::inputSuggestedPostInfo>> Client::get_input return make_object<td_api::inputSuggestedPostInfo>(std::move(price), send_date); } -td::Result<td_api::object_ptr<td_api::labeledPricePart>> Client::get_labeled_price_part(td::JsonValue &value) { +td::Result<td_api::object_ptr<td_api::labeledPricePart>> Client::get_labeled_price_part(td::JsonValue &&value) { if (value.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "LabeledPrice must be an Object"); } @@ -10443,51 +10570,31 @@ td::Result<td_api::object_ptr<td_api::labeledPricePart>> Client::get_labeled_pri } td::Result<td::vector<td_api::object_ptr<td_api::labeledPricePart>>> Client::get_labeled_price_parts( - td::JsonValue &value) { - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of labeled prices"); - } + td::JsonValue &&value) { + return get_array(std::move(value), "LabeledPrice", get_labeled_price_part); +} - td::vector<object_ptr<td_api::labeledPricePart>> prices; - for (auto &price : value.get_array()) { - auto r_labeled_price = get_labeled_price_part(price); - if (r_labeled_price.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse labeled price: " << r_labeled_price.error().message()); - } - prices.push_back(r_labeled_price.move_as_ok()); +td::Result<td::int64> Client::get_suggested_tip_amount(td::JsonValue &&value) { + td::Slice number; + if (value.type() == td::JsonValue::Type::Number) { + number = value.get_number(); + } else if (value.type() == td::JsonValue::Type::String) { + number = value.get_string(); + } else { + return td::Status::Error(400, "Suggested tip amount must be of type Number or String"); } - if (prices.empty()) { - return td::Status::Error(400, "There must be at least one price"); + auto parsed_amount = td::to_integer_safe<int64>(number); + if (parsed_amount.is_error()) { + return td::Status::Error(400, "Can't parse suggested tip amount as Number"); } - - return std::move(prices); + return parsed_amount.ok(); } -td::Result<td::vector<td::int64>> Client::get_suggested_tip_amounts(td::JsonValue &value) { - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of suggested tip amounts"); - } - - td::vector<int64> suggested_tip_amounts; - for (auto &amount : value.get_array()) { - td::Slice number; - if (amount.type() == td::JsonValue::Type::Number) { - number = amount.get_number(); - } else if (amount.type() == td::JsonValue::Type::String) { - number = amount.get_string(); - } else { - return td::Status::Error(400, "Suggested tip amount must be of type Number or String"); - } - auto parsed_amount = td::to_integer_safe<int64>(number); - if (parsed_amount.is_error()) { - return td::Status::Error(400, "Can't parse suggested tip amount as Number"); - } - suggested_tip_amounts.push_back(parsed_amount.ok()); - } - return std::move(suggested_tip_amounts); +td::Result<td::vector<td::int64>> Client::get_suggested_tip_amounts(td::JsonValue &&value) { + return get_array(std::move(value), "suggested tip amount", get_suggested_tip_amount); } -td::Result<td_api::object_ptr<td_api::shippingOption>> Client::get_shipping_option(td::JsonValue &option) { +td::Result<td_api::object_ptr<td_api::shippingOption>> Client::get_shipping_option(td::JsonValue &&option) { if (option.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "ShippingOption must be an Object"); } @@ -10506,7 +10613,7 @@ td::Result<td_api::object_ptr<td_api::shippingOption>> Client::get_shipping_opti TRY_RESULT(prices_json, object.extract_required_field("prices", td::JsonValue::Type::Array)); - auto r_prices = get_labeled_price_parts(prices_json); + auto r_prices = get_labeled_price_parts(std::move(prices_json)); if (r_prices.is_error()) { return td::Status::Error(400, PSLICE() << "Can't parse shipping option prices: " << r_prices.error().message()); } @@ -10524,27 +10631,7 @@ td::Result<td::vector<td_api::object_ptr<td_api::shippingOption>>> Client::get_s return td::Status::Error(400, "Can't parse shipping options JSON object"); } - return get_shipping_options(r_value.move_as_ok()); -} - -td::Result<td::vector<td_api::object_ptr<td_api::shippingOption>>> Client::get_shipping_options(td::JsonValue &&value) { - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of shipping options"); - } - - td::vector<object_ptr<td_api::shippingOption>> options; - for (auto &option : value.get_array()) { - auto r_shipping_option = get_shipping_option(option); - if (r_shipping_option.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse shipping option: " << r_shipping_option.error().message()); - } - options.push_back(r_shipping_option.move_as_ok()); - } - if (options.empty()) { - return td::Status::Error(400, "There must be at least one shipping option"); - } - - return std::move(options); + return get_array(r_value.move_as_ok(), "ShippingOption", get_shipping_option); } td_api::object_ptr<td_api::ChatAction> Client::get_chat_action(const Query *query) { @@ -10674,13 +10761,10 @@ td::Result<td_api::object_ptr<td_api::inputThumbnail>> Client::get_input_thumbna } td::Result<td_api::object_ptr<td_api::InputMessageContent>> Client::get_input_message_content( - td::JsonValue &input_message_content, bool is_input_message_content_required) { - CHECK(input_message_content.type() == td::JsonValue::Type::Object); - auto &object = input_message_content.get_object(); - + const Query *query, td::JsonObject &object, bool is_input_message_content_required) const { if (object.has_field("rich_message")) { TRY_RESULT(rich_message, object.extract_required_field("rich_message", td::JsonValue::Type::Object)); - TRY_RESULT(input_rich_message, get_input_rich_message(std::move(rich_message))); + TRY_RESULT(input_rich_message, get_input_rich_message(query, std::move(rich_message))); return make_object<td_api::inputMessageRichMessage>(std::move(input_rich_message), false); } @@ -10741,15 +10825,10 @@ td::Result<td_api::object_ptr<td_api::InputMessageContent>> Client::get_input_me 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)); + TRY_RESULT(prices, get_labeled_price_parts(std::move(prices_object))); TRY_RESULT(provider_data, object.get_optional_string_field("provider_data")); TRY_RESULT(max_tip_amount, object.get_optional_long_field("max_tip_amount")); - td::vector<int64> suggested_tip_amounts; - TRY_RESULT(suggested_tip_amounts_array, - object.extract_optional_field("suggested_tip_amounts", td::JsonValue::Type::Array)); - if (suggested_tip_amounts_array.type() == td::JsonValue::Type::Array) { - TRY_RESULT_ASSIGN(suggested_tip_amounts, get_suggested_tip_amounts(suggested_tip_amounts_array)); - } + TRY_RESULT(suggested_tip_amounts, get_suggested_tip_amounts(object.extract_field("suggested_tip_amounts"))); TRY_RESULT(photo_url, object.get_optional_string_field("photo_url")); TRY_RESULT(photo_size, object.get_optional_int_field("photo_size")); TRY_RESULT(photo_width, object.get_optional_int_field("photo_width")); @@ -10834,7 +10913,7 @@ td::Result<td_api::object_ptr<td_api::inlineQueryResultsButton>> Client::get_inl } td::Result<td::vector<td_api::object_ptr<td_api::InputInlineQueryResult>>> Client::get_inline_query_results( - const Query *query, BotUserIds &bot_user_ids) { + const Query *query, BotUserIds &bot_user_ids) const { auto results_encoded = query->arg("results"); if (results_encoded.empty()) { return td::vector<object_ptr<td_api::InputInlineQueryResult>>(); @@ -10847,37 +10926,13 @@ td::Result<td::vector<td_api::object_ptr<td_api::InputInlineQueryResult>>> Clien 400, PSLICE() << "Can't parse JSON encoded inline query results: " << r_values.error().message()); } - return get_inline_query_results(r_values.move_as_ok(), bot_user_ids); -} - -td::Result<td::vector<td_api::object_ptr<td_api::InputInlineQueryResult>>> Client::get_inline_query_results( - td::JsonValue &&values, BotUserIds &bot_user_ids) { - if (values.type() == td::JsonValue::Type::Null) { - return td::vector<object_ptr<td_api::InputInlineQueryResult>>(); - } - if (values.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of inline query results"); - } - constexpr std::size_t MAX_INLINE_QUERY_RESULT_COUNT = 50; - if (values.get_array().size() > MAX_INLINE_QUERY_RESULT_COUNT) { - return td::Status::Error(400, "Too many inline query results specified"); - } - - td::vector<object_ptr<td_api::InputInlineQueryResult>> inline_query_results; - for (auto &value : values.get_array()) { - auto r_inline_query_result = get_inline_query_result(std::move(value), bot_user_ids); - if (r_inline_query_result.is_error()) { - return td::Status::Error( - 400, PSLICE() << "Can't parse inline query result: " << r_inline_query_result.error().message()); - } - inline_query_results.push_back(r_inline_query_result.move_as_ok()); - } - - return std::move(inline_query_results); + return get_array_generic<td_api::InputInlineQueryResult>( + r_values.move_as_ok(), "InlineQueryResult", + [&](td::JsonValue &&value) { return get_inline_query_result(query, std::move(value), bot_user_ids); }); } td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inline_query_result( - const Query *query, BotUserIds &bot_user_ids) { + const Query *query, BotUserIds &bot_user_ids) const { auto result_encoded = query->arg("result"); if (result_encoded.empty()) { return td::Status::Error(400, "Result isn't specified"); @@ -10890,11 +10945,11 @@ td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inlin 400, PSLICE() << "Can't parse JSON encoded web view query results " << r_value.error().message()); } - return get_inline_query_result(r_value.move_as_ok(), bot_user_ids); + return get_inline_query_result(query, r_value.move_as_ok(), bot_user_ids); } td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inline_query_result( - td::JsonValue &&value, BotUserIds &bot_user_ids) { + const Query *query, td::JsonValue &&value, BotUserIds &bot_user_ids) const { if (value.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "Inline query result must be an object"); } @@ -10926,8 +10981,9 @@ td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inlin input_message_content = std::move(input_message_text); } } else { + auto &input_message_content_object = input_message_content_obj.get_object(); TRY_RESULT(input_message_content_result, - get_input_message_content(input_message_content_obj, is_input_message_content_required)); + get_input_message_content(query, input_message_content_object, is_input_message_content_required)); input_message_content = std::move(input_message_content_result); } TRY_RESULT(input_caption, object.get_optional_string_field("caption")); @@ -11107,7 +11163,7 @@ td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inlin TRY_RESULT(sticker_file_id, object.get_required_string_field("sticker_file_id")); if (input_message_content == nullptr) { - input_message_content = make_object<td_api::inputMessageSticker>(nullptr, nullptr, 0, 0, td::string()); + input_message_content = make_object<td_api::inputMessageSticker>(nullptr, td::string()); } return make_object<td_api::inputInlineQueryResultSticker>(id, "", sticker_file_id, 0, 0, std::move(reply_markup), std::move(input_message_content)); @@ -11154,8 +11210,7 @@ td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inlin } if (input_message_content == nullptr) { - input_message_content = make_object<td_api::inputMessageVoiceNote>( - nullptr, voice_note_duration, "" /* waveform */, std::move(caption), nullptr); + input_message_content = make_object<td_api::inputMessageVoiceNote>(nullptr, std::move(caption), nullptr); } return make_object<td_api::inputInlineQueryResultVoiceNote>( id, title, voice_note_url, voice_note_duration, std::move(reply_markup), std::move(input_message_content)); @@ -11236,8 +11291,9 @@ td::Result<td_api::object_ptr<td_api::botCommand>> Client::get_bot_command(td::J TRY_RESULT(command, object.get_required_string_field("command")); TRY_RESULT(description, object.get_required_string_field("description")); + TRY_RESULT(is_ephemeral, object.get_optional_bool_field("is_ephemeral")); - return make_object<td_api::botCommand>(command, description); + return make_object<td_api::botCommand>(command, description, is_ephemeral); } td::Result<td::vector<td_api::object_ptr<td_api::botCommand>>> Client::get_bot_commands(const Query *query) { @@ -11252,20 +11308,7 @@ td::Result<td::vector<td_api::object_ptr<td_api::botCommand>>> Client::get_bot_c return td::Status::Error(400, "Can't parse commands JSON object"); } - auto value = r_value.move_as_ok(); - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of BotCommand"); - } - - td::vector<object_ptr<td_api::botCommand>> bot_commands; - for (auto &command : value.get_array()) { - auto r_bot_command = get_bot_command(std::move(command)); - if (r_bot_command.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse BotCommand: " << r_bot_command.error().message()); - } - bot_commands.push_back(r_bot_command.move_as_ok()); - } - return std::move(bot_commands); + return get_array(r_value.move_as_ok(), "BotCommand", get_bot_command); } td::Result<td_api::object_ptr<td_api::botMenuButton>> Client::get_bot_menu_button(td::JsonValue &&value) { @@ -11504,9 +11547,8 @@ td::Result<td_api::object_ptr<td_api::StickerFormat>> Client::get_sticker_format return td::Status::Error(400, "Invalid sticker format specified"); } -td::Result<td_api::object_ptr<td_api::inputSticker>> Client::get_input_sticker(const Query *query, - td::JsonValue &&value, - td::Slice default_sticker_format) const { +td::Result<td_api::object_ptr<td_api::newSticker>> Client::get_new_sticker(const Query *query, td::JsonValue &&value, + td::Slice default_sticker_format) const { if (value.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "InputSticker must be an Object"); } @@ -11541,11 +11583,11 @@ td::Result<td_api::object_ptr<td_api::inputSticker>> Client::get_input_sticker(c input_keywords.push_back(keyword.get_string().str()); } } - return make_object<td_api::inputSticker>(std::move(input_file), std::move(sticker_format), emojis, - std::move(mask_position), std::move(input_keywords)); + return make_object<td_api::newSticker>(std::move(input_file), std::move(sticker_format), emojis, + std::move(mask_position), std::move(input_keywords)); } -td::Result<td_api::object_ptr<td_api::inputSticker>> Client::get_input_sticker(const Query *query) const { +td::Result<td_api::object_ptr<td_api::newSticker>> Client::get_new_sticker(const Query *query) const { if (query->has_arg("sticker") || query->file("sticker") != nullptr) { auto sticker = query->arg("sticker"); LOG(INFO) << "Parsing JSON object: " << sticker; @@ -11555,17 +11597,17 @@ td::Result<td_api::object_ptr<td_api::inputSticker>> Client::get_input_sticker(c return td::Status::Error(400, "Can't parse sticker JSON object"); } - auto r_sticker = get_input_sticker(query, r_value.move_as_ok(), "auto"); + auto r_sticker = get_new_sticker(query, r_value.move_as_ok(), "auto"); if (r_sticker.is_error()) { return td::Status::Error(400, PSLICE() << "Can't parse sticker: " << r_sticker.error().message()); } return r_sticker.move_as_ok(); } - return get_legacy_input_sticker(query); + return get_legacy_new_sticker(query); } -td::Result<td_api::object_ptr<td_api::inputSticker>> Client::get_legacy_input_sticker(const Query *query) const { +td::Result<td_api::object_ptr<td_api::newSticker>> Client::get_legacy_new_sticker(const Query *query) const { auto emojis = query->arg("emojis"); auto sticker = get_input_file(query, "png_sticker"); object_ptr<td_api::StickerFormat> sticker_format; @@ -11593,11 +11635,11 @@ td::Result<td_api::object_ptr<td_api::inputSticker>> Client::get_legacy_input_st } } - return make_object<td_api::inputSticker>(std::move(sticker), std::move(sticker_format), emojis.str(), - std::move(mask_position), td::vector<td::string>()); + return make_object<td_api::newSticker>(std::move(sticker), std::move(sticker_format), emojis.str(), + std::move(mask_position), td::vector<td::string>()); } -td::Result<td::vector<td_api::object_ptr<td_api::inputSticker>>> Client::get_input_stickers(const Query *query) const { +td::Result<td::vector<td_api::object_ptr<td_api::newSticker>>> Client::get_new_stickers(const Query *query) const { if (query->has_arg("stickers")) { auto sticker_format_str = query->arg("sticker_format"); auto stickers = query->arg("stickers"); @@ -11607,32 +11649,16 @@ td::Result<td::vector<td_api::object_ptr<td_api::inputSticker>>> Client::get_inp LOG(INFO) << "Can't parse JSON object: " << r_value.error(); return td::Status::Error(400, "Can't parse stickers JSON object"); } - auto value = r_value.move_as_ok(); - - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of InputSticker"); - } - - constexpr std::size_t MAX_STICKER_COUNT = 50; - if (value.get_array().size() > MAX_STICKER_COUNT) { - return td::Status::Error(400, "Too many stickers specified"); - } - td::vector<object_ptr<td_api::inputSticker>> input_stickers; - for (auto &input_sticker : value.get_array()) { - auto r_input_sticker = get_input_sticker(query, std::move(input_sticker), sticker_format_str); - if (r_input_sticker.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse InputSticker: " << r_input_sticker.error().message()); - } - input_stickers.push_back(r_input_sticker.move_as_ok()); - } - return std::move(input_stickers); + return get_array_generic<td_api::newSticker>(r_value.move_as_ok(), "InputSticker", [&](td::JsonValue &&value) { + return get_new_sticker(query, std::move(value), sticker_format_str); + }); } - TRY_RESULT(input_sticker, get_legacy_input_sticker(query)); + TRY_RESULT(new_sticker, get_legacy_new_sticker(query)); - td::vector<object_ptr<td_api::inputSticker>> stickers; - stickers.push_back(std::move(input_sticker)); + td::vector<object_ptr<td_api::newSticker>> stickers; + stickers.push_back(std::move(new_sticker)); return std::move(stickers); } @@ -11741,20 +11767,7 @@ td::Result<td::vector<td_api::object_ptr<td_api::inputPassportElementError>>> Cl return td::Status::Error(400, "Can't parse errors JSON object"); } - auto value = r_value.move_as_ok(); - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of PassportElementError"); - } - - td::vector<object_ptr<td_api::inputPassportElementError>> errors; - for (auto &input_error : value.get_array()) { - auto r_error = get_passport_element_error(std::move(input_error)); - if (r_error.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse PassportElementError: " << r_error.error().message()); - } - errors.push_back(r_error.move_as_ok()); - } - return std::move(errors); + return get_array(r_value.move_as_ok(), "PassportElementError", get_passport_element_error); } td::JsonValue Client::get_input_entities(const Query *query, td::Slice field_name) { @@ -11961,7 +11974,360 @@ td::Result<td_api::object_ptr<td_api::inputMessageText>> Client::get_input_messa return make_object<td_api::inputMessageText>(std::move(formatted_text), std::move(link_preview_options), false); } -td::Result<td_api::object_ptr<td_api::inputRichMessage>> Client::get_input_rich_message(const Query *query) { +td::Result<td_api::object_ptr<td_api::pageBlockCaption>> Client::get_page_block_caption(td::JsonValue &&value) { + switch (value.type()) { + case td::JsonValue::Type::Null: + return nullptr; + case td::JsonValue::Type::Object: { + auto &object = value.get_object(); + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(credit, get_rich_text(object.extract_field("credit"))); + return make_object<td_api::pageBlockCaption>(std::move(text), std::move(credit)); + } + default: + return td::Status::Error(400, "RichBlockCaption must be an object"); + } +} + +td::Result<td_api::object_ptr<td_api::pageBlockTableCell>> Client::get_page_block_table_cell(td::JsonValue &&value) { + if (value.type() != td::JsonValue::Type::Object) { + return td::Status::Error(400, "RichBlockTableCell must be an object"); + } + + auto &object = value.get_object(); + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(is_header, object.get_optional_bool_field("is_header")); + TRY_RESULT(colspan, object.get_optional_int_field("colspan")); + TRY_RESULT(rowspan, object.get_optional_int_field("rowspan")); + + object_ptr<td_api::PageBlockHorizontalAlignment> align; + TRY_RESULT(align_str, object.get_optional_string_field("align")); + if (align_str == "left" || (align_str.empty() && !is_header)) { + align = make_object<td_api::pageBlockHorizontalAlignmentLeft>(); + } else if (align_str == "center" || (align_str.empty() && is_header)) { + align = make_object<td_api::pageBlockHorizontalAlignmentCenter>(); + } else if (align_str == "right") { + align = make_object<td_api::pageBlockHorizontalAlignmentRight>(); + } else { + return td::Status::Error(400, "Invalid horizontal alignment specified"); + } + + object_ptr<td_api::PageBlockVerticalAlignment> valign; + TRY_RESULT(valign_str, object.get_optional_string_field("valign")); + if (valign_str == "top") { + valign = make_object<td_api::pageBlockVerticalAlignmentTop>(); + } else if (valign_str == "middle" || valign_str.empty()) { + valign = make_object<td_api::pageBlockVerticalAlignmentMiddle>(); + } else if (valign_str == "bottom") { + valign = make_object<td_api::pageBlockVerticalAlignmentBottom>(); + } else { + return td::Status::Error(400, "Invalid vertical alignment specified"); + } + + return make_object<td_api::pageBlockTableCell>(std::move(text), is_header, colspan, rowspan, std::move(align), + std::move(valign)); +} + +td::Result<td_api::object_ptr<td_api::RichText>> Client::get_rich_text(td::JsonValue &&value) { + switch (value.type()) { + case td::JsonValue::Type::Null: + return nullptr; + case td::JsonValue::Type::String: + return make_object<td_api::richTextPlain>(value.get_string().str()); + case td::JsonValue::Type::Array: { + td::vector<object_ptr<td_api::RichText>> texts; + for (auto &input_text : value.get_array()) { + TRY_RESULT(text, get_rich_text(std::move(input_text))); + texts.push_back(std::move(text)); + } + return make_object<td_api::richTexts>(std::move(texts)); + } + case td::JsonValue::Type::Object: { + auto &object = value.get_object(); + TRY_RESULT(type, object.get_required_string_field("type")); + if (type == "bold") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextBold>(std::move(text)); + } + if (type == "italic") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextItalic>(std::move(text)); + } + if (type == "underline") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextUnderline>(std::move(text)); + } + if (type == "strikethrough") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextStrikethrough>(std::move(text)); + } + if (type == "spoiler") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextSpoiler>(std::move(text)); + } + if (type == "date_time") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(unix_time, object.get_required_int_field("unix_time")); + TRY_RESULT(format, object.get_optional_string_field("date_time_format")); + TRY_RESULT(formatting_type, get_date_time_formatting_type(format)); + return make_object<td_api::richTextDateTime>(std::move(text), unix_time, std::move(formatting_type)); + } + if (type == "mention") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextMention>(std::move(text), td::string()); + } + if (type == "hashtag") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextHashtag>(std::move(text), td::string()); + } + if (type == "cashtag") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextCashtag>(std::move(text), td::string()); + } + if (type == "bot_command") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextBotCommand>(std::move(text), td::string()); + } + if (type == "code") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextFixed>(std::move(text)); + } + if (type == "text_mention") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(user, object.extract_required_field("user", td::JsonValue::Type::Object)); + CHECK(user.type() == td::JsonValue::Type::Object); + const auto &user_object = user.get_object(); + TRY_RESULT(user_id, user_object.get_required_long_field("id")); + return make_object<td_api::richTextMentionName>(std::move(text), user_id); + } + if (type == "url") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(url, object.get_required_string_field("url")); + return make_object<td_api::richTextUrl>(std::move(text), url, false); + } + if (type == "email_address") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(email_address, object.get_required_string_field("email_address")); + return make_object<td_api::richTextEmailAddress>(std::move(text), email_address); + } + if (type == "bank_card_number") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextBankCardNumber>(std::move(text), td::string()); + } + if (type == "subscript") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextSubscript>(std::move(text)); + } + if (type == "superscript") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextSuperscript>(std::move(text)); + } + if (type == "marked") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::richTextMarked>(std::move(text)); + } + if (type == "phone_number") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(phone_number, object.get_required_string_field("phone_number")); + return make_object<td_api::richTextPhoneNumber>(std::move(text), phone_number); + } + if (type == "custom_emoji") { + TRY_RESULT(custom_emoji_id, object.get_required_long_field("custom_emoji_id")); + TRY_RESULT(alternative_text, object.get_required_string_field("alternative_text")); + return make_object<td_api::richTextCustomEmoji>(custom_emoji_id, alternative_text); + } + if (type == "mathematical_expression") { + TRY_RESULT(expression, object.get_required_string_field("expression")); + return make_object<td_api::richTextMathematicalExpression>(expression); + } + if (type == "reference") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(name, object.get_required_string_field("name")); + return make_object<td_api::richTextReference>(name, std::move(text)); + } + if (type == "reference_link") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(reference_name, object.get_required_string_field("reference_name")); + return make_object<td_api::richTextReferenceLink>(std::move(text), reference_name, td::string()); + } + if (type == "anchor") { + TRY_RESULT(name, object.get_required_string_field("name")); + return make_object<td_api::richTextAnchor>(name); + } + if (type == "anchor_link") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(anchor_name, object.get_required_string_field("anchor_name")); + return make_object<td_api::richTextAnchorLink>(std::move(text), anchor_name, td::string()); + } + return td::Status::Error(400, "Unsupported rich text type"); + } + default: + return td::Status::Error(400, "Invalid rich text specified"); + } +} + +td::Result<td_api::object_ptr<td_api::inputPageBlockListItem>> Client::get_input_page_block_list_item( + const Query *query, td::JsonValue &&value) const { + if (value.type() != td::JsonValue::Type::Object) { + return td::Status::Error(400, "Object expected as InputRichBlockListItem"); + } + auto &object = value.get_object(); + TRY_RESULT(blocks, get_input_page_blocks(query, object.extract_field("blocks"))); + TRY_RESULT(has_checkbox, object.get_optional_bool_field("has_checkbox")); + TRY_RESULT(is_checked, object.get_optional_bool_field("is_checked")); + TRY_RESULT(item_value, object.get_optional_int_field("value")); + TRY_RESULT(type, object.get_optional_string_field("type")); + return make_object<td_api::inputPageBlockListItem>(std::move(blocks), has_checkbox, is_checked, item_value, type); +} + +td::Result<td_api::object_ptr<td_api::InputPageBlock>> Client::get_input_page_block(const Query *query, + td::JsonValue &&value) const { + if (value.type() != td::JsonValue::Type::Object) { + return td::Status::Error(400, "Object expected as InputRichMessageBlock"); + } + auto &object = value.get_object(); + TRY_RESULT(type, object.get_required_string_field("type")); + if (type == "heading") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(size, object.get_required_int_field("size")); + return make_object<td_api::inputPageBlockSectionHeading>(std::move(text), size); + } + if (type == "paragraph") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::inputPageBlockParagraph>(std::move(text)); + } + if (type == "pre") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(language, object.get_optional_string_field("language")); + return make_object<td_api::inputPageBlockPreformatted>(std::move(text), language); + } + if (type == "footer") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::inputPageBlockFooter>(std::move(text)); + } + if (type == "thinking") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + return make_object<td_api::inputPageBlockThinking>(std::move(text)); + } + if (type == "divider") { + return make_object<td_api::inputPageBlockDivider>(); + } + if (type == "mathematical_expression") { + TRY_RESULT(expression, object.get_required_string_field("expression")); + return make_object<td_api::inputPageBlockMathematicalExpression>(expression); + } + if (type == "anchor") { + TRY_RESULT(name, object.get_required_string_field("name")); + return make_object<td_api::inputPageBlockAnchor>(name); + } + if (type == "list") { + TRY_RESULT(items, get_array_generic<td_api::inputPageBlockListItem>( + object.extract_field("items"), "InputRichBlockListItem", [&](td::JsonValue &&item) { + return get_input_page_block_list_item(query, std::move(item)); + })); + return make_object<td_api::inputPageBlockList>(std::move(items)); + } + if (type == "blockquote") { + TRY_RESULT(blocks, get_input_page_blocks(query, object.extract_field("blocks"))); + TRY_RESULT(credit, get_rich_text(object.extract_field("credit"))); + return make_object<td_api::inputPageBlockBlockQuote>(std::move(blocks), std::move(credit)); + } + if (type == "pullquote") { + TRY_RESULT(text, get_rich_text(object.extract_field("text"))); + TRY_RESULT(credit, get_rich_text(object.extract_field("credit"))); + return make_object<td_api::inputPageBlockPullQuote>(std::move(text), std::move(credit)); + } + if (type == "collage") { + TRY_RESULT(blocks, get_input_page_blocks(query, object.extract_field("blocks"))); + TRY_RESULT(caption, get_page_block_caption(object.extract_field("caption"))); + return make_object<td_api::inputPageBlockCollage>(std::move(blocks), std::move(caption)); + } + if (type == "slideshow") { + TRY_RESULT(blocks, get_input_page_blocks(query, object.extract_field("blocks"))); + TRY_RESULT(caption, get_page_block_caption(object.extract_field("caption"))); + return make_object<td_api::inputPageBlockSlideshow>(std::move(blocks), std::move(caption)); + } + if (type == "table") { + td::vector<td::vector<object_ptr<td_api::pageBlockTableCell>>> cells; + TRY_RESULT(cells_array, object.extract_required_field("cells", td::JsonValue::Type::Array)); + for (auto &row : cells_array.get_array()) { + TRY_RESULT(new_row, get_array(std::move(row), "PageBlockTableCell", get_page_block_table_cell)); + cells.push_back(std::move(new_row)); + } + TRY_RESULT(caption, get_rich_text(object.extract_field("caption"))); + TRY_RESULT(is_bordered, object.get_optional_bool_field("is_bordered")); + TRY_RESULT(is_striped, object.get_optional_bool_field("is_striped")); + return make_object<td_api::inputPageBlockTable>(std::move(caption), std::move(cells), is_bordered, is_striped); + } + if (type == "details") { + TRY_RESULT(summary, get_rich_text(object.extract_field("summary"))); + TRY_RESULT(blocks, get_input_page_blocks(query, object.extract_field("blocks"))); + TRY_RESULT(is_open, object.get_optional_bool_field("is_open")); + return make_object<td_api::inputPageBlockDetails>(std::move(summary), std::move(blocks), is_open); + } + if (type == "map") { + TRY_RESULT(location_field, object.extract_required_field("location", td::JsonValue::Type::Object)); + auto &location_object = location_field.get_object(); + TRY_RESULT(location, get_location(location_object)); + TRY_RESULT(zoom, object.get_optional_int_field("zoom")); + TRY_RESULT(width, object.get_optional_int_field("width")); + TRY_RESULT(height, object.get_optional_int_field("height")); + TRY_RESULT(caption, get_page_block_caption(object.extract_field("caption"))); + return make_object<td_api::inputPageBlockMap>(std::move(location), zoom, width, height, std::move(caption)); + } + if (type != "animation" && type != "audio" && type != "photo" && type != "video" && type != "voice_note") { + return td::Status::Error(400, PSLICE() << "type \"" << type << "\" is unsupported"); + } + TRY_RESULT(media_field, object.extract_required_field(type, td::JsonValue::Type::Object)); + auto &media_object = media_field.get_object(); + TRY_RESULT(media_type, media_object.get_required_string_field("type")); + if (media_type != type) { + return td::Status::Error(400, PSLICE() + << "Unexpected media type \"" << media_type << "\" for block \"" << type << "\""); + } + TRY_RESULT(input_message_content, get_input_media(query, media_object, type, nullptr, false, false, false, true)); + TRY_RESULT(caption, get_page_block_caption(object.extract_field("caption"))); + TRY_RESULT(has_spoiler, media_object.get_optional_bool_field("has_spoiler")); + if (type == "animation") { + CHECK(input_message_content->get_id() == td_api::inputMessageAnimation::ID); + return make_object<td_api::inputPageBlockAnimation>( + std::move(static_cast<td_api::inputMessageAnimation *>(input_message_content.get())->animation_), + std::move(caption), has_spoiler); + } + if (type == "audio") { + CHECK(input_message_content->get_id() == td_api::inputMessageAudio::ID); + return make_object<td_api::inputPageBlockAudio>( + std::move(static_cast<td_api::inputMessageAudio *>(input_message_content.get())->audio_), std::move(caption)); + } + if (type == "photo") { + CHECK(input_message_content->get_id() == td_api::inputMessagePhoto::ID); + return make_object<td_api::inputPageBlockPhoto>( + std::move(static_cast<td_api::inputMessagePhoto *>(input_message_content.get())->photo_), std::move(caption), + has_spoiler); + } + if (type == "video") { + CHECK(input_message_content->get_id() == td_api::inputMessageVideo::ID); + return make_object<td_api::inputPageBlockVideo>( + std::move(static_cast<td_api::inputMessageVideo *>(input_message_content.get())->video_), std::move(caption), + has_spoiler); + } + if (type == "voice_note") { + CHECK(input_message_content->get_id() == td_api::inputMessageVoiceNote::ID); + return make_object<td_api::inputPageBlockVoiceNote>( + std::move(static_cast<td_api::inputMessageVoiceNote *>(input_message_content.get())->voice_note_), + std::move(caption)); + } + UNREACHABLE(); +} + +td::Result<td::vector<td_api::object_ptr<td_api::InputPageBlock>>> Client::get_input_page_blocks( + const Query *query, td::JsonValue &&value) const { + return get_array_generic<td_api::InputPageBlock>(std::move(value), "InputRichBlock", [&](td::JsonValue &&block) { + return get_input_page_block(query, std::move(block)); + }); +} + +td::Result<td_api::object_ptr<td_api::inputRichMessage>> Client::get_input_rich_message(const Query *query) const { auto rich_message = query->arg("rich_message"); if (rich_message.empty()) { return td::Status::Error(400, "Rich message must be non-empty"); @@ -11974,10 +12340,11 @@ td::Result<td_api::object_ptr<td_api::inputRichMessage>> Client::get_input_rich_ return td::Status::Error(400, "Can't parse rich message JSON object"); } - return get_input_rich_message(r_value.move_as_ok()); + return get_input_rich_message(query, r_value.move_as_ok()); } -td::Result<td_api::object_ptr<td_api::inputRichMessage>> Client::get_input_rich_message(td::JsonValue &&value) { +td::Result<td_api::object_ptr<td_api::inputRichMessage>> Client::get_input_rich_message(const Query *query, + td::JsonValue &&value) const { if (value.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "Object expected as rich message"); } @@ -11985,12 +12352,17 @@ td::Result<td_api::object_ptr<td_api::inputRichMessage>> Client::get_input_rich_ TRY_RESULT(is_rtl, object.get_optional_bool_field("is_rtl")); TRY_RESULT(skip_entity_detection, object.get_optional_bool_field("skip_entity_detection")); auto result = make_object<td_api::inputRichMessage>(nullptr, is_rtl, !skip_entity_detection); - if (object.has_field("markdown")) { + if (object.has_field("blocks")) { + TRY_RESULT(blocks, get_input_page_blocks(query, object.extract_field("blocks"))); + result->source_ = make_object<td_api::richMessageSourceBlocks>(std::move(blocks)); + } else if (object.has_field("markdown")) { TRY_RESULT(text, object.get_required_string_field("markdown")); - result->source_ = make_object<td_api::richMessageSourceMarkdown>(text); + TRY_RESULT(media, get_input_rich_message_medias(query, object.extract_field("media"))); + result->source_ = make_object<td_api::richMessageSourceMarkdown>(text, std::move(media)); } else if (object.has_field("html")) { TRY_RESULT(text, object.get_required_string_field("html")); - result->source_ = make_object<td_api::richMessageSourceHtml>(text); + TRY_RESULT(media, get_input_rich_message_medias(query, object.extract_field("media"))); + result->source_ = make_object<td_api::richMessageSourceHtml>(text, std::move(media)); } else { return td::Status::Error(400, "Rich message must be non-empty"); } @@ -12184,8 +12556,73 @@ td::Result<td_api::object_ptr<td_api::chatPermissions>> Client::get_chat_permiss can_edit_tag, can_change_info, can_invite_users, can_pin_messages, can_manage_topics); } +td::Result<td_api::object_ptr<td_api::inputAnimation>> Client::get_input_animation( + const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file, + object_ptr<td_api::inputThumbnail> &&input_thumbnail) { + 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")); + width = td::clamp(width, 0, MAX_LENGTH); + height = td::clamp(height, 0, MAX_LENGTH); + duration = td::clamp(duration, 0, MAX_DURATION); + return make_object<td_api::inputAnimation>(std::move(input_file), std::move(input_thumbnail), td::vector<int32>(), + duration, width, height); +} + +td::Result<td_api::object_ptr<td_api::inputAudio>> Client::get_input_audio( + const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file, + object_ptr<td_api::inputThumbnail> &&input_thumbnail) { + TRY_RESULT(duration, object.get_optional_int_field("duration")); + TRY_RESULT(title, object.get_optional_string_field("title")); + TRY_RESULT(performer, object.get_optional_string_field("performer")); + duration = td::clamp(duration, 0, MAX_DURATION); + return make_object<td_api::inputAudio>(std::move(input_file), std::move(input_thumbnail), duration, title, performer); +} + +td::Result<td_api::object_ptr<td_api::inputPhoto>> Client::get_input_photo(const Query *query, + const td::JsonObject &object, + object_ptr<td_api::InputFile> &&input_file, + bool is_live) const { + if (is_live) { + TRY_RESULT(photo, object.get_optional_string_field("photo")); + auto input_photo = get_input_file(query, td::Slice(), photo, false); + if (input_photo == nullptr) { + return td::Status::Error("photo not found"); + } + return make_object<td_api::inputPhoto>(std::move(input_photo), nullptr, std::move(input_file), td::vector<int32>(), + 0, 0); + } + return make_object<td_api::inputPhoto>(std::move(input_file), nullptr, nullptr, td::vector<int32>(), 0, 0); +} + +td::Result<td_api::object_ptr<td_api::inputVideo>> Client::get_input_video( + const Query *query, const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file, + object_ptr<td_api::inputThumbnail> &&input_thumbnail) const { + 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")); + TRY_RESULT(cover, object.get_optional_string_field("cover")); + TRY_RESULT(start_timestamp, object.get_optional_int_field("start_timestamp")); + TRY_RESULT(supports_streaming, object.get_optional_bool_field("supports_streaming")); + auto input_cover = get_input_file(query, td::Slice(), cover, false); + width = td::clamp(width, 0, MAX_LENGTH); + height = td::clamp(height, 0, MAX_LENGTH); + duration = td::clamp(duration, 0, MAX_DURATION); + start_timestamp = td::clamp(start_timestamp, 0, MAX_DURATION); + return make_object<td_api::inputVideo>(std::move(input_file), std::move(input_thumbnail), std::move(input_cover), + start_timestamp, td::vector<int32>(), duration, width, height, + supports_streaming); +} + +td::Result<td_api::object_ptr<td_api::inputVoiceNote>> Client::get_input_voice_note( + const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file) { + TRY_RESULT(duration, object.get_optional_int_field("duration")); + duration = td::clamp(duration, 0, MAX_DURATION); + return make_object<td_api::inputVoiceNote>(std::move(input_file), duration, td::string()); +} + td::Result<td_api::object_ptr<td_api::inputChecklistTask>> Client::get_input_checklist_task( - td::JsonValue &&input_task) const { + td::JsonValue &&input_task) { if (input_task.type() != td::JsonValue::Type::Object) { return td::Status::Error("expected an Object"); } @@ -12200,23 +12637,6 @@ td::Result<td_api::object_ptr<td_api::inputChecklistTask>> Client::get_input_che return make_object<td_api::inputChecklistTask>(id, std::move(text)); } -td::Result<td::vector<td_api::object_ptr<td_api::inputChecklistTask>>> Client::get_input_checklist_tasks( - td::JsonValue &&value) const { - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of InputChecklistTask"); - } - - td::vector<object_ptr<td_api::inputChecklistTask>> tasks; - for (auto &input_task : value.get_array()) { - auto r_task = get_input_checklist_task(std::move(input_task)); - if (r_task.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse InputChecklistTask: " << r_task.error().message()); - } - tasks.push_back(r_task.move_as_ok()); - } - return std::move(tasks); -} - td::Result<td_api::object_ptr<td_api::inputChecklist>> Client::get_input_checklist( const Query *query, td::JsonValue &&input_checklist) const { if (input_checklist.type() != td::JsonValue::Type::Object) { @@ -12231,7 +12651,7 @@ td::Result<td_api::object_ptr<td_api::inputChecklist>> Client::get_input_checkli TRY_RESULT(others_can_add_tasks, object.get_optional_bool_field("others_can_add_tasks")); TRY_RESULT(others_can_mark_tasks_as_done, object.get_optional_bool_field("others_can_mark_tasks_as_done")); TRY_RESULT(input_tasks, object.extract_required_field("tasks", td::JsonValue::Type::Array)); - TRY_RESULT(tasks, get_input_checklist_tasks(std::move(input_tasks))); + TRY_RESULT(tasks, get_array(std::move(input_tasks), "InputChecklistTask", get_input_checklist_task)); return make_object<td_api::inputChecklist>(std::move(title), std::move(tasks), others_can_add_tasks, others_can_mark_tasks_as_done); @@ -12272,13 +12692,14 @@ td::Result<td_api::object_ptr<td_api::InputMessageContent>> Client::get_input_me TRY_RESULT(has_spoiler, object.get_optional_bool_field("has_spoiler")); TRY_RESULT(type, object.get_required_string_field("type")); - return get_input_media(query, object, type, std::move(caption), show_caption_above_media, has_spoiler, for_album); + return get_input_media(query, object, type, std::move(caption), show_caption_above_media, has_spoiler, for_album, + false); } td::Result<td_api::object_ptr<td_api::InputMessageContent>> Client::get_input_media( const Query *query, const td::JsonObject &object, const td::string &type, - object_ptr<td_api::formattedText> &&caption, bool show_caption_above_media, bool has_spoiler, - bool for_album) const { + object_ptr<td_api::formattedText> &&caption, bool show_caption_above_media, bool has_spoiler, bool for_album, + bool for_rich_message) const { TRY_RESULT(media, object.get_optional_string_field("media")); auto input_file = get_input_file(query, td::Slice(), media, false); @@ -12287,72 +12708,45 @@ td::Result<td_api::object_ptr<td_api::InputMessageContent>> Client::get_input_me } TRY_RESULT(input_thumbnail, get_input_thumbnail(query, object, true)); - if (type == "photo") { - return make_object<td_api::inputMessagePhoto>( - make_object<td_api::inputPhoto>(std::move(input_file), nullptr, nullptr, td::vector<int32>(), 0, 0), - std::move(caption), show_caption_above_media, nullptr, has_spoiler); - } - if (type == "live_photo") { - TRY_RESULT(photo, object.get_optional_string_field("photo")); - auto input_photo = get_input_file(query, td::Slice(), photo, false); - if (input_photo == nullptr) { - return td::Status::Error("photo not found"); - } - return make_object<td_api::inputMessagePhoto>( - make_object<td_api::inputPhoto>(std::move(input_photo), nullptr, std::move(input_file), td::vector<int32>(), 0, - 0), - std::move(caption), show_caption_above_media, nullptr, has_spoiler); + if (type == "photo" || type == "live_photo") { + TRY_RESULT(input_photo, get_input_photo(query, object, std::move(input_file), type == "live_photo")); + return make_object<td_api::inputMessagePhoto>(std::move(input_photo), std::move(caption), show_caption_above_media, + nullptr, has_spoiler); } if (type == "video") { - 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")); - TRY_RESULT(cover, object.get_optional_string_field("cover")); - TRY_RESULT(start_timestamp, object.get_optional_int_field("start_timestamp")); - TRY_RESULT(supports_streaming, object.get_optional_bool_field("supports_streaming")); - auto input_cover = get_input_file(query, td::Slice(), cover, false); - width = td::clamp(width, 0, MAX_LENGTH); - height = td::clamp(height, 0, MAX_LENGTH); - duration = td::clamp(duration, 0, MAX_DURATION); - start_timestamp = td::clamp(start_timestamp, 0, MAX_DURATION); - - return make_object<td_api::inputMessageVideo>( - make_object<td_api::inputVideo>(std::move(input_file), std::move(input_thumbnail), std::move(input_cover), - start_timestamp, td::vector<int32>(), duration, width, height, - supports_streaming), - std::move(caption), show_caption_above_media, nullptr, has_spoiler); + TRY_RESULT(input_video, get_input_video(query, object, std::move(input_file), std::move(input_thumbnail))); + return make_object<td_api::inputMessageVideo>(std::move(input_video), 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")); - width = td::clamp(width, 0, MAX_LENGTH); - height = td::clamp(height, 0, MAX_LENGTH); - duration = td::clamp(duration, 0, MAX_DURATION); - return make_object<td_api::inputMessageAnimation>( - make_object<td_api::inputAnimation>(std::move(input_file), std::move(input_thumbnail), td::vector<int32>(), - duration, width, height), - std::move(caption), show_caption_above_media, has_spoiler); + TRY_RESULT(input_animation, get_input_animation(object, std::move(input_file), std::move(input_thumbnail))); + return make_object<td_api::inputMessageAnimation>(std::move(input_animation), std::move(caption), + show_caption_above_media, has_spoiler); } if (type == "audio") { - TRY_RESULT(duration, object.get_optional_int_field("duration")); - TRY_RESULT(title, object.get_optional_string_field("title")); - TRY_RESULT(performer, object.get_optional_string_field("performer")); - duration = td::clamp(duration, 0, MAX_DURATION); - return make_object<td_api::inputMessageAudio>( - make_object<td_api::inputAudio>(std::move(input_file), std::move(input_thumbnail), duration, title, performer), - std::move(caption)); + TRY_RESULT(input_audio, get_input_audio(object, std::move(input_file), std::move(input_thumbnail))); + return make_object<td_api::inputMessageAudio>(std::move(input_audio), std::move(caption)); } if (type == "document") { + if (for_rich_message) { + return td::Status::Error(PSLICE() << "type \"" << type << "\" is not allowed"); + } TRY_RESULT(disable_content_type_detection, object.get_optional_bool_field("disable_content_type_detection")); return make_object<td_api::inputMessageDocument>( make_object<td_api::inputDocument>(std::move(input_file), std::move(input_thumbnail), disable_content_type_detection || for_album), std::move(caption)); } + if (type == "voice_note") { + if (!for_rich_message) { + return td::Status::Error(PSLICE() << "type \"" << type << "\" is not allowed"); + } + TRY_RESULT(input_voice_note, get_input_voice_note(object, std::move(input_file))); + return make_object<td_api::inputMessageVoiceNote>(std::move(input_voice_note), std::move(caption), nullptr); + } return td::Status::Error(PSLICE() << "type \"" << type << "\" is unsupported"); } @@ -12411,60 +12805,29 @@ td::Result<td_api::object_ptr<td_api::InputPollMedia>> Client::get_input_poll_me } TRY_RESULT(input_thumbnail, get_input_thumbnail(query, object, true)); - if (type == "photo") { - return make_object<td_api::inputPollMediaPhoto>( - make_object<td_api::inputPhoto>(std::move(input_file), nullptr, nullptr, td::vector<int32>(), 0, 0)); - } - if (type == "live_photo") { - TRY_RESULT(photo, object.get_optional_string_field("photo")); - auto input_photo = get_input_file(query, td::Slice(), photo, false); - if (input_photo == nullptr) { - return td::Status::Error("photo not found"); - } - return make_object<td_api::inputPollMediaPhoto>(make_object<td_api::inputPhoto>( - std::move(input_photo), nullptr, std::move(input_file), td::vector<int32>(), 0, 0)); + if (type == "photo" || type == "live_photo") { + TRY_RESULT(input_photo, get_input_photo(query, object, std::move(input_file), type == "live_photo")); + return make_object<td_api::inputPollMediaPhoto>(std::move(input_photo)); } if (type == "video") { - 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")); - TRY_RESULT(cover, object.get_optional_string_field("cover")); - TRY_RESULT(start_timestamp, object.get_optional_int_field("start_timestamp")); - TRY_RESULT(supports_streaming, object.get_optional_bool_field("supports_streaming")); - auto input_cover = get_input_file(query, td::Slice(), cover, false); - width = td::clamp(width, 0, MAX_LENGTH); - height = td::clamp(height, 0, MAX_LENGTH); - duration = td::clamp(duration, 0, MAX_DURATION); - start_timestamp = td::clamp(start_timestamp, 0, MAX_DURATION); - - return make_object<td_api::inputPollMediaVideo>(make_object<td_api::inputVideo>( - std::move(input_file), std::move(input_thumbnail), std::move(input_cover), start_timestamp, td::vector<int32>(), - duration, width, height, supports_streaming)); + TRY_RESULT(input_video, get_input_video(query, object, std::move(input_file), std::move(input_thumbnail))); + return make_object<td_api::inputPollMediaVideo>(std::move(input_video)); } if (type == "animation") { - 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")); - width = td::clamp(width, 0, MAX_LENGTH); - height = td::clamp(height, 0, MAX_LENGTH); - duration = td::clamp(duration, 0, MAX_DURATION); - return make_object<td_api::inputPollMediaAnimation>(make_object<td_api::inputAnimation>( - std::move(input_file), std::move(input_thumbnail), td::vector<int32>(), duration, width, height)); + TRY_RESULT(input_animation, get_input_animation(object, std::move(input_file), std::move(input_thumbnail))); + return make_object<td_api::inputPollMediaAnimation>(std::move(input_animation)); } if (for_option ? type != "sticker" : type != "audio" && type != "document") { return td::Status::Error(PSLICE() << "type \"" << type << "\" is unsupported"); } if (type == "sticker") { - return make_object<td_api::inputPollMediaSticker>(std::move(input_file), std::move(input_thumbnail), 0, 0); + return make_object<td_api::inputPollMediaSticker>( + make_object<td_api::inputSticker>(std::move(input_file), std::move(input_thumbnail), 0, 0)); } if (type == "audio") { - TRY_RESULT(duration, object.get_optional_int_field("duration")); - TRY_RESULT(title, object.get_optional_string_field("title")); - TRY_RESULT(performer, object.get_optional_string_field("performer")); - duration = td::clamp(duration, 0, MAX_DURATION); - return make_object<td_api::inputPollMediaAudio>( - make_object<td_api::inputAudio>(std::move(input_file), std::move(input_thumbnail), duration, title, performer)); + TRY_RESULT(input_audio, get_input_audio(object, std::move(input_file), std::move(input_thumbnail))); + return make_object<td_api::inputPollMediaAudio>(std::move(input_audio)); } if (type == "document") { TRY_RESULT(disable_content_type_detection, object.get_optional_bool_field("disable_content_type_detection")); @@ -12496,6 +12859,28 @@ td::Result<td_api::object_ptr<td_api::InputPollMedia>> Client::get_input_poll_me return r_input_message_content.move_as_ok(); } +td::Result<td_api::object_ptr<td_api::inputRichMessageMedia>> Client::get_input_rich_message_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(id, object.get_required_string_field("id")); + TRY_RESULT(media, object.extract_required_field("media", td::JsonValue::Type::Object)); + auto &media_object = media.get_object(); + TRY_RESULT(type, media_object.get_required_string_field("type")); + TRY_RESULT(input_message_content, get_input_media(query, media_object, type, nullptr, false, false, false, true)); + return make_object<td_api::inputRichMessageMedia>(id, std::move(input_message_content)); +} + +td::Result<td::vector<td_api::object_ptr<td_api::inputRichMessageMedia>>> Client::get_input_rich_message_medias( + const Query *query, td::JsonValue &&value) const { + return get_array_generic<td_api::inputRichMessageMedia>( + std::move(value), "InputRichMessageMedia", + [&](td::JsonValue &&input_media) { return get_input_rich_message_media(query, std::move(input_media)); }); +} + td::Result<td::vector<td_api::object_ptr<td_api::InputMessageContent>>> Client::get_input_message_contents( const Query *query, td::Slice field_name) const { TRY_RESULT(media, get_required_string_arg(query, field_name)); @@ -12507,25 +12892,9 @@ td::Result<td::vector<td_api::object_ptr<td_api::InputMessageContent>>> Client:: return td::Status::Error(400, "Can't parse media JSON object"); } - return get_input_message_contents(query, r_value.move_as_ok()); -} - -td::Result<td::vector<td_api::object_ptr<td_api::InputMessageContent>>> Client::get_input_message_contents( - const Query *query, td::JsonValue &&value) const { - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of InputMedia"); - } - - td::vector<object_ptr<td_api::InputMessageContent>> contents; - for (auto &input_media : value.get_array()) { - auto r_input_message_content = get_input_media(query, std::move(input_media), true); - if (r_input_message_content.is_error()) { - return td::Status::Error(400, PSLICE() - << "Can't parse InputMedia: " << r_input_message_content.error().message()); - } - contents.push_back(r_input_message_content.move_as_ok()); - } - return std::move(contents); + return get_array_generic<td_api::InputMessageContent>(r_value.move_as_ok(), "InputMedia", [&](td::JsonValue &&value) { + return get_input_media(query, std::move(value), true); + }); } td::Result<td_api::object_ptr<td_api::inputPaidMedia>> Client::get_input_paid_media(const Query *query, @@ -12605,24 +12974,9 @@ td::Result<td::vector<td_api::object_ptr<td_api::inputPaidMedia>>> Client::get_p return td::Status::Error(400, "Can't parse paid media JSON object"); } - return get_paid_media(query, r_value.move_as_ok()); -} - -td::Result<td::vector<td_api::object_ptr<td_api::inputPaidMedia>>> 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<object_ptr<td_api::inputPaidMedia>> 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); + return get_array_generic<td_api::inputPaidMedia>( + r_value.move_as_ok(), "InputPaidMedia", + [&](td::JsonValue &&input_media) { return get_input_paid_media(query, std::move(input_media)); }); } td::Result<td_api::object_ptr<td_api::inputMessageInvoice>> Client::get_input_message_invoice( @@ -12644,7 +12998,7 @@ td::Result<td_api::object_ptr<td_api::inputMessageInvoice>> Client::get_input_me return td::Status::Error(400, "Can't parse prices JSON object"); } - TRY_RESULT(prices, get_labeled_price_parts(r_labeled_price_parts_value.ok_ref())); + TRY_RESULT(prices, get_labeled_price_parts(r_labeled_price_parts_value.move_as_ok())); int64 max_tip_amount = 0; td::vector<int64> suggested_tip_amounts; @@ -12665,7 +13019,7 @@ td::Result<td_api::object_ptr<td_api::inputMessageInvoice>> Client::get_input_me return td::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())); + TRY_RESULT_ASSIGN(suggested_tip_amounts, get_suggested_tip_amounts(r_suggested_tip_amounts_value.move_as_ok())); } } @@ -12713,34 +13067,29 @@ td::Result<td::vector<td_api::object_ptr<td_api::inputPollOption>>> Client::get_ return td::Status::Error(400, "Can't parse options JSON object"); } - auto value = r_value.move_as_ok(); - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of String as options"); - } - - td::vector<object_ptr<td_api::inputPollOption>> options; - for (auto &input_option : value.get_array()) { - if (input_option.type() != td::JsonValue::Type::String) { - if (input_option.type() == td::JsonValue::Type::Object) { - auto &object = input_option.get_object(); - TRY_RESULT(text, object.get_required_string_field("text")); - TRY_RESULT(parse_mode, object.get_optional_string_field("text_parse_mode")); - TRY_RESULT(option_text, - get_formatted_text(std::move(text), std::move(parse_mode), object.extract_field("text_entities"))); - auto r_media = get_input_poll_media(query, object.extract_field("media"), true); - if (r_media.is_error()) { - return td::Status::Error(400, PSLICE() << "Failed to parse poll option media: " << r_media.error().message()); - } - options.push_back(make_object<td_api::inputPollOption>(std::move(option_text), r_media.move_as_ok())); - continue; - } + return get_array_generic<td_api::inputPollOption>( + r_value.move_as_ok(), "InputPollOption", + [&](td::JsonValue &&input_option) -> td::Result<object_ptr<td_api::inputPollOption>> { + if (input_option.type() != td::JsonValue::Type::String) { + if (input_option.type() == td::JsonValue::Type::Object) { + auto &object = input_option.get_object(); + TRY_RESULT(text, object.get_required_string_field("text")); + TRY_RESULT(parse_mode, object.get_optional_string_field("text_parse_mode")); + TRY_RESULT(option_text, get_formatted_text(std::move(text), std::move(parse_mode), + object.extract_field("text_entities"))); + auto r_media = get_input_poll_media(query, object.extract_field("media"), true); + if (r_media.is_error()) { + return td::Status::Error(400, PSLICE() + << "Failed to parse poll option media: " << r_media.error().message()); + } + return make_object<td_api::inputPollOption>(std::move(option_text), r_media.move_as_ok()); + } - return td::Status::Error(400, "Expected an option to be of type String"); - } - options.push_back(make_object<td_api::inputPollOption>( - make_object<td_api::formattedText>(input_option.get_string().str(), td::Auto()), nullptr)); - } - return std::move(options); + return td::Status::Error(400, "Expected InputPollOption to be an Object"); + } + return make_object<td_api::inputPollOption>( + make_object<td_api::formattedText>(input_option.get_string().str(), td::Auto()), nullptr); + }); } td::Result<td_api::object_ptr<td_api::ReactionType>> Client::get_reaction_type(td::JsonValue &&value) { @@ -12774,20 +13123,7 @@ td::Result<td::vector<td_api::object_ptr<td_api::ReactionType>>> Client::get_rea return td::Status::Error(400, "Can't parse reaction types JSON object"); } - auto value = r_value.move_as_ok(); - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of ReactionType"); - } - - td::vector<object_ptr<td_api::ReactionType>> reaction_types; - for (auto &type : value.get_array()) { - auto r_reaction_type = get_reaction_type(std::move(type)); - if (r_reaction_type.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse ReactionType: " << r_reaction_type.error().message()); - } - reaction_types.push_back(r_reaction_type.move_as_ok()); - } - return std::move(reaction_types); + return get_array(r_value.move_as_ok(), "ReactionType", get_reaction_type); } td::Result<td_api::object_ptr<td_api::InputStoryAreaType>> Client::get_input_story_area_type(td::JsonValue &&value) { @@ -12876,19 +13212,7 @@ td::Result<td_api::object_ptr<td_api::inputStoryAreas>> Client::get_input_story_ return td::Status::Error(400, "Can't parse story areas JSON object"); } - auto value = r_value.move_as_ok(); - if (value.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Expected an Array of InputStoryArea"); - } - - td::vector<object_ptr<td_api::inputStoryArea>> input_story_areas; - for (auto &area : value.get_array()) { - auto r_input_story_area = get_input_story_area(std::move(area)); - if (r_input_story_area.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse InputStoryArea: " << r_input_story_area.error().message()); - } - input_story_areas.push_back(r_input_story_area.move_as_ok()); - } + TRY_RESULT(input_story_areas, get_array(r_value.move_as_ok(), "InputStoryArea", get_input_story_area)); return make_object<td_api::inputStoryAreas>(std::move(input_story_areas)); } @@ -13674,7 +13998,8 @@ td::Status Client::process_send_sticker_query(PromisedQueryPtr &query) { return td::Status::Error(400, "There is no sticker in the request"); } auto emoji = query->arg("emoji"); - do_send_message(make_object<td_api::inputMessageSticker>(std::move(sticker), nullptr, 0, 0, emoji.str()), + do_send_message(make_object<td_api::inputMessageSticker>( + make_object<td_api::inputSticker>(std::move(sticker), nullptr, 0, 0), emoji.str()), std::move(query)); return td::Status::OK(); } @@ -13711,9 +14036,10 @@ td::Status Client::process_send_video_note_query(PromisedQueryPtr &query) { auto thumbnail = get_input_thumbnail(query.get()); int32 duration = get_integer_arg(query.get(), "duration", 0, 0, MAX_DURATION); int32 length = get_integer_arg(query.get(), "length", 0, 0, MAX_LENGTH); - do_send_message(make_object<td_api::inputMessageVideoNote>(std::move(video_note), std::move(thumbnail), duration, - length, nullptr), - std::move(query)); + do_send_message( + make_object<td_api::inputMessageVideoNote>( + make_object<td_api::inputVideoNote>(std::move(video_note), std::move(thumbnail), duration, length), nullptr), + std::move(query)); return td::Status::OK(); } @@ -13725,7 +14051,8 @@ td::Status Client::process_send_voice_query(PromisedQueryPtr &query) { int32 duration = get_integer_arg(query.get(), "duration", 0, 0, MAX_DURATION); TRY_RESULT(caption, get_caption(query.get())); do_send_message( - make_object<td_api::inputMessageVoiceNote>(std::move(voice_note), duration, "", std::move(caption), nullptr), + make_object<td_api::inputMessageVoiceNote>( + make_object<td_api::inputVoiceNote>(std::move(voice_note), duration, ""), std::move(caption), nullptr), std::move(query)); return td::Status::OK(); } @@ -14092,12 +14419,12 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &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, int64 chat_id, PromisedQueryPtr query) mutable { - send_request( - make_object<td_api::sendBusinessMessageAlbum>( - business_connection->id_, chat_id, get_input_message_reply_to(std::move(reply_parameters)), - disable_notification, protect_content, effect_id, std::move(input_message_contents)), - td::make_unique<TdOnSendBusinessMessageAlbumCallback>(this, business_connection->id_, - std::move(query))); + send_request(make_object<td_api::sendBusinessMessageAlbum>( + business_connection->id_, chat_id, + get_input_message_reply_to_unchecked(std::move(reply_parameters)), + disable_notification, protect_content, effect_id, std::move(input_message_contents)), + td::make_unique<TdOnSendBusinessMessageAlbumCallback>(this, business_connection->id_, + std::move(query))); }); } @@ -14496,18 +14823,28 @@ td::Status Client::process_edit_message_reply_markup_query(PromisedQueryPtr &que return td::Status::OK(); } -td::Status Client::process_delete_message_query(PromisedQueryPtr &query) { - auto chat_id = query->arg("chat_id"); - auto message_id = get_message_id(query.get()); +td::Status Client::process_edit_ephemeral_message_text_query(PromisedQueryPtr &query) { + TRY_RESULT(input_message_text, get_input_message_text(query.get())); + return do_edit_ephemeral_message(std::move(input_message_text), query); +} - if (chat_id.empty()) { - return td::Status::Error(400, "Chat identifier is not specified"); - } +td::Status Client::process_edit_ephemeral_message_media_query(PromisedQueryPtr &query) { + TRY_RESULT(input_media, get_input_media(query.get(), "media")); + return do_edit_ephemeral_message(std::move(input_media), query); +} - if (message_id == 0) { - return td::Status::Error(400, "Message identifier is not specified"); - } +td::Status Client::process_edit_ephemeral_message_caption_query(PromisedQueryPtr &query) { + TRY_RESULT(caption, get_caption(query.get())); + return do_edit_ephemeral_message(make_object<td_api::inputMessageText>(std::move(caption), nullptr, false), query); +} +td::Status Client::process_edit_ephemeral_message_reply_markup_query(PromisedQueryPtr &query) { + return do_edit_ephemeral_message(nullptr, query); +} + +td::Status Client::process_delete_message_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + auto message_id = get_message_id(query.get()); check_message(chat_id, message_id, false, AccessRights::Write, "message to delete", std::move(query), [this](int64 chat_id, int64 message_id, PromisedQueryPtr query) { send_request(make_object<td_api::deleteMessages>(chat_id, td::vector<int64>{message_id}, true), @@ -14519,9 +14856,6 @@ td::Status Client::process_delete_message_query(PromisedQueryPtr &query) { td::Status Client::process_delete_messages_query(PromisedQueryPtr &query) { auto chat_id = query->arg("chat_id"); TRY_RESULT(message_ids, get_message_ids(query.get(), 100)); - if (message_ids.empty()) { - return td::Status::Error(400, "Message identifiers are not specified"); - } check_messages(chat_id, std::move(message_ids), true, AccessRights::Write, "message to delete", std::move(query), [this](int64 chat_id, td::vector<int64> message_ids, PromisedQueryPtr query) { send_request(make_object<td_api::deleteMessages>(chat_id, std::move(message_ids), true), @@ -14530,6 +14864,19 @@ td::Status Client::process_delete_messages_query(PromisedQueryPtr &query) { return td::Status::OK(); } +td::Status Client::process_delete_ephemeral_message_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + TRY_RESULT(receiver_user_id, get_user_id(query.get(), "receiver_user_id")); + auto ephemeral_message_id = get_integer_arg(query.get(), "ephemeral_message_id", 0); + check_chat(chat_id, AccessRights::Read, std::move(query), + [this, receiver_user_id, ephemeral_message_id](int64 chat_id, PromisedQueryPtr query) { + send_request( + make_object<td_api::deleteEphemeralMessage>(chat_id, receiver_user_id, ephemeral_message_id), + td::make_unique<TdOnOkQueryCallback>(std::move(query))); + }); + return td::Status::OK(); +} + td::Status Client::process_delete_message_reaction_query(PromisedQueryPtr &query) { auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); @@ -15040,7 +15387,7 @@ td::Status Client::process_save_prepared_keyboard_button_query(PromisedQueryPtr return td::Status::Error(400, "Can't parse keyboard button JSON object"); } auto value = r_value.move_as_ok(); - auto r_button = get_keyboard_button(value); + auto r_button = get_keyboard_button(std::move(value)); if (r_button.is_error()) { return td::Status::Error(400, PSLICE() << "Can't parse keyboard button: " << r_button.error().message()); } @@ -16249,7 +16596,7 @@ td::Status Client::process_create_new_sticker_set_query(PromisedQueryPtr &query) auto name = query->arg("name"); auto title = query->arg("title"); auto needs_repainting = to_bool(query->arg("needs_repainting")); - TRY_RESULT(stickers, get_input_stickers(query.get())); + TRY_RESULT(stickers, get_new_stickers(query.get())); TRY_RESULT(sticker_type, get_sticker_type(query->arg("sticker_type"))); if (to_bool(query->arg("contains_masks"))) { @@ -16270,7 +16617,7 @@ td::Status Client::process_create_new_sticker_set_query(PromisedQueryPtr &query) td::Status Client::process_add_sticker_to_set_query(PromisedQueryPtr &query) { TRY_RESULT(user_id, get_user_id(query.get())); auto name = query->arg("name"); - TRY_RESULT(sticker, get_input_sticker(query.get())); + TRY_RESULT(sticker, get_new_sticker(query.get())); check_user(user_id, std::move(query), [this, user_id, name, sticker = std::move(sticker)](PromisedQueryPtr query) mutable { @@ -16284,7 +16631,7 @@ td::Status Client::process_replace_sticker_in_set_query(PromisedQueryPtr &query) TRY_RESULT(user_id, get_user_id(query.get())); auto name = query->arg("name"); TRY_RESULT(input_file, get_sticker_input_file(query.get(), "old_sticker")); - TRY_RESULT(sticker, get_input_sticker(query.get())); + TRY_RESULT(sticker, get_new_sticker(query.get())); check_user(user_id, std::move(query), [this, user_id, name, input_file = std::move(input_file), @@ -16853,6 +17200,15 @@ void Client::do_send_message(object_ptr<td_api::InputMessageContent> input_messa auto chat_id = query->arg("chat_id"); auto forum_topic_id = get_forum_topic_id(query.get(), "message_thread_id"); auto business_connection_id = query->arg("business_connection_id"); + int64 receiver_user_id = 0; + if (query->has_arg("receiver_user_id")) { + auto r_receiver_user_id = get_user_id(query.get(), "receiver_user_id"); + if (r_receiver_user_id.is_error()) { + return fail_query_with_error(std::move(query), 400, r_receiver_user_id.error().message()); + } + receiver_user_id = r_receiver_user_id.move_as_ok(); + } + auto callback_query_id = td::to_integer<int64>(query->arg("callback_query_id")); auto r_reply_parameters = get_reply_parameters(query.get()); if (r_reply_parameters.is_error()) { return fail_query_with_error(std::move(query), 400, r_reply_parameters.error().message()); @@ -16878,26 +17234,27 @@ void Client::do_send_message(object_ptr<td_api::InputMessageContent> input_messa resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), [this, chat_id_str = chat_id.str(), forum_topic_id, direct_messages_topic_id, - business_connection_id = business_connection_id.str(), reply_parameters = std::move(reply_parameters), - disable_notification, protect_content, allow_paid_broadcast, effect_id, send_options = std::move(send_options), - input_message_content = std::move(input_message_content)](object_ptr<td_api::ReplyMarkup> reply_markup, - PromisedQueryPtr query) mutable { + business_connection_id = business_connection_id.str(), receiver_user_id, callback_query_id, + reply_parameters = std::move(reply_parameters), disable_notification, protect_content, allow_paid_broadcast, + effect_id, send_options = std::move(send_options), input_message_content = std::move(input_message_content)]( + object_ptr<td_api::ReplyMarkup> 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, 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, int64 chat_id, PromisedQueryPtr query) mutable { - send_request(make_object<td_api::sendBusinessMessage>( - 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<TdOnReturnBusinessMessageCallback>(this, business_connection->id_, - std::move(query))); + send_request( + make_object<td_api::sendBusinessMessage>( + business_connection->id_, chat_id, + get_input_message_reply_to_unchecked(std::move(reply_parameters)), disable_notification, + protect_content, effect_id, std::move(reply_markup), std::move(input_message_content)), + td::make_unique<TdOnReturnBusinessMessageCallback>(this, business_connection->id_, + std::move(query))); }); } - auto on_success = [this, send_options = std::move(send_options), + auto on_success = [this, receiver_user_id, callback_query_id, send_options = std::move(send_options), input_message_content = std::move(input_message_content), reply_markup = std::move(reply_markup)]( int64 chat_id, object_ptr<td_api::MessageTopic> topic_id, @@ -16908,6 +17265,14 @@ void Client::do_send_message(object_ptr<td_api::InputMessageContent> input_messa } count++; + if (receiver_user_id != 0) { + return send_request(make_object<td_api::sendEphemeralMessage>( + chat_id, std::move(topic_id), receiver_user_id, callback_query_id, + get_input_message_reply_to(std::move(reply_parameters)), 0, false, + std::move(reply_markup), std::move(input_message_content)), + td::make_unique<TdOnSendMessageCallback>(this, chat_id, std::move(query))); + } + send_request(make_object<td_api::sendMessage>( chat_id, std::move(topic_id), get_input_message_reply_to(std::move(reply_parameters)), std::move(send_options), std::move(reply_markup), std::move(input_message_content)), @@ -16956,6 +17321,31 @@ void Client::on_sent_story(object_ptr<td_api::story> &&story, PromisedQueryPtr q CHECK(emplace_result.second); } +td::Status Client::do_edit_ephemeral_message(object_ptr<td_api::InputMessageContent> input_message_content, + PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + TRY_RESULT(receiver_user_id, get_user_id(query.get(), "receiver_user_id")); + auto ephemeral_message_id = get_integer_arg(query.get(), "ephemeral_message_id", 0); + TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); + + resolve_reply_markup_bot_usernames( + std::move(reply_markup), std::move(query), + [this, chat_id_str = chat_id.str(), receiver_user_id, ephemeral_message_id, + input_message_content = std::move(input_message_content)](object_ptr<td_api::ReplyMarkup> reply_markup, + PromisedQueryPtr query) mutable { + check_chat( + chat_id_str, AccessRights::Edit, std::move(query), + [this, receiver_user_id, ephemeral_message_id, input_message_content = std::move(input_message_content), + reply_markup = std::move(reply_markup)](int64 chat_id, PromisedQueryPtr query) mutable { + send_request( + make_object<td_api::editEphemeralMessage>(chat_id, receiver_user_id, ephemeral_message_id, + std::move(reply_markup), std::move(input_message_content)), + td::make_unique<TdOnOkQueryCallback>(std::move(query))); + }); + }); + return td::Status::OK(); +} + void Client::abort_long_poll(bool from_set_webhook) { if (long_poll_query_) { td::Slice message; @@ -17289,6 +17679,22 @@ const Client::ChatInfo *Client::get_chat(int64 chat_id) const { return chats_.get_pointer(chat_id); } +void Client::add_community(CommunityInfo *community_info, object_ptr<td_api::community> &&community) { + community_info->name = std::move(community->name_); +} + +Client::CommunityInfo *Client::add_community_info(int64 community_id) { + auto &community_info = communities_[community_id]; + if (community_info == nullptr) { + community_info = td::make_unique<CommunityInfo>(); + } + return community_info.get(); +} + +const Client::CommunityInfo *Client::get_community_info(int64 community_id) const { + return communities_.get_pointer(community_id); +} + void Client::set_chat_available_reactions(ChatInfo *chat_info, object_ptr<td_api::ChatAvailableReactions> &&available_reactions) { CHECK(chat_info != nullptr); @@ -17615,13 +18021,15 @@ td::Slice Client::get_update_type_name(UpdateType update_type) { return td::Slice("managed_bot"); case UpdateType::GuestMessage: return td::Slice("guest_message"); + case UpdateType::Subscription: + return td::Slice("subscription"); default: UNREACHABLE(); return td::Slice(); } } -td::uint32 Client::get_allowed_update_types(td::MutableSlice allowed_updates, bool is_internal) { +td::uint64 Client::get_allowed_update_types(td::MutableSlice allowed_updates, bool is_internal) { if (allowed_updates.empty()) { return 0; } @@ -17633,11 +18041,11 @@ td::uint32 Client::get_allowed_update_types(td::MutableSlice allowed_updates, bo return 0; } - td::uint32 result = 0; + td::uint64 result = 0; auto value = r_value.move_as_ok(); if (value.type() != td::JsonValue::Type::Array) { if (value.type() == td::JsonValue::Type::Number && is_internal) { - auto r_number = td::to_integer_safe<td::uint32>(value.get_number()); + auto r_number = td::to_integer_safe<td::uint64>(value.get_number()); if (r_number.is_ok() && r_number.ok() > 0) { return r_number.ok(); } @@ -17652,7 +18060,7 @@ td::uint32 Client::get_allowed_update_types(td::MutableSlice allowed_updates, bo to_lower_inplace(type_name); for (int32 i = 0; i < static_cast<int32>(UpdateType::Size); i++) { if (get_update_type_name(static_cast<UpdateType>(i)) == type_name) { - result |= (1 << i); + result |= (static_cast<td::uint64>(1) << i); } } } @@ -17671,7 +18079,7 @@ bool Client::update_allowed_update_types(const Query *query) { if (allowed_update_types == DEFAULT_ALLOWED_UPDATE_TYPES) { value = make_object<td_api::optionValueEmpty>(); } else { - value = make_object<td_api::optionValueInteger>(allowed_update_types); + value = make_object<td_api::optionValueInteger>(static_cast<int64>(allowed_update_types)); } send_request(make_object<td_api::setOption>("xallowed_update_types", std::move(value)), td::make_unique<TdOnOkCallback>()); @@ -17887,11 +18295,12 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { CHECK(state == 3); CHECK(user_id == query->sender_user_id_); - add_update(UpdateType::CallbackQuery, - JsonCallbackQuery(query->id_, user_id, chat_id, message_id, message_info, query->chat_instance_, - query->payload_.get(), this), - 150, user_id + (static_cast<int64>(3) << 33)); - + if (message_info != nullptr || as_tdlib_message_id(as_client_message_id_unchecked(message_id)) == message_id) { + add_update(UpdateType::CallbackQuery, + JsonCallbackQuery(query->id_, user_id, chat_id, message_id, message_info, query->chat_instance_, + query->payload_.get(), this), + 150, user_id + (static_cast<int64>(3) << 33)); + } queue.queue_.pop(); state = 0; } @@ -17989,6 +18398,12 @@ void Client::add_update_managed_bot(object_ptr<td_api::updateManagedBot> &&query query->user_id_ + (static_cast<int64>(13) << 33)); } +void Client::add_update_subscription(object_ptr<td_api::updateUserSubscription> &&query) { + CHECK(query != nullptr); + add_update(UpdateType::Subscription, JsonBotSubscriptionUpdated(query.get(), this), 86400, + query->user_id_ + (static_cast<int64>(14) << 33)); +} + void Client::add_new_custom_event(object_ptr<td_api::updateNewCustomEvent> &&event) { CHECK(event != nullptr); add_update(UpdateType::CustomEvent, JsonCustomJson(event->event_), 600, 0); @@ -18413,9 +18828,9 @@ bool Client::are_equal_suggested_post_prices(const td_api::SuggestedPostPrice *l case td_api::suggestedPostPriceStar::ID: return static_cast<const td_api::suggestedPostPriceStar *>(lhs)->star_count_ == static_cast<const td_api::suggestedPostPriceStar *>(rhs)->star_count_; - case td_api::suggestedPostPriceTon::ID: - return static_cast<const td_api::suggestedPostPriceTon *>(lhs)->toncoin_cent_count_ == - static_cast<const td_api::suggestedPostPriceTon *>(rhs)->toncoin_cent_count_; + case td_api::suggestedPostPriceGram::ID: + return static_cast<const td_api::suggestedPostPriceGram *>(lhs)->gram_cent_count_ == + static_cast<const td_api::suggestedPostPriceGram *>(rhs)->gram_cent_count_; default: UNREACHABLE(); return false; @@ -18868,6 +19283,8 @@ td::unique_ptr<Client::MessageInfo> Client::create_message(object_ptr<td_api::me int64 chat_id = message->chat_id_; message_info->id = message->id_; message_info->chat_id = chat_id; + message_info->receiver_id = std::move(message->receiver_id_); + message_info->ephemeral_message_id = message->ephemeral_message_id_; message_info->date = message->date_; message_info->edit_date = message->edit_date_; message_info->media_album_id = message->media_album_id_; @@ -18924,7 +19341,7 @@ td::unique_ptr<Client::MessageInfo> Client::create_message(object_ptr<td_api::me message_info->sender_tag = std::move(message->sender_tag_); message_info->paid_message_star_count = message->paid_message_star_count_; message_info->effect_id = message->effect_id_; - message_info->is_paid_post = message->is_paid_star_suggested_post_ || message->is_paid_ton_suggested_post_; + message_info->is_paid_post = message->is_paid_star_suggested_post_ || message->is_paid_gram_suggested_post_; message_info->is_outgoing = message->is_outgoing_; message_info->is_self_destruct = message->self_destruct_type_ != nullptr; message_info->is_imported = message->import_info_ != nullptr; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index bf60e99..60f8acf 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -29,6 +29,7 @@ #include "td/utils/Status.h" #include "td/utils/WaitFreeHashMap.h" +#include <functional> #include <limits> #include <memory> #include <queue> @@ -89,6 +90,7 @@ class Client final : public WebhookActor::Callback { class JsonFile; class JsonDatedFile; class JsonDatedFiles; + class JsonCommunity; class JsonUser; class JsonUsers; class JsonReactionType; @@ -244,6 +246,8 @@ class Client final : public WebhookActor::Callback { class JsonChatShared; class JsonManagedBotCreated; class JsonManagedBotUpdated; + class JsonCommunityChatAdded; + class JsonBotSubscriptionUpdated; class JsonGiveawayCreated; class JsonGiveaway; class JsonGiveawayWinners; @@ -352,6 +356,7 @@ class Client final : public WebhookActor::Callback { struct InputReplyParameters { td::string reply_in_chat_id; int64 reply_to_message_id = 0; + int32 reply_to_ephemeral_message_id = 0; bool allow_sending_without_reply = false; object_ptr<td_api::inputTextQuote> quote; int32 checklist_task_id = 0; @@ -361,6 +366,7 @@ class Client final : public WebhookActor::Callback { struct CheckedReplyParameters { int64 reply_in_chat_id = 0; int64 reply_to_message_id = 0; + int32 reply_to_ephemeral_message_id = 0; object_ptr<td_api::inputTextQuote> quote; int32 checklist_task_id = 0; td::string poll_option_id; @@ -497,9 +503,19 @@ class Client final : public WebhookActor::Callback { static bool is_reply_in_same_topic(const object_ptr<td_api::MessageTopic> &reply_topic_id, const object_ptr<td_api::MessageTopic> &topic_id); + template <class T> + static td::Result<td::vector<T>> get_array(td::JsonValue &&values, td::Slice class_name, + td::Result<T> (*func)(td::JsonValue &&value)); + + template <class T> + static td::Result<td::vector<object_ptr<T>>> get_array_generic( + td::JsonValue &&values, td::Slice class_name, + const std::function<td::Result<object_ptr<T>>(td::JsonValue &&value)> &func); + static object_ptr<td_api::InputMessageReplyTo> get_input_message_reply_to(CheckedReplyParameters &&reply_parameters); - static object_ptr<td_api::InputMessageReplyTo> get_input_message_reply_to(InputReplyParameters &&reply_parameters); + static object_ptr<td_api::InputMessageReplyTo> get_input_message_reply_to_unchecked( + InputReplyParameters &&reply_parameters); static td::Result<InputReplyParameters> get_reply_parameters(const Query *query); @@ -509,12 +525,12 @@ class Client final : public WebhookActor::Callback { static td::Result<object_ptr<td_api::KeyboardButtonType>> get_keyboard_button_type(td::JsonObject &object); - static td::Result<object_ptr<td_api::keyboardButton>> get_keyboard_button(td::JsonValue &button); + static td::Result<object_ptr<td_api::keyboardButton>> get_keyboard_button(td::JsonValue &&button); static td::Result<object_ptr<td_api::InlineKeyboardButtonType>> get_inline_keyboard_button_type( td::JsonObject &object, BotUserIds &bot_user_ids); - static td::Result<object_ptr<td_api::inlineKeyboardButton>> get_inline_keyboard_button(td::JsonValue &button, + static td::Result<object_ptr<td_api::inlineKeyboardButton>> get_inline_keyboard_button(td::JsonValue &&button, BotUserIds &bot_user_ids); static td::Result<object_ptr<td_api::ReplyMarkup>> get_reply_markup(const Query *query, BotUserIds &bot_user_ids); @@ -527,20 +543,20 @@ class Client final : public WebhookActor::Callback { static td::Result<object_ptr<td_api::inputSuggestedPostInfo>> get_input_suggested_post_info(td::JsonValue &&value); - static td::Result<object_ptr<td_api::labeledPricePart>> get_labeled_price_part(td::JsonValue &value); + static td::Result<object_ptr<td_api::labeledPricePart>> get_labeled_price_part(td::JsonValue &&value); - static td::Result<td::vector<object_ptr<td_api::labeledPricePart>>> get_labeled_price_parts(td::JsonValue &value); + static td::Result<td::vector<object_ptr<td_api::labeledPricePart>>> get_labeled_price_parts(td::JsonValue &&value); - static td::Result<td::vector<int64>> get_suggested_tip_amounts(td::JsonValue &value); + static td::Result<int64> get_suggested_tip_amount(td::JsonValue &&value); - static td::Result<object_ptr<td_api::shippingOption>> get_shipping_option(td::JsonValue &option); + static td::Result<td::vector<int64>> get_suggested_tip_amounts(td::JsonValue &&value); - static td::Result<td::vector<object_ptr<td_api::shippingOption>>> get_shipping_options(const Query *query); + static td::Result<object_ptr<td_api::shippingOption>> get_shipping_option(td::JsonValue &&option); - static td::Result<td::vector<object_ptr<td_api::shippingOption>>> get_shipping_options(td::JsonValue &&value); + static td::Result<td::vector<object_ptr<td_api::shippingOption>>> get_shipping_options(const Query *query); - static td::Result<object_ptr<td_api::InputMessageContent>> get_input_message_content( - td::JsonValue &input_message_content, bool is_input_message_content_required); + td::Result<object_ptr<td_api::InputMessageContent>> get_input_message_content( + const Query *query, td::JsonObject &object, bool is_input_message_content_required) const; static object_ptr<td_api::ChatAction> get_chat_action(const Query *query); @@ -562,17 +578,15 @@ class Client final : public WebhookActor::Callback { static td::Result<object_ptr<td_api::inlineQueryResultsButton>> get_inline_query_results_button( td::MutableSlice value); - static td::Result<object_ptr<td_api::InputInlineQueryResult>> get_inline_query_result(const Query *query, - BotUserIds &bot_user_ids); + td::Result<object_ptr<td_api::InputInlineQueryResult>> get_inline_query_result(const Query *query, + BotUserIds &bot_user_ids) const; - static td::Result<object_ptr<td_api::InputInlineQueryResult>> get_inline_query_result(td::JsonValue &&value, - BotUserIds &bot_user_ids); + td::Result<object_ptr<td_api::InputInlineQueryResult>> get_inline_query_result(const Query *query, + td::JsonValue &&value, + BotUserIds &bot_user_ids) const; - static td::Result<td::vector<object_ptr<td_api::InputInlineQueryResult>>> get_inline_query_results( - const Query *query, BotUserIds &bot_user_ids); - - static td::Result<td::vector<object_ptr<td_api::InputInlineQueryResult>>> get_inline_query_results( - td::JsonValue &&value, BotUserIds &bot_user_ids); + td::Result<td::vector<object_ptr<td_api::InputInlineQueryResult>>> get_inline_query_results( + const Query *query, BotUserIds &bot_user_ids) const; struct BotCommandScope { object_ptr<td_api::BotCommandScope> scope_; @@ -615,14 +629,14 @@ class Client final : public WebhookActor::Callback { static td::Result<object_ptr<td_api::StickerFormat>> get_sticker_format(td::Slice sticker_format); - td::Result<object_ptr<td_api::inputSticker>> get_legacy_input_sticker(const Query *query) const; + td::Result<object_ptr<td_api::newSticker>> get_legacy_new_sticker(const Query *query) const; - td::Result<object_ptr<td_api::inputSticker>> get_input_sticker(const Query *query) const; + td::Result<object_ptr<td_api::newSticker>> get_new_sticker(const Query *query) const; - td::Result<object_ptr<td_api::inputSticker>> get_input_sticker(const Query *query, td::JsonValue &&value, - td::Slice default_sticker_format) const; + td::Result<object_ptr<td_api::newSticker>> get_new_sticker(const Query *query, td::JsonValue &&value, + td::Slice default_sticker_format) const; - td::Result<td::vector<object_ptr<td_api::inputSticker>>> get_input_stickers(const Query *query) const; + td::Result<td::vector<object_ptr<td_api::newSticker>>> get_new_stickers(const Query *query) const; static td::Result<object_ptr<td_api::InputFile>> get_sticker_input_file(const Query *query, td::Slice field_name = "sticker"); @@ -660,9 +674,24 @@ class Client final : public WebhookActor::Callback { td::string text, object_ptr<td_api::linkPreviewOptions> link_preview_options, td::string parse_mode, td::JsonValue &&input_entities); - static td::Result<object_ptr<td_api::inputRichMessage>> get_input_rich_message(const Query *query); + static td::Result<object_ptr<td_api::pageBlockCaption>> get_page_block_caption(td::JsonValue &&value); + + static td::Result<object_ptr<td_api::pageBlockTableCell>> get_page_block_table_cell(td::JsonValue &&value); - static td::Result<object_ptr<td_api::inputRichMessage>> get_input_rich_message(td::JsonValue &&value); + static td::Result<object_ptr<td_api::RichText>> get_rich_text(td::JsonValue &&value); + + td::Result<td_api::object_ptr<td_api::inputPageBlockListItem>> get_input_page_block_list_item( + const Query *query, td::JsonValue &&value) const; + + td::Result<object_ptr<td_api::InputPageBlock>> get_input_page_block(const Query *query, td::JsonValue &&value) const; + + td::Result<td::vector<object_ptr<td_api::InputPageBlock>>> get_input_page_blocks(const Query *query, + td::JsonValue &&value) const; + + td::Result<object_ptr<td_api::inputRichMessage>> get_input_rich_message(const Query *query) const; + + td::Result<object_ptr<td_api::inputRichMessage>> get_input_rich_message(const Query *query, + td::JsonValue &&value) const; static td::Result<object_ptr<td_api::location>> get_location(const Query *query); @@ -673,9 +702,26 @@ class Client final : public WebhookActor::Callback { static td::Result<object_ptr<td_api::chatPermissions>> get_chat_permissions(const Query *query, bool &allow_legacy, bool use_independent_chat_permissions); - td::Result<object_ptr<td_api::inputChecklistTask>> get_input_checklist_task(td::JsonValue &&input_task) const; + static td::Result<object_ptr<td_api::inputAnimation>> get_input_animation( + const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file, + object_ptr<td_api::inputThumbnail> &&input_thumbnail); + + static td::Result<object_ptr<td_api::inputAudio>> get_input_audio( + const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file, + object_ptr<td_api::inputThumbnail> &&input_thumbnail); + + td::Result<object_ptr<td_api::inputPhoto>> get_input_photo(const Query *query, const td::JsonObject &object, + object_ptr<td_api::InputFile> &&input_file, + bool is_live) const; - td::Result<td::vector<object_ptr<td_api::inputChecklistTask>>> get_input_checklist_tasks(td::JsonValue &&value) const; + td::Result<object_ptr<td_api::inputVideo>> get_input_video( + const Query *query, const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file, + object_ptr<td_api::inputThumbnail> &&input_thumbnail) const; + + static td::Result<object_ptr<td_api::inputVoiceNote>> get_input_voice_note( + const td::JsonObject &object, object_ptr<td_api::InputFile> &&input_file); + + static td::Result<object_ptr<td_api::inputChecklistTask>> get_input_checklist_task(td::JsonValue &&input_task); td::Result<object_ptr<td_api::inputChecklist>> get_input_checklist(const Query *query, td::JsonValue &&input_checklist) const; @@ -689,7 +735,7 @@ class Client final : public WebhookActor::Callback { const td::string &type, object_ptr<td_api::formattedText> &&caption, bool show_caption_above_media, bool has_spoiler, - bool for_album) const; + bool for_album, bool for_rich_message) const; td::Result<object_ptr<td_api::InputMessageContent>> get_input_media(const Query *query, td::Slice field_name) const; @@ -698,12 +744,15 @@ class Client final : public WebhookActor::Callback { td::Result<object_ptr<td_api::InputPollMedia>> get_input_poll_media(const Query *query, td::Slice field_name) const; - td::Result<td::vector<object_ptr<td_api::InputMessageContent>>> get_input_message_contents( - const Query *query, td::Slice field_name) const; + td::Result<object_ptr<td_api::inputRichMessageMedia>> get_input_rich_message_media(const Query *query, + td::JsonValue &&input_media) const; - td::Result<td::vector<object_ptr<td_api::InputMessageContent>>> get_input_message_contents( + td::Result<td::vector<object_ptr<td_api::inputRichMessageMedia>>> get_input_rich_message_medias( const Query *query, td::JsonValue &&value) const; + td::Result<td::vector<object_ptr<td_api::InputMessageContent>>> get_input_message_contents( + const Query *query, td::Slice field_name) const; + td::Result<object_ptr<td_api::inputPaidMedia>> get_input_paid_media(const Query *query, td::JsonValue &&input_media) const; @@ -712,9 +761,6 @@ class Client final : public WebhookActor::Callback { td::Result<td::vector<object_ptr<td_api::inputPaidMedia>>> get_paid_media(const Query *query, td::Slice field_name) const; - td::Result<td::vector<object_ptr<td_api::inputPaidMedia>>> get_paid_media(const Query *query, - td::JsonValue &&value) const; - td::Result<object_ptr<td_api::inputMessageInvoice>> get_input_message_invoice(const Query *query) const; static object_ptr<td_api::messageSendOptions> get_message_send_options( @@ -841,8 +887,13 @@ class Client final : public WebhookActor::Callback { td::Status process_edit_message_caption_query(PromisedQueryPtr &query); td::Status process_edit_message_checklist_query(PromisedQueryPtr &query); td::Status process_edit_message_reply_markup_query(PromisedQueryPtr &query); + td::Status process_edit_ephemeral_message_text_query(PromisedQueryPtr &query); + td::Status process_edit_ephemeral_message_media_query(PromisedQueryPtr &query); + td::Status process_edit_ephemeral_message_caption_query(PromisedQueryPtr &query); + td::Status process_edit_ephemeral_message_reply_markup_query(PromisedQueryPtr &query); td::Status process_delete_message_query(PromisedQueryPtr &query); td::Status process_delete_messages_query(PromisedQueryPtr &query); + td::Status process_delete_ephemeral_message_query(PromisedQueryPtr &query); td::Status process_delete_message_reaction_query(PromisedQueryPtr &query); td::Status process_delete_all_message_reactions_query(PromisedQueryPtr &query); td::Status process_post_story_query(PromisedQueryPtr &query); @@ -982,7 +1033,7 @@ class Client final : public WebhookActor::Callback { void on_webhook_closed(td::Status status); - void delete_last_send_message_time(td::int64 file_size, double max_delay); + void delete_last_send_message_time(int64 file_size, double max_delay); void do_send_message(object_ptr<td_api::InputMessageContent> input_message_content, PromisedQueryPtr query, bool force = false); @@ -993,6 +1044,9 @@ class Client final : public WebhookActor::Callback { void on_sent_story(object_ptr<td_api::story> &&story, PromisedQueryPtr query); + td::Status do_edit_ephemeral_message(object_ptr<td_api::InputMessageContent> input_message_content, + PromisedQueryPtr &query); + void do_get_file(object_ptr<td_api::file> file, PromisedQueryPtr query); bool is_file_being_downloaded(int32 file_id) const; @@ -1071,6 +1125,7 @@ class Client final : public WebhookActor::Callback { object_ptr<td_api::audio> first_profile_audio; int64 personal_chat_id = 0; int64 paid_message_star_count = 0; + int64 community_id = 0; bool have_access = false; bool can_join_groups = false; @@ -1121,6 +1176,7 @@ class Client final : public WebhookActor::Callback { int64 direct_messages_chat_id = 0; int64 paid_message_star_count = 0; int64 guard_bot_user_id = 0; + int64 community_id = 0; object_ptr<td_api::chatLocation> location; object_ptr<td_api::ChatMemberStatus> status; bool is_supergroup = false; @@ -1166,6 +1222,13 @@ class Client final : public WebhookActor::Callback { ChatInfo *add_chat(int64 chat_id); const ChatInfo *get_chat(int64 chat_id) const; + struct CommunityInfo { + td::string name; + }; + static void add_community(CommunityInfo *community_info, object_ptr<td_api::community> &&community); + CommunityInfo *add_community_info(int64 community_id); + const CommunityInfo *get_community_info(int64 community_id) const; + void set_chat_available_reactions(ChatInfo *chat_info, object_ptr<td_api::ChatAvailableReactions> &&available_reactions); @@ -1180,10 +1243,12 @@ class Client final : public WebhookActor::Callback { int64 sender_user_id = 0; int64 sender_chat_id = 0; int64 chat_id = 0; + object_ptr<td_api::MessageSender> receiver_id; int32 date = 0; int32 edit_date = 0; int32 initial_send_date = 0; int32 sender_boost_count = 0; + int32 ephemeral_message_id = 0; int64 paid_message_star_count = 0; int64 guest_query_id = 0; object_ptr<td_api::MessageOrigin> forward_origin; @@ -1394,6 +1459,8 @@ class Client final : public WebhookActor::Callback { void add_update_managed_bot(object_ptr<td_api::updateManagedBot> &&query); + void add_update_subscription(object_ptr<td_api::updateUserSubscription> &&query); + void add_new_custom_event(object_ptr<td_api::updateNewCustomEvent> &&event); void add_new_custom_query(object_ptr<td_api::updateNewCustomQuery> &&query); @@ -1441,12 +1508,13 @@ class Client final : public WebhookActor::Callback { PurchasedPaidMedia, ManagedBot, GuestMessage, + Subscription, Size }; static td::Slice get_update_type_name(UpdateType update_type); - static td::uint32 get_allowed_update_types(td::MutableSlice allowed_updates, bool is_internal); + static td::uint64 get_allowed_update_types(td::MutableSlice allowed_updates, bool is_internal); bool update_allowed_update_types(const Query *query); @@ -1477,10 +1545,11 @@ class Client final : public WebhookActor::Callback { bool have_message_access(int64 chat_id) const; // by default ChatMember, MessageReaction, and MessageReactionCount updates are disabled - static constexpr td::uint32 DEFAULT_ALLOWED_UPDATE_TYPES = - (1 << static_cast<int32>(UpdateType::Size)) - 1 - (1 << static_cast<int32>(UpdateType::ChatMember)) - - (1 << static_cast<int32>(UpdateType::MessageReaction)) - - (1 << static_cast<int32>(UpdateType::MessageReactionCount)); + static constexpr td::uint64 DEFAULT_ALLOWED_UPDATE_TYPES = + (static_cast<td::uint64>(1) << static_cast<int32>(UpdateType::Size)) - 1 - + (static_cast<td::uint64>(1) << static_cast<int32>(UpdateType::ChatMember)) - + (static_cast<td::uint64>(1) << static_cast<int32>(UpdateType::MessageReaction)) - + (static_cast<td::uint64>(1) << static_cast<int32>(UpdateType::MessageReactionCount)); object_ptr<td_api::AuthorizationState> authorization_state_; bool was_authorized_ = false; @@ -1516,6 +1585,7 @@ class Client final : public WebhookActor::Callback { td::WaitFreeHashMap<int64, td::unique_ptr<GroupInfo>> groups_; td::WaitFreeHashMap<int64, td::unique_ptr<SupergroupInfo>> supergroups_; td::WaitFreeHashMap<int64, td::unique_ptr<ChatInfo>> chats_; + td::WaitFreeHashMap<int64, td::unique_ptr<CommunityInfo>> communities_; td::WaitFreeHashMap<td::string, td::unique_ptr<BusinessConnection>> business_connections_; td::FlatHashMap<int32, td::vector<PromisedQueryPtr>> file_download_listeners_; @@ -1645,7 +1715,7 @@ class Client final : public WebhookActor::Callback { double next_bot_updates_warning_time_ = 0; bool was_bot_updates_warning_ = false; - td::uint32 allowed_update_types_ = DEFAULT_ALLOWED_UPDATE_TYPES; + td::uint64 allowed_update_types_ = DEFAULT_ALLOWED_UPDATE_TYPES; bool has_webhook_certificate_ = false; enum class WebhookQueryType { Cancel, Verify }; diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 6aa0562..15504b1 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared<SharedData>(); auto parameters = std::make_unique<ClientParameters>(); - parameters->version_ = "10.1"; + parameters->version_ = "10.2"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats(); |
