diff --git a/CMakeLists.txt b/CMakeLists.txt index be48be4..b73a401 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 5.7 LANGUAGES CXX) +project(TelegramBotApi VERSION 6.0.1 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/td b/td index 1e1ab5d..782670c 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 1e1ab5d1b0e4811e6d9e1584a82da08448d0cada +Subproject commit 782670c7dbf278e0ba07fc7e168f39ac154c7238 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index c504510..a3b3ba5 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -212,6 +212,10 @@ bool Client::init_methods() { methods_.emplace("getmycommands", &Client::process_get_my_commands_query); methods_.emplace("setmycommands", &Client::process_set_my_commands_query); methods_.emplace("deletemycommands", &Client::process_delete_my_commands_query); + methods_.emplace("getmydefaultadministratorrights", &Client::process_get_my_default_administrator_rights_query); + methods_.emplace("setmydefaultadministratorrights", &Client::process_set_my_default_administrator_rights_query); + methods_.emplace("getchatmenubutton", &Client::process_get_chat_menu_button_query); + methods_.emplace("setchatmenubutton", &Client::process_set_chat_menu_button_query); methods_.emplace("getuserprofilephotos", &Client::process_get_user_profile_photos_query); methods_.emplace("sendmessage", &Client::process_send_message_query); methods_.emplace("sendanimation", &Client::process_send_animation_query); @@ -243,6 +247,7 @@ bool Client::init_methods() { methods_.emplace("deletemessage", &Client::process_delete_message_query); methods_.emplace("setgamescore", &Client::process_set_game_score_query); methods_.emplace("getgamehighscores", &Client::process_get_game_high_scores_query); + methods_.emplace("answerwebappquery", &Client::process_answer_web_app_query_query); methods_.emplace("answerinlinequery", &Client::process_answer_inline_query_query); methods_.emplace("answercallbackquery", &Client::process_answer_callback_query_query); methods_.emplace("answershippingquery", &Client::process_answer_shipping_query_query); @@ -689,6 +694,7 @@ class Client::JsonChat final : public Jsonable { CHECK(chat_info != nullptr); auto object = scope->enter_object(); object("id", chat_id_); + const td_api::chatPhoto *photo = nullptr; switch (chat_info->type) { case ChatInfo::Type::Private: { auto user_info = client_->get_user_info(chat_info->user_id); @@ -719,6 +725,7 @@ class Client::JsonChat final : public Jsonable { object("has_private_forwards", td::JsonTrue()); } } + photo = user_info->photo.get(); break; } case ChatInfo::Type::Group: { @@ -743,6 +750,7 @@ class Client::JsonChat final : public Jsonable { permissions->can_add_web_page_previews_ && permissions->can_change_info_ && permissions->can_invite_users_ && permissions->can_pin_messages_; object("all_members_are_administrators", td::JsonBool(everyone_is_administrator)); + photo = group_info->photo.get(); break; } case ChatInfo::Type::Supergroup: { @@ -800,6 +808,7 @@ class Client::JsonChat final : public Jsonable { object("location", JsonChatLocation(supergroup_info->location.get())); } } + photo = supergroup_info->photo.get(); break; } case ChatInfo::Type::Unknown: @@ -807,8 +816,34 @@ class Client::JsonChat final : public Jsonable { UNREACHABLE(); } if (is_full_) { - if (chat_info->photo != nullptr) { - object("photo", JsonChatPhotoInfo(chat_info->photo.get())); + if (photo != nullptr) { + const td_api::file *small_file = nullptr; + const td_api::file *big_file = nullptr; + for (auto &size : photo->sizes_) { + if (size->type_ == "a") { + small_file = size->photo_.get(); + } else if (size->type_ == "c") { + big_file = size->photo_.get(); + } + } + if (small_file == nullptr || big_file == nullptr) { + LOG(ERROR) << "Failed to convert chatPhoto to chatPhotoInfo for " << chat_id_ << ": " << to_string(*photo); + } else { + if (chat_info->photo_info == nullptr) { + LOG(ERROR) << "Have chatPhoto without chatPhotoInfo for " << chat_id_; + } else { + if (small_file->remote_->unique_id_ != chat_info->photo_info->small_->remote_->unique_id_ || + big_file->remote_->unique_id_ != chat_info->photo_info->big_->remote_->unique_id_) { + LOG(ERROR) << "Have different chatPhoto and chatPhotoInfo for " << chat_id_ << ": " << to_string(*photo) + << ' ' << to_string(chat_info->photo_info); + } + } + } + } else if (chat_info->photo_info != nullptr) { + LOG(ERROR) << "Have chatPhotoInfo without chatPhoto for " << chat_id_; + } + if (chat_info->photo_info != nullptr) { + object("photo", JsonChatPhotoInfo(chat_info->photo_info.get())); } if (pinned_message_id_ != 0) { CHECK(pinned_message_id_ != -1); @@ -1523,6 +1558,20 @@ class Client::JsonPassportData final : public Jsonable { const Client *client_; }; +class Client::JsonWebAppData final : public Jsonable { + public: + explicit JsonWebAppData(const td_api::messageWebAppDataReceived *web_app_data) : web_app_data_(web_app_data) { + } + void store(JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("button_text", web_app_data_->button_text_); + object("data", web_app_data_->data_); + } + + private: + const td_api::messageWebAppDataReceived *web_app_data_; +}; + class Client::JsonProximityAlertTriggered final : public Jsonable { public: JsonProximityAlertTriggered(const td_api::messageProximityAlertTriggered *proximity_alert_triggered, @@ -1612,6 +1661,19 @@ class Client::JsonCallbackGame final : public Jsonable { } }; +class Client::JsonWebAppInfo final : public Jsonable { + public: + explicit JsonWebAppInfo(const td::string &url) : url_(url) { + } + void store(JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("url", url_); + } + + private: + const td::string &url_; +}; + class Client::JsonInlineKeyboardButton final : public Jsonable { public: explicit JsonInlineKeyboardButton(const td_api::inlineKeyboardButton *button) : button_(button) { @@ -1660,6 +1722,11 @@ class Client::JsonInlineKeyboardButton final : public Jsonable { object("url", PSLICE() << "tg://user?id=" << type->user_id_); break; } + case td_api::inlineKeyboardButtonTypeWebApp::ID: { + auto type = static_cast(button_->type_.get()); + object("web_app", JsonWebAppInfo(type->url_)); + break; + } default: UNREACHABLE(); break; @@ -1776,109 +1843,105 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { } switch (message_->content->get_id()) { case td_api::messageText::ID: { - auto message_text = static_cast(message_->content.get()); - object("text", message_text->text_->text_); - if (!message_text->text_->entities_.empty()) { - object("entities", JsonVectorEntities(message_text->text_->entities_, client_)); + auto content = static_cast(message_->content.get()); + object("text", content->text_->text_); + if (!content->text_->entities_.empty()) { + object("entities", JsonVectorEntities(content->text_->entities_, client_)); } break; } case td_api::messageAnimation::ID: { - auto message_animation = static_cast(message_->content.get()); - object("animation", JsonAnimation(message_animation->animation_.get(), false, client_)); - object("document", JsonAnimation(message_animation->animation_.get(), true, client_)); - add_caption(object, message_animation->caption_); + auto content = static_cast(message_->content.get()); + object("animation", JsonAnimation(content->animation_.get(), false, client_)); + object("document", JsonAnimation(content->animation_.get(), true, client_)); + add_caption(object, content->caption_); break; } case td_api::messageAudio::ID: { - auto message_audio = static_cast(message_->content.get()); - object("audio", JsonAudio(message_audio->audio_.get(), client_)); - add_caption(object, message_audio->caption_); + auto content = static_cast(message_->content.get()); + object("audio", JsonAudio(content->audio_.get(), client_)); + add_caption(object, content->caption_); break; } case td_api::messageDocument::ID: { - auto message_document = static_cast(message_->content.get()); - object("document", JsonDocument(message_document->document_.get(), client_)); - add_caption(object, message_document->caption_); + auto content = static_cast(message_->content.get()); + object("document", JsonDocument(content->document_.get(), client_)); + add_caption(object, content->caption_); break; } case td_api::messagePhoto::ID: { - auto message_photo = static_cast(message_->content.get()); - if (message_photo->photo_ == nullptr) { - LOG(ERROR) << "Got empty messagePhoto"; - break; - } - object("photo", JsonPhoto(message_photo->photo_.get(), client_)); - add_caption(object, message_photo->caption_); + auto content = static_cast(message_->content.get()); + CHECK(content->photo_ != nullptr); + object("photo", JsonPhoto(content->photo_.get(), client_)); + add_caption(object, content->caption_); break; } case td_api::messageSticker::ID: { - auto message_sticker = static_cast(message_->content.get()); - object("sticker", JsonSticker(message_sticker->sticker_.get(), client_)); + auto content = static_cast(message_->content.get()); + object("sticker", JsonSticker(content->sticker_.get(), client_)); break; } case td_api::messageVideo::ID: { - auto message_video = static_cast(message_->content.get()); - object("video", JsonVideo(message_video->video_.get(), client_)); - add_caption(object, message_video->caption_); + auto content = static_cast(message_->content.get()); + object("video", JsonVideo(content->video_.get(), client_)); + add_caption(object, content->caption_); break; } case td_api::messageVideoNote::ID: { - auto message_video_note = static_cast(message_->content.get()); - object("video_note", JsonVideoNote(message_video_note->video_note_.get(), client_)); + auto content = static_cast(message_->content.get()); + object("video_note", JsonVideoNote(content->video_note_.get(), client_)); break; } case td_api::messageVoiceNote::ID: { - auto message_voice_note = static_cast(message_->content.get()); - object("voice", JsonVoiceNote(message_voice_note->voice_note_.get(), client_)); - add_caption(object, message_voice_note->caption_); + auto content = static_cast(message_->content.get()); + object("voice", JsonVoiceNote(content->voice_note_.get(), client_)); + add_caption(object, content->caption_); break; } case td_api::messageContact::ID: { - auto message_contact = static_cast(message_->content.get()); - object("contact", JsonContact(message_contact->contact_.get())); + auto content = static_cast(message_->content.get()); + object("contact", JsonContact(content->contact_.get())); break; } case td_api::messageDice::ID: { - auto message_dice = static_cast(message_->content.get()); - object("dice", JsonDice(message_dice->emoji_, message_dice->value_)); + auto content = static_cast(message_->content.get()); + object("dice", JsonDice(content->emoji_, content->value_)); break; } case td_api::messageGame::ID: { - auto message_game = static_cast(message_->content.get()); - object("game", JsonGame(message_game->game_.get(), client_)); + auto content = static_cast(message_->content.get()); + object("game", JsonGame(content->game_.get(), client_)); break; } case td_api::messageInvoice::ID: { - auto message_invoice = static_cast(message_->content.get()); - object("invoice", JsonInvoice(message_invoice)); + auto content = static_cast(message_->content.get()); + object("invoice", JsonInvoice(content)); break; } case td_api::messageLocation::ID: { - auto message_location = static_cast(message_->content.get()); - object("location", JsonLocation(message_location->location_.get(), message_location->expires_in_, - message_location->live_period_, message_location->heading_, - message_location->proximity_alert_radius_)); + auto content = static_cast(message_->content.get()); + object("location", JsonLocation(content->location_.get(), content->expires_in_, content->live_period_, + content->heading_, content->proximity_alert_radius_)); break; } case td_api::messageVenue::ID: { - auto message_venue = static_cast(message_->content.get()); - object("location", JsonLocation(message_venue->venue_->location_.get())); - object("venue", JsonVenue(message_venue->venue_.get())); + auto content = static_cast(message_->content.get()); + object("location", JsonLocation(content->venue_->location_.get())); + object("venue", JsonVenue(content->venue_.get())); break; } case td_api::messagePoll::ID: { - auto message_poll = static_cast(message_->content.get()); - object("poll", JsonPoll(message_poll->poll_.get(), client_)); + auto content = static_cast(message_->content.get()); + object("poll", JsonPoll(content->poll_.get(), client_)); break; } case td_api::messageChatAddMembers::ID: { - auto message_add_members = static_cast(message_->content.get()); - int64 user_id = client_->choose_added_member_id(message_add_members); + auto content = static_cast(message_->content.get()); + int64 user_id = client_->choose_added_member_id(content); if (user_id > 0) { object("new_chat_participant", JsonUser(user_id, client_)); object("new_chat_member", JsonUser(user_id, client_)); - object("new_chat_members", JsonUsers(message_add_members->member_user_ids_, client_)); + object("new_chat_members", JsonUsers(content->member_user_ids_, client_)); } else { LOG(ERROR) << "Can't choose added member for new_chat_member field"; } @@ -1901,24 +1964,24 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { break; } case td_api::messageChatDeleteMember::ID: { - auto message_delete_member = static_cast(message_->content.get()); - int64 user_id = message_delete_member->user_id_; + auto content = static_cast(message_->content.get()); + int64 user_id = content->user_id_; object("left_chat_participant", JsonUser(user_id, client_)); object("left_chat_member", JsonUser(user_id, client_)); break; } case td_api::messageChatChangeTitle::ID: { - auto message_change_title = static_cast(message_->content.get()); - object("new_chat_title", message_change_title->title_); + auto content = static_cast(message_->content.get()); + object("new_chat_title", content->title_); break; } case td_api::messageChatChangePhoto::ID: { - auto message_change_photo = static_cast(message_->content.get()); - if (message_change_photo->photo_ == nullptr) { + auto content = static_cast(message_->content.get()); + if (content->photo_ == nullptr) { LOG(ERROR) << "Got empty messageChatChangePhoto"; break; } - object("new_chat_photo", JsonChatPhoto(message_change_photo->photo_.get(), client_)); + object("new_chat_photo", JsonChatPhoto(content->photo_.get(), client_)); break; } case td_api::messageChatDeletePhoto::ID: @@ -1943,20 +2006,20 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { break; } case td_api::messageChatUpgradeTo::ID: { - auto message_chat_upgrade_to = static_cast(message_->content.get()); - auto chat_id = get_supergroup_chat_id(message_chat_upgrade_to->supergroup_id_); + auto content = static_cast(message_->content.get()); + auto chat_id = get_supergroup_chat_id(content->supergroup_id_); object("migrate_to_chat_id", td::JsonLong(chat_id)); break; } case td_api::messageChatUpgradeFrom::ID: { - auto message_chat_upgrade_from = static_cast(message_->content.get()); - auto chat_id = get_basic_group_chat_id(message_chat_upgrade_from->basic_group_id_); + auto content = static_cast(message_->content.get()); + auto chat_id = get_basic_group_chat_id(content->basic_group_id_); object("migrate_from_chat_id", td::JsonLong(chat_id)); break; } case td_api::messagePinMessage::ID: { - auto message_pin_message = static_cast(message_->content.get()); - auto message_id = message_pin_message->message_id_; + auto content = static_cast(message_->content.get()); + auto message_id = content->message_id_; if (message_id > 0) { const MessageInfo *pinned_message = client_->get_message(message_->chat_id, message_id); if (pinned_message != nullptr) { @@ -1972,8 +2035,8 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { case td_api::messagePaymentSuccessful::ID: break; case td_api::messagePaymentSuccessfulBot::ID: { - auto message_payment_sent_bot = static_cast(message_->content.get()); - object("successful_payment", JsonSuccessfulPaymentBot(message_payment_sent_bot)); + auto content = static_cast(message_->content.get()); + object("successful_payment", JsonSuccessfulPaymentBot(content)); break; } case td_api::messageCall::ID: @@ -2006,18 +2069,17 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { break; } - auto message_website_connected = static_cast(message_->content.get()); - if (!message_website_connected->domain_name_.empty()) { - object("connected_website", message_website_connected->domain_name_); + auto content = static_cast(message_->content.get()); + if (!content->domain_name_.empty()) { + object("connected_website", content->domain_name_); } break; } case td_api::messagePassportDataSent::ID: break; case td_api::messagePassportDataReceived::ID: { - auto message_passport_data_received = - static_cast(message_->content.get()); - object("passport_data", JsonPassportData(message_passport_data_received, client_)); + auto content = static_cast(message_->content.get()); + object("passport_data", JsonPassportData(content, client_)); break; } case td_api::messageProximityAlertTriggered::ID: { @@ -2027,22 +2089,33 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { } case td_api::messageVideoChatScheduled::ID: { auto content = static_cast(message_->content.get()); + object("video_chat_scheduled", JsonVideoChatScheduled(content)); object("voice_chat_scheduled", JsonVideoChatScheduled(content)); break; } case td_api::messageVideoChatStarted::ID: + object("video_chat_started", JsonVideoChatStarted()); object("voice_chat_started", JsonVideoChatStarted()); break; case td_api::messageVideoChatEnded::ID: { auto content = static_cast(message_->content.get()); + object("video_chat_ended", JsonVideoChatEnded(content)); object("voice_chat_ended", JsonVideoChatEnded(content)); break; } case td_api::messageInviteVideoChatParticipants::ID: { auto content = static_cast(message_->content.get()); + object("video_chat_participants_invited", JsonInviteVideoChatParticipants(content, client_)); object("voice_chat_participants_invited", JsonInviteVideoChatParticipants(content, client_)); break; } + case td_api::messageWebAppDataSent::ID: + break; + case td_api::messageWebAppDataReceived::ID: { + auto content = static_cast(message_->content.get()); + object("web_app_data", JsonWebAppData(content)); + break; + } default: UNREACHABLE(); } @@ -2339,6 +2412,42 @@ class Client::JsonBotCommand final : public Jsonable { const td_api::botCommand *command_; }; +class Client::JsonBotMenuButton final : public Jsonable { + public: + explicit JsonBotMenuButton(const td_api::botMenuButton *menu_button) : menu_button_(menu_button) { + } + void store(JsonValueScope *scope) const { + auto object = scope->enter_object(); + if (menu_button_->text_.empty()) { + object("type", menu_button_->url_.empty() ? "commands" : "default"); + } else { + object("type", "web_app"); + object("text", menu_button_->text_); + object("web_app", JsonWebAppInfo(menu_button_->url_)); + } + } + + private: + const td_api::botMenuButton *menu_button_; +}; + +class Client::JsonChatAdministratorRights final : public Jsonable { + public: + JsonChatAdministratorRights(const td_api::chatAdministratorRights *rights, Client::ChatType chat_type) + : rights_(rights), chat_type_(chat_type) { + } + + void store(JsonValueScope *scope) const { + auto object = scope->enter_object(); + td_api::chatAdministratorRights empty_rights; + Client::json_store_administrator_rights(object, rights_ == nullptr ? &empty_rights : rights_, chat_type_); + } + + private: + const td_api::chatAdministratorRights *rights_; + Client::ChatType chat_type_; +}; + class Client::JsonChatPhotos final : public Jsonable { public: JsonChatPhotos(const td_api::chatPhotos *photos, const Client *client) : photos_(photos), client_(client) { @@ -2401,24 +2510,11 @@ class Client::JsonChatMember final : public Jsonable { case td_api::chatMemberStatusAdministrator::ID: { auto administrator = static_cast(member_->status_.get()); object("can_be_edited", td::JsonBool(administrator->can_be_edited_)); - object("can_manage_chat", td::JsonBool(administrator->can_manage_chat_)); - object("can_change_info", td::JsonBool(administrator->can_change_info_)); - if (chat_type_ == Client::ChatType::Channel) { - object("can_post_messages", td::JsonBool(administrator->can_post_messages_)); - object("can_edit_messages", td::JsonBool(administrator->can_edit_messages_)); - } - object("can_delete_messages", td::JsonBool(administrator->can_delete_messages_)); - object("can_invite_users", td::JsonBool(administrator->can_invite_users_)); - object("can_restrict_members", td::JsonBool(administrator->can_restrict_members_)); - if (chat_type_ == Client::ChatType::Group || chat_type_ == Client::ChatType::Supergroup) { - object("can_pin_messages", td::JsonBool(administrator->can_pin_messages_)); - } - object("can_promote_members", td::JsonBool(administrator->can_promote_members_)); - object("can_manage_voice_chats", td::JsonBool(administrator->can_manage_video_chats_)); + Client::json_store_administrator_rights(object, administrator->rights_.get(), chat_type_); + object("can_manage_voice_chats", td::JsonBool(administrator->rights_->can_manage_video_chats_)); if (!administrator->custom_title_.empty()) { object("custom_title", administrator->custom_title_); } - object("is_anonymous", td::JsonBool(administrator->is_anonymous_)); break; } case td_api::chatMemberStatusMember::ID: @@ -2610,6 +2706,9 @@ class Client::JsonWebhookInfo final : public Jsonable { if (client_->allowed_update_types_ != DEFAULT_ALLOWED_UPDATE_TYPES) { object("allowed_updates", JsonUpdateTypes(client_->allowed_update_types_)); } + if (client_->last_synchronization_error_date_ > 0) { + object("last_synchronization_error_date", client_->last_synchronization_error_date_); + } } private: @@ -2644,6 +2743,21 @@ class Client::JsonStickerSet final : public Jsonable { const Client *client_; }; +class Client::JsonSentWebAppMessage final : public Jsonable { + public: + explicit JsonSentWebAppMessage(const td_api::sentWebAppMessage *message) : message_(message) { + } + void store(JsonValueScope *scope) const { + auto object = scope->enter_object(); + if (!message_->inline_message_id_.empty()) { + object("inline_message_id", message_->inline_message_id_); + } + } + + private: + const td_api::sentWebAppMessage *message_; +}; + // start custom Json objects impl class Client::JsonAuthorizationState : public Jsonable { @@ -3585,7 +3699,7 @@ class Client::TdOnGetChatPinnedMessageCallback final : public TdQueryCallback { if (sticker_set_id != 0 && client_->get_sticker_set_name(sticker_set_id).empty()) { return client_->send_request( make_object(sticker_set_id), - std::make_unique(client_, chat_id_, pinned_message_id, std::move(query_))); + td::make_unique(client_, chat_id_, pinned_message_id, std::move(query_))); } } @@ -3622,7 +3736,7 @@ class Client::TdOnGetChatPinnedMessageToUnpinCallback final : public TdQueryCall CHECK(pinned_message_id > 0); client_->send_request(make_object(chat_id_, pinned_message_id), - std::make_unique(std::move(query_))); + td::make_unique(std::move(query_))); } private: @@ -3651,6 +3765,55 @@ class Client::TdOnGetMyCommandsCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; +class Client::TdOnGetMyDefaultAdministratorRightsCallback final : public TdQueryCallback { + public: + TdOnGetMyDefaultAdministratorRightsCallback(bool for_channels, PromisedQueryPtr query) + : for_channels_(for_channels), query_(std::move(query)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + return fail_query_with_error(std::move(query_), move_object_as(result)); + } + + CHECK(result->get_id() == td_api::userFullInfo::ID); + auto full_info = move_object_as(result); + if (full_info->bot_info_ == nullptr) { + LOG(ERROR) << "Have no bot info for self"; + return fail_query_with_error(std::move(query_), + make_object(500, "Requested data is inaccessible")); + } + auto bot_info = std::move(full_info->bot_info_); + const auto *rights = for_channels_ ? bot_info->default_channel_administrator_rights_.get() + : bot_info->default_group_administrator_rights_.get(); + answer_query(JsonChatAdministratorRights(rights, for_channels_ ? ChatType::Channel : ChatType::Supergroup), + std::move(query_)); + } + + private: + bool for_channels_; + PromisedQueryPtr query_; +}; + +class Client::TdOnGetMenuButtonCallback final : public TdQueryCallback { + public: + explicit TdOnGetMenuButtonCallback(PromisedQueryPtr query) : query_(std::move(query)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + return fail_query_with_error(std::move(query_), move_object_as(result)); + } + + CHECK(result->get_id() == td_api::botMenuButton::ID); + auto menu_button = move_object_as(result); + answer_query(JsonBotMenuButton(menu_button.get()), std::move(query_)); + } + + private: + PromisedQueryPtr query_; +}; + class Client::TdOnGetChatFullInfoCallback final : public TdQueryCallback { public: TdOnGetChatFullInfoCallback(Client *client, int64 chat_id, PromisedQueryPtr query) @@ -3665,7 +3828,7 @@ class Client::TdOnGetChatFullInfoCallback final : public TdQueryCallback { // we don't need the result, everything is already received through updates client_->send_request(make_object(chat_id_), - std::make_unique(client_, chat_id_, std::move(query_))); + td::make_unique(client_, chat_id_, std::move(query_))); } private: @@ -3807,6 +3970,25 @@ class Client::TdOnGetGameHighScoresCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; +class Client::TdOnAnswerWebAppQueryCallback final : public TdQueryCallback { + public: + explicit TdOnAnswerWebAppQueryCallback(PromisedQueryPtr query) : query_(std::move(query)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + return fail_query_with_error(std::move(query_), move_object_as(result)); + } + + CHECK(result->get_id() == td_api::sentWebAppMessage::ID); + auto message = move_object_as(result); + answer_query(JsonSentWebAppMessage(message.get()), std::move(query_)); + } + + private: + PromisedQueryPtr query_; +}; + class Client::TdOnReturnFileCallback final : public TdQueryCallback { public: TdOnReturnFileCallback(const Client *client, PromisedQueryPtr query) : client_(client), query_(std::move(query)) { @@ -4085,14 +4267,14 @@ void Client::close() { if (td_client_.empty()) { set_timeout_in(0); } else if (!closing_) { - do_send_request(make_object(), std::make_unique()); + do_send_request(make_object(), td::make_unique()); } } void Client::log_out(bool is_api_id_invalid) { is_api_id_invalid_ |= is_api_id_invalid; if (!td_client_.empty() && !logging_out_ && !closing_) { - do_send_request(make_object(), std::make_unique()); + do_send_request(make_object(), td::make_unique()); } } @@ -4100,6 +4282,18 @@ std::size_t Client::get_pending_update_count() const { return parameters_->shared_data_->tqueue_->get_size(tqueue_id_); } +void Client::update_last_synchronization_error_date() { + if (disconnection_time_ == 0 || !was_authorized_ || logging_out_ || closing_) { + return; + } + auto now = td::Time::now(); + if (last_update_creation_time_ > now - 10 || disconnection_time_ > now - 180) { + return; + } + + last_synchronization_error_date_ = get_unix_time(); +} + ServerBotInfo Client::get_bot_info() const { ServerBotInfo res; if (is_user_) { @@ -4323,7 +4517,7 @@ template void Client::check_user_read_access(const UserInfo *user_info, PromisedQueryPtr query, OnSuccess on_success) { CHECK(user_info != nullptr); if (!user_info->have_access) { - return fail_query(400, "Bad Request: have no access to the user", std::move(query)); + // return fail_query(400, "Bad Request: have no access to the user", std::move(query)); } on_success(std::move(query)); } @@ -4335,7 +4529,7 @@ void Client::check_user(int64 user_id, PromisedQueryPtr query, OnSuccess on_succ return check_user_read_access(user_info, std::move(query), std::move(on_success)); } send_request(make_object(user_id), - std::make_unique>(this, std::move(query), std::move(on_success))); + td::make_unique>(this, std::move(query), std::move(on_success))); } template @@ -4346,7 +4540,7 @@ void Client::check_user_no_fail(int64 user_id, PromisedQueryPtr query, OnSuccess return; } send_request(make_object(user_id), - std::make_unique>(std::move(query), std::move(on_success))); + td::make_unique>(std::move(query), std::move(on_success))); } template @@ -4377,9 +4571,9 @@ void Client::check_chat_access(int64 chat_id, AccessRights access_rights, const CHECK(group_info != nullptr); if (!group_info->is_active && need_write_access) { if (group_info->upgraded_to_supergroup_id != 0) { - std::unordered_map> parameters; + td::FlatHashMap> parameters; auto updagraded_to_chat_id = get_supergroup_chat_id(group_info->upgraded_to_supergroup_id); - parameters.emplace("migrate_to_chat_id", std::make_unique(updagraded_to_chat_id)); + parameters.emplace("migrate_to_chat_id", td::make_unique(updagraded_to_chat_id)); return fail_query(400, "Bad Request: group chat was upgraded to a supergroup chat", std::move(query), std::move(parameters)); } else { @@ -4430,7 +4624,7 @@ void Client::check_chat(Slice chat_id_str, AccessRights access_rights, PromisedQ if (chat_id_str[0] == '@') { return send_request(make_object(chat_id_str.str()), - std::make_unique>(this, false, access_rights, std::move(query), + td::make_unique>(this, false, access_rights, std::move(query), std::move(on_success))); } @@ -4440,8 +4634,8 @@ void Client::check_chat(Slice chat_id_str, AccessRights access_rights, PromisedQ return check_chat_access(chat_id, access_rights, chat_info, std::move(query), std::move(on_success)); } send_request(make_object(chat_id), - std::make_unique>(this, false, access_rights, std::move(query), - std::move(on_success))); + td::make_unique>(this, false, access_rights, std::move(query), + std::move(on_success))); } template @@ -4460,7 +4654,7 @@ void Client::check_chat_no_fail(Slice chat_id_str, PromisedQueryPtr query, OnSuc if (chat_info != nullptr) { return on_success(chat_id, std::move(query)); } - send_request(make_object(chat_id), std::make_unique>( + send_request(make_object(chat_id), td::make_unique>( chat_id, std::move(query), std::move(on_success))); } @@ -4501,7 +4695,7 @@ void Client::check_remote_file_id(td::string file_id, PromisedQueryPtr query, On } send_request(make_object(std::move(file_id), nullptr), - std::make_unique>(std::move(query), std::move(on_success))); + td::make_unique>(std::move(query), std::move(on_success))); } bool Client::is_chat_member(const object_ptr &status) { @@ -4555,7 +4749,7 @@ void Client::check_message(Slice chat_id_str, int64 message_id, bool allow_empty send_request( make_object(chat_id, message_id), - std::make_unique>( + td::make_unique>( this, chat_id, message_id, allow_empty, message_type, std::move(query), std::move(on_success))); }); } @@ -4567,7 +4761,7 @@ void Client::resolve_sticker_set(const td::string &sticker_set_name, PromisedQue } send_request(make_object(sticker_set_name), - std::make_unique>(std::move(query), std::move(on_success))); + td::make_unique>(std::move(query), std::move(on_success))); } void Client::fix_reply_markup_bot_user_ids(object_ptr &reply_markup) const { @@ -4619,7 +4813,7 @@ void Client::resolve_bot_usernames(PromisedQueryPtr query, td::Promise(username), - std::make_unique(this, username)); + td::make_unique(this, username)); } } unresolved_bot_usernames_.clear(); @@ -4700,13 +4894,12 @@ void Client::get_chat_member(int64 chat_id, int64 user_id, PromisedQueryPtr quer check_user_no_fail( user_id, std::move(query), [this, chat_id, user_id, on_success = std::move(on_success)](PromisedQueryPtr query) mutable { - send_request( - make_object(chat_id, td_api::make_object(user_id)), - std::make_unique>(std::move(query), std::move(on_success))); + send_request(make_object(chat_id, make_object(user_id)), + td::make_unique>(std::move(query), std::move(on_success))); }); } -void Client::send_request(object_ptr &&f, std::unique_ptr handler) { +void Client::send_request(object_ptr &&f, td::unique_ptr handler) { if (logging_out_) { return handler->on_result( make_object(LOGGING_OUT_ERROR_CODE, get_logging_out_error_description().str())); @@ -4718,7 +4911,7 @@ void Client::send_request(object_ptr &&f, std::unique_ptr &&f, std::unique_ptr handler) { +void Client::do_send_request(object_ptr &&f, td::unique_ptr handler) { CHECK(!td_client_.empty()); auto id = handlers_.create(std::move(handler)); send_closure(td_client_, &td::ClientActor::request, id, std::move(f)); @@ -4736,7 +4929,7 @@ void Client::on_update_file(object_ptr file) { if ((!parameters_->local_mode_ || !parameters_->no_file_limit_) && file->local_->downloaded_size_ > MAX_DOWNLOAD_FILE_SIZE) { if (file->local_->is_downloading_active_) { send_request(make_object(file_id, false), - std::make_unique()); + td::make_unique()); } return on_file_download(file_id, Status::Error(400, "Bad Request: file is too big")); } @@ -4762,47 +4955,47 @@ void Client::on_update_authorization_state() { case td_api::authorizationStateWaitTdlibParameters::ID: { send_request( make_object("ignore_inline_thumbnails", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("reuse_uploaded_photos_by_hash", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("disable_persistent_network_statistics", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("disable_time_adjustment_protection", make_object(true)), - std::make_unique()); + td::make_unique()); send_request( make_object("disable_minithumbnails", make_object(true)), - std::make_unique()); + td::make_unique()); send_request( make_object("disable_document_filenames", make_object(true)), - std::make_unique()); + td::make_unique()); send_request( make_object("disable_notifications", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("ignore_update_chat_last_message", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("ignore_update_chat_read_inbox", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("ignore_update_user_chat_action", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("ignore_server_deletes_and_reads", make_object(true)), - std::make_unique()); + td::make_unique()); send_request(make_object("delete_chat_reference_after_seconds", make_object(3600)), - std::make_unique()); + td::make_unique()); send_request(make_object("delete_user_reference_after_seconds", make_object(3600)), - std::make_unique()); + td::make_unique()); send_request(make_object("delete_file_reference_after_seconds", make_object( parameters_->file_expiration_timeout_seconds_)), - std::make_unique()); + td::make_unique()); auto parameters = make_object(); @@ -4821,19 +5014,19 @@ void Client::on_update_authorization_state() { parameters->ignore_file_names_ = true; return send_request(make_object(std::move(parameters)), - std::make_unique(this)); + td::make_unique(this)); } case td_api::authorizationStateWaitEncryptionKey::ID: - return send_request(make_object(), std::make_unique(this)); + return send_request(make_object(), td::make_unique(this)); case td_api::authorizationStateWaitPhoneNumber::ID: send_request(make_object("online", make_object(true)), - std::make_unique()); + td::make_unique()); if (is_user_) { waiting_for_auth_input_ = true; return loop(); } else { return send_request(make_object(bot_token_), - std::make_unique(this)); + td::make_unique(this)); } case td_api::authorizationStateWaitCode::ID: case td_api::authorizationStateWaitPassword::ID: @@ -4845,7 +5038,7 @@ void Client::on_update_authorization_state() { auto user_info = get_user_info(my_id_); if (my_id_ <= 0 || user_info == nullptr) { LOG(INFO) << "Send getMe request for " << my_id_; - return send_request(make_object(), std::make_unique(this)); + return send_request(make_object(), td::make_unique(this)); } if (!was_authorized_) { @@ -4860,6 +5053,7 @@ void Client::on_update_authorization_state() { } td::reset_to_empty(pending_updates_); } + last_update_creation_time_ = td::Time::now(); } return loop(); } @@ -4867,6 +5061,7 @@ void Client::on_update_authorization_state() { waiting_for_auth_input_ = false; if (!logging_out_) { LOG(WARNING) << "Logging out"; + update_last_synchronization_error_date(); logging_out_ = true; if (was_authorized_ && !closing_) { td::send_event(parent_, td::Event::raw(nullptr)); @@ -4877,6 +5072,7 @@ void Client::on_update_authorization_state() { waiting_for_auth_input_ = false; if (!closing_) { LOG(WARNING) << "Closing"; + update_last_synchronization_error_date(); closing_ = true; if (was_authorized_ && !logging_out_) { td::send_event(parent_, td::Event::raw(nullptr)); @@ -4953,7 +5149,7 @@ void Client::on_update(object_ptr result) { auto message_id = update->message_id_; on_update_message_edited(chat_id, message_id, update->edit_date_, std::move(update->reply_markup_)); send_request(make_object(chat_id, message_id), - std::make_unique(this)); + td::make_unique(this)); break; } case td_api::updateDeleteMessages::ID: { @@ -4973,7 +5169,7 @@ void Client::on_update(object_ptr result) { auto generation_id = update->generation_id_; send_request( make_object(generation_id, make_object(400, "Wrong file_id")), - std::make_unique()); + td::make_unique()); break; } case td_api::updateNewChat::ID: { @@ -5017,7 +5213,7 @@ void Client::on_update(object_ptr result) { } chat_info->title = std::move(chat->title_); - chat_info->photo = std::move(chat->photo_); + chat_info->photo_info = std::move(chat->photo_); chat_info->permissions = std::move(chat->permissions_); chat_info->message_auto_delete_time = chat->message_ttl_; chat_info->has_protected_content = chat->has_protected_content_; @@ -5034,7 +5230,7 @@ void Client::on_update(object_ptr result) { auto update = move_object_as(result); auto chat_info = add_chat(update->chat_id_); CHECK(chat_info->type != ChatInfo::Type::Unknown); - chat_info->photo = std::move(update->photo_); + chat_info->photo_info = std::move(update->photo_); break; } case td_api::updateChatPermissions::ID: { @@ -5060,25 +5256,30 @@ void Client::on_update(object_ptr result) { } case td_api::updateUser::ID: { auto update = move_object_as(result); - add_user(users_, std::move(update->user_)); + auto *user_info = add_user_info(update->user_->id_); + add_user(user_info, std::move(update->user_)); break; } case td_api::updateUserFullInfo::ID: { auto update = move_object_as(result); auto user_id = update->user_id_; - set_user_bio(user_id, std::move(update->user_full_info_->bio_)); - set_user_has_private_forwards(user_id, update->user_full_info_->has_private_forwards_); + auto full_info = update->user_full_info_.get(); + set_user_photo(user_id, std::move(full_info->photo_)); + set_user_bio(user_id, std::move(full_info->bio_)); + set_user_has_private_forwards(user_id, full_info->has_private_forwards_); break; } case td_api::updateBasicGroup::ID: { auto update = move_object_as(result); - add_group(groups_, std::move(update->basic_group_)); + auto *group_info = add_group_info(update->basic_group_->id_); + add_group(group_info, std::move(update->basic_group_)); break; } case td_api::updateBasicGroupFullInfo::ID: { auto update = move_object_as(result); auto group_id = update->basic_group_id_; - auto full_info = std::move(update->basic_group_full_info_); + auto full_info = update->basic_group_full_info_.get(); + set_group_photo(group_id, std::move(full_info->photo_)); set_group_description(group_id, std::move(full_info->description_)); set_group_invite_link(group_id, full_info->invite_link_ != nullptr ? std::move(full_info->invite_link_->invite_link_) @@ -5087,13 +5288,15 @@ void Client::on_update(object_ptr result) { } case td_api::updateSupergroup::ID: { auto update = move_object_as(result); - add_supergroup(supergroups_, std::move(update->supergroup_)); + auto *supergroup_info = add_supergroup_info(update->supergroup_->id_); + add_supergroup(supergroup_info, std::move(update->supergroup_)); break; } case td_api::updateSupergroupFullInfo::ID: { auto update = move_object_as(result); auto supergroup_id = update->supergroup_id_; - auto full_info = std::move(update->supergroup_full_info_); + auto full_info = update->supergroup_full_info_.get(); + set_supergroup_photo(supergroup_id, std::move(full_info->photo_)); set_supergroup_description(supergroup_id, std::move(full_info->description_)); set_supergroup_invite_link(supergroup_id, full_info->invite_link_ != nullptr ? std::move(full_info->invite_link_->invite_link_) @@ -5196,6 +5399,16 @@ void Client::on_update(object_ptr result) { case td_api::updateNewChatJoinRequest::ID: add_update_chat_join_request(move_object_as(result)); break; + case td_api::updateConnectionState::ID: { + auto update = move_object_as(result); + if (update->state_->get_id() == td_api::connectionStateReady::ID) { + update_last_synchronization_error_date(); + disconnection_time_ = 0; + } else if (disconnection_time_ == 0) { + disconnection_time_ = td::Time::now(); + } + break; + } default: // we are not interested in this update break; @@ -5369,6 +5582,13 @@ td::Result> Client::get_keyboard_butt text, make_object(force_regular, force_quiz)); } + if (has_json_object_field(object, "web_app")) { + TRY_RESULT(web_app, get_json_object_field(object, "web_app", JsonValue::Type::Object, false)); + auto &web_app_object = web_app.get_object(); + TRY_RESULT(url, get_json_object_string_field(web_app_object, "url", false)); + return make_object(text, make_object(url)); + } + return make_object(text, nullptr); } if (button.type() == JsonValue::Type::String) { @@ -5465,6 +5685,13 @@ td::Result> Client::get_inline_ text, make_object(url, bot_user_id, forward_text)); } + if (has_json_object_field(object, "web_app")) { + TRY_RESULT(web_app, get_json_object_field(object, "web_app", JsonValue::Type::Object, false)); + auto &web_app_object = web_app.get_object(); + TRY_RESULT(url, get_json_object_string_field(web_app_object, "url", false)); + return make_object(text, make_object(url)); + } + return Status::Error(400, "Text buttons are unallowed in the inline keyboard"); } @@ -5971,6 +6198,22 @@ td::Result>> Clien return std::move(inline_query_results); } +td::Result> Client::get_inline_query_result(const Query *query) { + auto result_encoded = query->arg("result"); + if (result_encoded.empty()) { + return Status::Error(400, "Result isn't specified"); + } + + LOG(INFO) << "Parsing JSON object: " << result_encoded; + auto r_value = json_decode(result_encoded); + if (r_value.is_error()) { + return Status::Error(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()); +} + td::Result> Client::get_inline_query_result(td::JsonValue &&value) { if (value.type() != JsonValue::Type::Object) { return Status::Error(400, "Inline query result must be an object"); @@ -6359,6 +6602,95 @@ td::Result>> Client::get_bot_c return std::move(bot_commands); } +td::Result> Client::get_bot_menu_button(JsonValue &&value) { + if (value.type() != JsonValue::Type::Object) { + return Status::Error(400, "MenuButton must be an Object"); + } + + auto &object = value.get_object(); + + TRY_RESULT(type, get_json_object_string_field(object, "type", false)); + if (type == "default") { + return td_api::make_object("", "default"); + } + if (type == "commands") { + return nullptr; + } + if (type == "web_app") { + TRY_RESULT(text, get_json_object_string_field(object, "text", false)); + TRY_RESULT(web_app, get_json_object_field(object, "web_app", JsonValue::Type::Object, false)); + auto &web_app_object = web_app.get_object(); + TRY_RESULT(url, get_json_object_string_field(web_app_object, "url", false)); + return td_api::make_object(text, url); + } + + return Status::Error(400, "MenuButton has unsupported type"); +} + +td::Result> Client::get_bot_menu_button(const Query *query) { + auto menu_button = query->arg("menu_button"); + if (menu_button.empty()) { + return td_api::make_object("", "default"); + } + + LOG(INFO) << "Parsing JSON object: " << menu_button; + auto r_value = json_decode(menu_button); + if (r_value.is_error()) { + LOG(INFO) << "Can't parse JSON object: " << r_value.error(); + return Status::Error(400, "Can't parse menu button JSON object"); + } + + auto r_menu_button = get_bot_menu_button(r_value.move_as_ok()); + if (r_menu_button.is_error()) { + return Status::Error(400, PSLICE() << "Can't parse menu button: " << r_menu_button.error().message()); + } + return r_menu_button.move_as_ok(); +} + +td::Result> Client::get_chat_administrator_rights( + JsonValue &&value) { + if (value.type() != JsonValue::Type::Object) { + return Status::Error(400, "ChatAdministratorRights must be an Object"); + } + + auto &object = value.get_object(); + TRY_RESULT(can_manage_chat, get_json_object_bool_field(object, "can_manage_chat")); + TRY_RESULT(can_change_info, get_json_object_bool_field(object, "can_change_info")); + TRY_RESULT(can_post_messages, get_json_object_bool_field(object, "can_post_messages")); + TRY_RESULT(can_edit_messages, get_json_object_bool_field(object, "can_edit_messages")); + TRY_RESULT(can_delete_messages, get_json_object_bool_field(object, "can_delete_messages")); + TRY_RESULT(can_invite_users, get_json_object_bool_field(object, "can_invite_users")); + TRY_RESULT(can_restrict_members, get_json_object_bool_field(object, "can_restrict_members")); + TRY_RESULT(can_pin_messages, get_json_object_bool_field(object, "can_pin_messages")); + TRY_RESULT(can_promote_members, get_json_object_bool_field(object, "can_promote_members")); + TRY_RESULT(can_manage_video_chats, get_json_object_bool_field(object, "can_manage_video_chats")); + TRY_RESULT(is_anonymous, get_json_object_bool_field(object, "is_anonymous")); + return make_object( + can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members, can_manage_video_chats, is_anonymous); +} + +td::Result> Client::get_chat_administrator_rights( + const Query *query) { + auto rights = query->arg("rights"); + if (rights.empty()) { + return nullptr; + } + + LOG(INFO) << "Parsing JSON object: " << rights; + auto r_value = json_decode(rights); + if (r_value.is_error()) { + LOG(INFO) << "Can't parse JSON object: " << r_value.error(); + return Status::Error(400, "Can't parse ChatAdministratorRights JSON object"); + } + + auto r_rights = get_chat_administrator_rights(r_value.move_as_ok()); + if (r_rights.is_error()) { + return Status::Error(400, PSLICE() << "Can't parse ChatAdministratorRights: " << r_rights.error().message()); + } + return r_rights.move_as_ok(); +} + td::Result> Client::get_mask_position(JsonValue &&value) { if (value.type() != JsonValue::Type::Object) { return Status::Error(400, "MaskPosition must be an Object"); @@ -7182,7 +7514,7 @@ void Client::on_message_send_succeeded(object_ptr &&message, in auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id, &message_info->is_reply_to_message_deleted); - auto &query = pending_send_message_queries_[query_id]; + auto &query = *pending_send_message_queries_[query_id]; if (query.is_multisend) { query.messages.push_back(td::json_encode(JsonMessage(message_info, true, "sent message", this))); query.awaited_message_count--; @@ -7210,7 +7542,7 @@ void Client::on_message_send_failed(int64 chat_id, int64 old_message_id, int64 n auto error = make_object(result.code(), result.message().str()); auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id, nullptr); - auto &query = pending_send_message_queries_[query_id]; + auto &query = *pending_send_message_queries_[query_id]; if (query.is_multisend) { if (query.error == nullptr) { query.error = std::move(error); @@ -7229,7 +7561,7 @@ void Client::on_message_send_failed(int64 chat_id, int64 old_message_id, int64 n if (new_message_id != 0 && !logging_out_ && !closing_) { send_request(make_object(chat_id, td::vector{new_message_id}, false), - std::make_unique(this, chat_id, new_message_id)); + td::make_unique(this, chat_id, new_message_id)); } } @@ -7242,11 +7574,11 @@ void Client::on_cmd(PromisedQueryPtr query) { return query->set_retry_after_error(retry_after); } need_close_ = true; - return do_send_request(make_object(), std::make_unique(std::move(query))); + return do_send_request(make_object(), td::make_unique(std::move(query))); } if (query->method() == "logout") { clear_tqueue_ = true; - return do_send_request(make_object(), std::make_unique(std::move(query))); + return do_send_request(make_object(), td::make_unique(std::move(query))); } } if (waiting_for_auth_input_) { @@ -7296,7 +7628,7 @@ td::Status Client::process_get_my_commands_query(PromisedQueryPtr &query) { [this](object_ptr &&scope, PromisedQueryPtr query) mutable { auto language_code = query->arg("language_code").str(); send_request(make_object(std::move(scope), language_code), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -7312,7 +7644,7 @@ td::Status Client::process_set_my_commands_query(PromisedQueryPtr &query) { PromisedQueryPtr query) mutable { auto language_code = query->arg("language_code").str(); send_request(make_object(std::move(scope), language_code, std::move(bot_commands)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -7324,11 +7656,61 @@ td::Status Client::process_delete_my_commands_query(PromisedQueryPtr &query) { [this](object_ptr &&scope, PromisedQueryPtr query) mutable { auto language_code = query->arg("language_code").str(); send_request(make_object(std::move(scope), language_code), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } +td::Status Client::process_get_my_default_administrator_rights_query(PromisedQueryPtr &query) { + bool for_channels = to_bool(query->arg("for_channels")); + send_request(make_object(my_id_), + td::make_unique(for_channels, std::move(query))); + return Status::OK(); +} + +td::Status Client::process_set_my_default_administrator_rights_query(PromisedQueryPtr &query) { + bool for_channels = to_bool(query->arg("for_channels")); + TRY_RESULT(rights, get_chat_administrator_rights(query.get())); + + if (for_channels) { + send_request(make_object(std::move(rights)), + td::make_unique(std::move(query))); + } else { + send_request(make_object(std::move(rights)), + td::make_unique(std::move(query))); + } + return Status::OK(); +} + +td::Status Client::process_get_chat_menu_button_query(PromisedQueryPtr &query) { + if (query->has_arg("chat_id")) { + TRY_RESULT(user_id, get_user_id(query.get(), "chat_id")); + check_user(user_id, std::move(query), [this, user_id](PromisedQueryPtr query) { + send_request(make_object(user_id), + td::make_unique(std::move(query))); + }); + } else { + send_request(make_object(0), td::make_unique(std::move(query))); + } + return Status::OK(); +} + +td::Status Client::process_set_chat_menu_button_query(PromisedQueryPtr &query) { + TRY_RESULT(menu_button, get_bot_menu_button(query.get())); + if (query->has_arg("chat_id")) { + TRY_RESULT(user_id, get_user_id(query.get(), "chat_id")); + check_user(user_id, std::move(query), + [this, user_id, menu_button = std::move(menu_button)](PromisedQueryPtr query) mutable { + send_request(make_object(user_id, std::move(menu_button)), + td::make_unique(std::move(query))); + }); + } else { + send_request(make_object(0, std::move(menu_button)), + td::make_unique(std::move(query))); + } + return Status::OK(); +} + td::Status Client::process_get_user_profile_photos_query(PromisedQueryPtr &query) { TRY_RESULT(user_id, get_user_id(query.get())); int32 offset = get_integer_arg(query.get(), "offset", 0, 0); @@ -7336,7 +7718,7 @@ td::Status Client::process_get_user_profile_photos_query(PromisedQueryPtr &query check_user(user_id, std::move(query), [this, user_id, offset, limit](PromisedQueryPtr query) { send_request(make_object(user_id, offset, limit), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); return Status::OK(); } @@ -7642,7 +8024,7 @@ td::Status Client::process_stop_poll_query(PromisedQueryPtr &query) { PromisedQueryPtr query) mutable { send_request( make_object(chat_id, message_id, std::move(reply_markup)), - std::make_unique(this, chat_id, message_id, std::move(query))); + td::make_unique(this, chat_id, message_id, std::move(query))); }); }); return Status::OK(); @@ -7707,8 +8089,8 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { send_request( make_object(chat_id, 0, reply_to_message_id, get_message_send_options(disable_notification, protect_content, std::move(send_at)), - std::move(input_message_contents)), - std::make_unique(this, std::move(query))); + std::move(input_message_contents), false), + td::make_unique(this, std::move(query))); }; check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, AccessRights::Write, "replied message", std::move(query), std::move(on_success)); @@ -7726,7 +8108,7 @@ td::Status Client::process_send_chat_action_query(PromisedQueryPtr &query) { check_chat(chat_id, AccessRights::Write, std::move(query), [this, action = std::move(action)](int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, 0, std::move(action)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -7746,7 +8128,7 @@ td::Status Client::process_edit_message_text_query(PromisedQueryPtr &query) { object_ptr reply_markup, PromisedQueryPtr query) mutable { send_request(make_object(inline_message_id, std::move(reply_markup), std::move(input_message_text)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } else { resolve_reply_markup_bot_usernames( @@ -7759,7 +8141,7 @@ td::Status Client::process_edit_message_text_query(PromisedQueryPtr &query) { int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, message_id, std::move(reply_markup), std::move(input_message_text)), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); }); } @@ -7787,7 +8169,7 @@ td::Status Client::process_edit_message_live_location_query(PromisedQueryPtr &qu send_request( make_object(inline_message_id, std::move(reply_markup), std::move(location), heading, proximity_alert_radius), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } else { resolve_reply_markup_bot_usernames( @@ -7801,7 +8183,7 @@ td::Status Client::process_edit_message_live_location_query(PromisedQueryPtr &qu send_request(make_object( chat_id, message_id, std::move(reply_markup), std::move(location), heading, proximity_alert_radius), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); }); } @@ -7823,7 +8205,7 @@ td::Status Client::process_edit_message_media_query(PromisedQueryPtr &query) { object_ptr reply_markup, PromisedQueryPtr query) mutable { send_request(make_object(inline_message_id, std::move(reply_markup), std::move(input_message_content)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } else { resolve_reply_markup_bot_usernames( @@ -7836,7 +8218,7 @@ td::Status Client::process_edit_message_media_query(PromisedQueryPtr &query) { int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, message_id, std::move(reply_markup), std::move(input_message_content)), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); }); } @@ -7858,7 +8240,7 @@ td::Status Client::process_edit_message_caption_query(PromisedQueryPtr &query) { object_ptr reply_markup, PromisedQueryPtr query) mutable { send_request(make_object(inline_message_id, std::move(reply_markup), std::move(caption)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } else { resolve_reply_markup_bot_usernames( @@ -7870,7 +8252,7 @@ td::Status Client::process_edit_message_caption_query(PromisedQueryPtr &query) { int64 chat_id, int64 message_id, PromisedQueryPtr query) mutable { send_request(make_object( chat_id, message_id, std::move(reply_markup), std::move(caption)), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); }); } @@ -7891,7 +8273,7 @@ td::Status Client::process_edit_message_reply_markup_query(PromisedQueryPtr &que [this, inline_message_id = inline_message_id.str()](object_ptr reply_markup, PromisedQueryPtr query) { send_request(make_object(inline_message_id, std::move(reply_markup)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } else { resolve_reply_markup_bot_usernames( @@ -7903,7 +8285,7 @@ td::Status Client::process_edit_message_reply_markup_query(PromisedQueryPtr &que PromisedQueryPtr query) mutable { send_request( make_object(chat_id, message_id, std::move(reply_markup)), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); }); } @@ -7926,7 +8308,7 @@ td::Status Client::process_delete_message_query(PromisedQueryPtr &query) { [this](int64 chat_id, int64 message_id, PromisedQueryPtr query) { delete_message(chat_id, message_id, false); send_request(make_object(chat_id, td::vector{message_id}, true), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -7952,7 +8334,7 @@ td::Status Client::process_set_game_score_query(PromisedQueryPtr &query) { [this, inline_message_id = inline_message_id.str(), edit_message, user_id, score, force](PromisedQueryPtr query) { send_request(make_object(inline_message_id, edit_message, user_id, score, force), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } else { check_message(chat_id, message_id, false, AccessRights::Edit, "message to set game score", std::move(query), @@ -7962,7 +8344,7 @@ td::Status Client::process_set_game_score_query(PromisedQueryPtr &query) { [this, chat_id, message_id, user_id, score, force, edit_message](PromisedQueryPtr query) { send_request(make_object(chat_id, message_id, edit_message, user_id, score, force), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); }); } @@ -7980,7 +8362,7 @@ td::Status Client::process_get_game_high_scores_query(PromisedQueryPtr &query) { check_user_no_fail(user_id, std::move(query), [this, inline_message_id = inline_message_id.str(), user_id](PromisedQueryPtr query) { send_request(make_object(inline_message_id, user_id), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); } else { check_message(chat_id, message_id, false, AccessRights::Read, "message to get game high scores", std::move(query), @@ -7988,13 +8370,31 @@ td::Status Client::process_get_game_high_scores_query(PromisedQueryPtr &query) { check_user_no_fail( user_id, std::move(query), [this, chat_id, message_id, user_id](PromisedQueryPtr query) { send_request(make_object(chat_id, message_id, user_id), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); }); } return Status::OK(); } +td::Status Client::process_answer_web_app_query_query(PromisedQueryPtr &query) { + auto web_app_query_id = query->arg("web_app_query_id"); + + TRY_RESULT(result, get_inline_query_result(query.get())); + td::vector> results; + results.push_back(std::move(result)); + + resolve_inline_query_results_bot_usernames( + std::move(results), std::move(query), + [this, web_app_query_id = web_app_query_id.str()](td::vector> results, + PromisedQueryPtr query) { + CHECK(results.size() == 1); + send_request(make_object(web_app_query_id, std::move(results[0])), + td::make_unique(std::move(query))); + }); + return Status::OK(); +} + td::Status Client::process_answer_inline_query_query(PromisedQueryPtr &query) { CHECK_IS_BOT(); auto inline_query_id = td::to_integer(query->arg("inline_query_id")); @@ -8014,7 +8414,7 @@ td::Status Client::process_answer_inline_query_query(PromisedQueryPtr &query) { send_request( make_object(inline_query_id, is_personal, std::move(results), cache_time, next_offset, switch_pm_text, switch_pm_parameter), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8028,7 +8428,7 @@ td::Status Client::process_answer_callback_query_query(PromisedQueryPtr &query) int32 cache_time = get_integer_arg(query.get(), "cache_time", 0, 0, 24 * 30 * 60 * 60); send_request(make_object(callback_query_id, text, show_alert, url, cache_time), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -8045,7 +8445,7 @@ td::Status Client::process_answer_shipping_query_query(PromisedQueryPtr &query) } send_request( make_object(shipping_query_id, std::move(shipping_options), error_message.str()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -8059,7 +8459,7 @@ td::Status Client::process_answer_pre_checkout_query_query(PromisedQueryPtr &que } send_request(make_object(pre_checkout_query_id, error_message.str()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -8068,7 +8468,7 @@ td::Status Client::process_export_chat_invite_link_query(PromisedQueryPtr &query check_chat(chat_id, AccessRights::Write, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8085,7 +8485,7 @@ td::Status Client::process_create_chat_invite_link_query(PromisedQueryPtr &query PromisedQueryPtr query) { send_request(make_object(chat_id, name, expire_date, member_limit, creates_join_request), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); return Status::OK(); } @@ -8103,7 +8503,7 @@ td::Status Client::process_edit_chat_invite_link_query(PromisedQueryPtr &query) creates_join_request](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id, invite_link, name, expire_date, member_limit, creates_join_request), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); return Status::OK(); } @@ -8115,7 +8515,7 @@ td::Status Client::process_revoke_chat_invite_link_query(PromisedQueryPtr &query check_chat(chat_id, AccessRights::Write, std::move(query), [this, invite_link = invite_link.str()](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id, invite_link), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); return Status::OK(); } @@ -8129,13 +8529,13 @@ td::Status Client::process_get_chat_query(PromisedQueryPtr &query) { switch (chat_info->type) { case ChatInfo::Type::Private: return send_request(make_object(chat_info->user_id), - std::make_unique(this, chat_id, std::move(query))); + td::make_unique(this, chat_id, std::move(query))); case ChatInfo::Type::Group: return send_request(make_object(chat_info->group_id), - std::make_unique(this, chat_id, std::move(query))); + td::make_unique(this, chat_id, std::move(query))); case ChatInfo::Type::Supergroup: return send_request(make_object(chat_info->supergroup_id), - std::make_unique(this, chat_id, std::move(query))); + td::make_unique(this, chat_id, std::move(query))); case ChatInfo::Type::Unknown: default: UNREACHABLE(); @@ -8158,7 +8558,7 @@ td::Status Client::process_set_chat_photo_query(PromisedQueryPtr &query) { [this, photo = std::move(photo)](int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object( chat_id, make_object(std::move(photo))), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8168,7 +8568,7 @@ td::Status Client::process_delete_chat_photo_query(PromisedQueryPtr &query) { check_chat(chat_id, AccessRights::Write, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id, nullptr), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8180,7 +8580,7 @@ td::Status Client::process_set_chat_title_query(PromisedQueryPtr &query) { check_chat(chat_id, AccessRights::Write, std::move(query), [this, title = title.str()](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id, title), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8194,7 +8594,7 @@ td::Status Client::process_set_chat_permissions_query(PromisedQueryPtr &query) { check_chat(chat_id, AccessRights::Write, std::move(query), [this, permissions = std::move(permissions)](int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, std::move(permissions)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8206,7 +8606,7 @@ td::Status Client::process_set_chat_description_query(PromisedQueryPtr &query) { check_chat(chat_id, AccessRights::Write, std::move(query), [this, description = description.str()](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id, description), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8219,7 +8619,7 @@ td::Status Client::process_pin_chat_message_query(PromisedQueryPtr &query) { check_message(chat_id, message_id, false, AccessRights::Write, "message to pin", std::move(query), [this, disable_notification](int64 chat_id, int64 message_id, PromisedQueryPtr query) { send_request(make_object(chat_id, message_id, disable_notification, false), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8231,13 +8631,13 @@ td::Status Client::process_unpin_chat_message_query(PromisedQueryPtr &query) { if (message_id == 0) { check_chat(chat_id, AccessRights::Write, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id), - std::make_unique(this, chat_id, std::move(query))); + td::make_unique(this, chat_id, std::move(query))); }); } else { check_message(chat_id, message_id, false, AccessRights::Write, "message to unpin", std::move(query), [this](int64 chat_id, int64 message_id, PromisedQueryPtr query) { send_request(make_object(chat_id, message_id), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } return Status::OK(); @@ -8248,7 +8648,7 @@ td::Status Client::process_unpin_all_chat_messages_query(PromisedQueryPtr &query check_chat(chat_id, AccessRights::Write, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8270,7 +8670,7 @@ td::Status Client::process_set_chat_sticker_set_query(PromisedQueryPtr &query) { CHECK(chat_info->type == ChatInfo::Type::Supergroup); send_request( make_object(chat_info->supergroup_id, sticker_set_id), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); }); return Status::OK(); @@ -8288,7 +8688,7 @@ td::Status Client::process_delete_chat_sticker_set_query(PromisedQueryPtr &query CHECK(chat_info != nullptr); CHECK(chat_info->type == ChatInfo::Type::Supergroup); send_request(make_object(chat_info->supergroup_id, 0), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8319,12 +8719,12 @@ td::Status Client::process_get_chat_administrators_query(PromisedQueryPtr &query return fail_query(400, "Bad Request: there are no administrators in the private chat", std::move(query)); case ChatInfo::Type::Group: return send_request(make_object(chat_info->group_id), - std::make_unique(this, true, std::move(query))); + td::make_unique(this, true, std::move(query))); case ChatInfo::Type::Supergroup: return send_request( make_object( chat_info->supergroup_id, make_object(), 0, 100), - std::make_unique(this, get_chat_type(chat_id), std::move(query))); + td::make_unique(this, get_chat_type(chat_id), std::move(query))); case ChatInfo::Type::Unknown: default: UNREACHABLE(); @@ -8349,7 +8749,7 @@ td::Status Client::process_get_chat_member_count_query(PromisedQueryPtr &query) } case ChatInfo::Type::Supergroup: return send_request(make_object(chat_info->supergroup_id), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); case ChatInfo::Type::Unknown: default: UNREACHABLE(); @@ -8370,20 +8770,20 @@ td::Status Client::process_optimize_memory_query(PromisedQueryPtr &query) { template void Client::disable_internet_connection(PromisedQueryPtr query, OnSuccess on_success) { send_request(make_object(make_object()), - std::make_unique>(this, std::move(query), + td::make_unique>(this, std::move(query), std::move(on_success))); } void Client::enable_internet_connection(PromisedQueryPtr query) { send_request(make_object(make_object()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); } template void Client::optimize_memory(PromisedQueryPtr query, OnSuccess on_success) { /* send_request(make_object(), - std::make_unique>(this, std::move(query), std::move(on_success))); + td::make_unique>(this, std::move(query), std::move(on_success))); */ } @@ -8391,7 +8791,7 @@ td::Status Client::process_leave_chat_query(PromisedQueryPtr &query) { auto chat_id = query->arg("chat_id"); check_chat(chat_id, AccessRights::Read, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) { - send_request(make_object(chat_id), std::make_unique(std::move(query))); + send_request(make_object(chat_id), td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8408,12 +8808,14 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) { auto can_restrict_members = to_bool(query->arg("can_restrict_members")); auto can_pin_messages = to_bool(query->arg("can_pin_messages")); auto can_promote_members = to_bool(query->arg("can_promote_members")); - auto can_manage_video_chats = to_bool(query->arg("can_manage_voice_chats")); + auto can_manage_video_chats = + to_bool(query->arg("can_manage_voice_chats")) || to_bool(query->arg("can_manage_video_chats")); auto is_anonymous = to_bool(query->arg("is_anonymous")); auto status = make_object( - td::string(), true, can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, - can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, can_manage_video_chats, - is_anonymous); + td::string(), true, + make_object( + can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members, can_manage_video_chats, is_anonymous)); check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id, status = std::move(status)](int64 chat_id, PromisedQueryPtr query) mutable { auto chat_info = get_chat(chat_id); @@ -8433,10 +8835,9 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) { status->custom_title_ = std::move(administrator->custom_title_); } - send_request( - make_object( - chat_id, td_api::make_object(user_id), std::move(status)), - std::make_unique(std::move(query))); + send_request(make_object( + chat_id, make_object(user_id), std::move(status)), + td::make_unique(std::move(query))); }); }); return Status::OK(); @@ -8468,8 +8869,8 @@ td::Status Client::process_set_chat_administrator_custom_title_query(PromisedQue administrator->custom_title_ = query->arg("custom_title").str(); send_request(make_object( - chat_id, td_api::make_object(user_id), std::move(administrator)), - std::make_unique(std::move(query))); + chat_id, make_object(user_id), std::move(administrator)), + td::make_unique(std::move(query))); }); }); return Status::OK(); @@ -8481,16 +8882,16 @@ td::Status Client::process_ban_chat_member_query(PromisedQueryPtr &query) { int32 until_date = get_integer_arg(query.get(), "until_date", 0); auto revoke_messages = to_bool(query->arg("revoke_messages")); - check_chat(chat_id, AccessRights::Write, std::move(query), - [this, user_id, until_date, revoke_messages](int64 chat_id, PromisedQueryPtr query) { - check_user_no_fail(user_id, std::move(query), - [this, chat_id, user_id, until_date, revoke_messages](PromisedQueryPtr query) { - send_request(make_object( - chat_id, td_api::make_object(user_id), - until_date, revoke_messages), - std::make_unique(std::move(query))); - }); - }); + check_chat( + chat_id, AccessRights::Write, std::move(query), + [this, user_id, until_date, revoke_messages](int64 chat_id, PromisedQueryPtr query) { + check_user_no_fail( + user_id, std::move(query), [this, chat_id, user_id, until_date, revoke_messages](PromisedQueryPtr query) { + send_request(make_object(chat_id, make_object(user_id), + until_date, revoke_messages), + td::make_unique(std::move(query))); + }); + }); return Status::OK(); } @@ -8523,10 +8924,10 @@ td::Status Client::process_restrict_chat_member_query(PromisedQueryPtr &query) { } send_request(make_object( - chat_id, td_api::make_object(user_id), + chat_id, make_object(user_id), make_object( is_chat_member(chat_member->status_), until_date, std::move(permissions))), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); }); return Status::OK(); @@ -8555,16 +8956,16 @@ td::Status Client::process_unban_chat_member_query(PromisedQueryPtr &query) { } send_request(make_object( - chat_id, td_api::make_object(user_id), + chat_id, make_object(user_id), make_object()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } else { check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) { send_request(make_object( - chat_id, td_api::make_object(user_id), + chat_id, make_object(user_id), make_object()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); } }); @@ -8580,11 +8981,10 @@ td::Status Client::process_ban_chat_sender_chat_query(PromisedQueryPtr &query) { [this, sender_chat_id = sender_chat_id.str(), until_date](int64 chat_id, PromisedQueryPtr query) { check_chat_no_fail(sender_chat_id, std::move(query), [this, chat_id, until_date](int64 sender_chat_id, PromisedQueryPtr query) { - send_request( - make_object( - chat_id, td_api::make_object(sender_chat_id), - until_date, false), - std::make_unique(std::move(query))); + send_request(make_object( + chat_id, make_object(sender_chat_id), + until_date, false), + td::make_unique(std::move(query))); }); }); return Status::OK(); @@ -8596,13 +8996,13 @@ td::Status Client::process_unban_chat_sender_chat_query(PromisedQueryPtr &query) check_chat(chat_id, AccessRights::Write, std::move(query), [this, sender_chat_id = sender_chat_id.str()](int64 chat_id, PromisedQueryPtr query) { - check_chat_no_fail( - sender_chat_id, std::move(query), [this, chat_id](int64 sender_chat_id, PromisedQueryPtr query) { - send_request(make_object( - chat_id, td_api::make_object(sender_chat_id), - make_object()), - std::make_unique(std::move(query))); - }); + check_chat_no_fail(sender_chat_id, std::move(query), + [this, chat_id](int64 sender_chat_id, PromisedQueryPtr query) { + send_request(make_object( + chat_id, make_object(sender_chat_id), + make_object()), + td::make_unique(std::move(query))); + }); }); return Status::OK(); } @@ -8614,7 +9014,7 @@ td::Status Client::process_approve_chat_join_request_query(PromisedQueryPtr &que check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id](int64 chat_id, PromisedQueryPtr query) { check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) { send_request(make_object(chat_id, user_id, true), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); }); return Status::OK(); @@ -8627,7 +9027,7 @@ td::Status Client::process_decline_chat_join_request_query(PromisedQueryPtr &que check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id](int64 chat_id, PromisedQueryPtr query) { check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) { send_request(make_object(chat_id, user_id, false), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); }); return Status::OK(); @@ -8637,10 +9037,10 @@ td::Status Client::process_get_sticker_set_query(PromisedQueryPtr &query) { auto name = query->arg("name"); if (td::trim(to_lower(name)) == to_lower(GREAT_MINDS_SET_NAME)) { send_request(make_object(GREAT_MINDS_SET_ID), - std::make_unique(this, true, std::move(query))); + td::make_unique(this, true, std::move(query))); } else { send_request(make_object(name.str()), - std::make_unique(this, true, std::move(query))); + td::make_unique(this, true, std::move(query))); } return Status::OK(); } @@ -8655,7 +9055,7 @@ td::Status Client::process_upload_sticker_file_query(PromisedQueryPtr &query) { send_request(make_object( user_id, make_object(std::move(png_sticker), "", make_object())), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); return Status::OK(); } @@ -8672,7 +9072,7 @@ td::Status Client::process_create_new_sticker_set_query(PromisedQueryPtr &query) [this, user_id, title, name, stickers = std::move(stickers)](PromisedQueryPtr query) mutable { send_request(make_object(user_id, title.str(), name.str(), std::move(stickers), PSTRING() << "bot" << my_id_), - std::make_unique(this, false, std::move(query))); + td::make_unique(this, false, std::move(query))); }); return Status::OK(); } @@ -8687,7 +9087,7 @@ td::Status Client::process_add_sticker_to_set_query(PromisedQueryPtr &query) { check_user(user_id, std::move(query), [this, user_id, name, sticker = std::move(stickers[0])](PromisedQueryPtr query) mutable { send_request(make_object(user_id, name.str(), std::move(sticker)), - std::make_unique(this, false, std::move(query))); + td::make_unique(this, false, std::move(query))); }); return Status::OK(); } @@ -8700,7 +9100,7 @@ td::Status Client::process_set_sticker_set_thumb_query(PromisedQueryPtr &query) check_user(user_id, std::move(query), [this, user_id, name, thumbnail = std::move(thumbnail)](PromisedQueryPtr query) mutable { send_request(make_object(user_id, name.str(), std::move(thumbnail)), - std::make_unique(this, false, std::move(query))); + td::make_unique(this, false, std::move(query))); }); return Status::OK(); } @@ -8715,7 +9115,7 @@ td::Status Client::process_set_sticker_position_in_set_query(PromisedQueryPtr &q send_request( make_object(make_object(file_id.str()), position), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -8727,7 +9127,7 @@ td::Status Client::process_delete_sticker_from_set_query(PromisedQueryPtr &query } send_request(make_object(make_object(file_id.str())), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -8739,7 +9139,7 @@ td::Status Client::process_set_passport_data_errors_query(PromisedQueryPtr &quer check_user(user_id, std::move(query), [this, user_id, errors = std::move(passport_element_errors)](PromisedQueryPtr query) mutable { send_request(make_object(user_id, std::move(errors)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -8748,7 +9148,7 @@ td::Status Client::process_send_custom_request_query(PromisedQueryPtr &query) { TRY_RESULT(method, get_required_string_arg(query.get(), "method")); auto parameters = query->arg("parameters"); send_request(make_object(method.str(), parameters.str()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -8756,7 +9156,7 @@ td::Status Client::process_answer_custom_query_query(PromisedQueryPtr &query) { auto custom_query_id = td::to_integer(query->arg("custom_query_id")); auto data = query->arg("data"); send_request(make_object(custom_query_id, data.str()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -8850,6 +9250,7 @@ td::Status Client::process_set_webhook_query(PromisedQueryPtr &query) { } td::Status Client::process_get_webhook_info_query(PromisedQueryPtr &query) { + update_last_synchronization_error_date(); answer_query(JsonWebhookInfo(this), std::move(query)); return Status::OK(); } @@ -8893,7 +9294,7 @@ td::Status Client::process_get_chat_members_query(PromisedQueryPtr &query) { auto group_info = get_group_info(chat_info->group_id); CHECK(group_info != nullptr); return send_request(make_object(chat_info->group_id), - std::make_unique(this, false, std::move(query))); + td::make_unique(this, false, std::move(query))); } case ChatInfo::Type::Supergroup: { td_api::object_ptr filter; @@ -8918,7 +9319,7 @@ td::Status Client::process_get_chat_members_query(PromisedQueryPtr &query) { return send_request( make_object( chat_info->supergroup_id, std::move(filter), offset, limit), - std::make_unique(this, get_chat_type(chat_id), std::move(query))); + td::make_unique(this, get_chat_type(chat_id), std::move(query))); } case ChatInfo::Type::Unknown: default: @@ -8963,7 +9364,7 @@ td::Status Client::process_delete_messages_query(PromisedQueryPtr &query) { if (!ids.empty()) { send_request(make_object(chat_id, std::move(ids), true), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); } }); @@ -8976,21 +9377,21 @@ td::Status Client::process_toggle_group_invites_query(PromisedQueryPtr &query) { } td::Status Client::process_ping_query(PromisedQueryPtr &query) { - send_request(make_object(), std::make_unique(std::move(query))); + send_request(make_object(), td::make_unique(std::move(query))); return Status::OK(); } td::Status Client::process_get_memory_stats_query(PromisedQueryPtr &query) { /* send_request(make_object(), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); */ return Status::OK(); } td::Status Client::process_get_proxies_query(PromisedQueryPtr &query) { send_request(make_object(), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -9021,7 +9422,7 @@ td::Status Client::process_add_proxy_query(PromisedQueryPtr &query) { } send_request(make_object(server.str(), port, false, std::move(type)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -9032,7 +9433,7 @@ td::Status Client::process_delete_proxy_query(PromisedQueryPtr &query) { } send_request(make_object(td::to_integer(arg_pid)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } @@ -9043,13 +9444,13 @@ td::Status Client::process_enable_proxy_query(PromisedQueryPtr &query) { } send_request(make_object(td::to_integer(arg_pid)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } td::Status Client::process_disable_proxy_query(PromisedQueryPtr &query) { send_request(make_object(), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); return Status::OK(); } //end custom methods impl @@ -9058,7 +9459,7 @@ td::Status Client::process_disable_proxy_query(PromisedQueryPtr &query) { td::Status Client::process_get_chats_query(PromisedQueryPtr &query) { CHECK_IS_USER(); send_request(make_object(make_object(), 100), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); return Status::OK(); } @@ -9068,7 +9469,7 @@ td::Status Client::process_get_common_chats_query(PromisedQueryPtr &query) { td::int64 offset_chat_id = get_integer_arg(query.get(), "offset_chat_id", 0); send_request(make_object(user_id, offset_chat_id, 100), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); return Status::OK(); } @@ -9076,7 +9477,7 @@ td::Status Client::process_get_inactive_chats_query(PromisedQueryPtr &query) { CHECK_IS_USER(); send_request(make_object(), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); return Status::OK(); } @@ -9085,7 +9486,7 @@ td::Status Client::process_get_nearby_chats_query(PromisedQueryPtr &query) { TRY_RESULT(location, get_location(query.get())); send_request(make_object(std::move(location)), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); return Status::OK(); } @@ -9094,7 +9495,7 @@ td::Status Client::process_search_public_chats_query(PromisedQueryPtr &query) { auto query_ = query->arg("query"); send_request(make_object(query_.str()), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); return Status::OK(); } @@ -9107,7 +9508,7 @@ td::Status Client::process_set_poll_answer_query(PromisedQueryPtr &query) { [this, option_ids = std::move(option_ids)](int64 chat_id, PromisedQueryPtr query) mutable { auto message_id = get_message_id(query.get()); send_request(make_object(chat_id, message_id, std::move(option_ids)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); @@ -9121,11 +9522,11 @@ td::Status Client::process_join_chat_query(PromisedQueryPtr &query) { if (!chat_id.empty()) { check_chat(chat_id, AccessRights::Read, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) { send_request(make_object(chat_id), - std::make_unique(this, std::move(query), chat_id)); + td::make_unique(this, std::move(query), chat_id)); }); } else if (!invite_link.empty()) { send_request(make_object(invite_link.str()), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); } else { fail_query(400, "Bad request: Please specify chat_id or invite_link", std::move(query)); } @@ -9144,10 +9545,10 @@ td::Status Client::process_add_chat_member_query(PromisedQueryPtr &query) { if (chat->type == ChatInfo::Type::Supergroup) { std::vector user_ids{user_id}; send_request(make_object(chat_id, std::move(user_ids)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); } else if (chat->type == ChatInfo::Type::Group) { send_request(make_object(chat_id, user_id, 0), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); } }); return Status::OK(); @@ -9164,7 +9565,7 @@ td::Status Client::process_report_chat_query(PromisedQueryPtr &query) { PromisedQueryPtr query) mutable { send_request(make_object(chat_id, std::move(message_ids), std::move(reason), reason->get_id() == td_api::chatReportReasonCustom::ID ? query->arg("reason").str() : td::string()), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -9177,14 +9578,14 @@ td::Status Client::process_create_chat_query(PromisedQueryPtr &query) { if (chat_type == "supergroup") { send_request(make_object(title.str(), false, description.str(), nullptr, false), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); } else if (chat_type == "channel") { send_request(make_object(title.str(), true, description.str(), nullptr, false), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); } else if (chat_type == "group") { TRY_RESULT(initial_members, get_int_array_arg(query.get(), "user_ids")) send_request(make_object(std::move(initial_members), title.str()), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); } else { return Status::Error(400, "Chat type is not specified"); } @@ -9203,7 +9604,7 @@ td::Status Client::process_search_messages_query(PromisedQueryPtr &query) { send_request(make_object(nullptr, query_.str(), offset_date, offset_chat_id, offset_message_id, 100, std::move(filter), min_date, max_date), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); return Status::OK(); } @@ -9224,7 +9625,7 @@ td::Status Client::process_search_chat_messages_query(PromisedQueryPtr &query) { int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, query_.str(), std::move(sender), from_message_id, 0, 100, std::move(filter), 0), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); return Status::OK(); } @@ -9238,7 +9639,7 @@ td::Status Client::process_get_callback_query_answer_query(PromisedQueryPtr &que check_chat(chat_id, AccessRights::Read, std::move(query), [this, message_id, payload = std::move(payload)](int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, message_id, std::move(payload)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -9252,7 +9653,7 @@ td::Status Client::process_delete_chat_history_query(PromisedQueryPtr &query) { check_chat(chat_id, AccessRights::Read, std::move(query), [this, for_everyone, remove_from_chat_list](int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, for_everyone, remove_from_chat_list), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -9262,7 +9663,7 @@ td::Status Client::process_get_scheduled_messages_query(PromisedQueryPtr &query) auto chat_id = query->arg("chat_id"); check_chat(chat_id, AccessRights::Read, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }); return Status::OK(); } @@ -9275,7 +9676,7 @@ td::Status Client::process_edit_message_scheduling_query(PromisedQueryPtr &query check_chat(chat_id, AccessRights::Read, std::move(query), [this, message_id, send_at = std::move(send_at)](int64 chat_id, PromisedQueryPtr query) mutable { send_request(make_object(chat_id, message_id, std::move(send_at)), - std::make_unique(std::move(query))); + td::make_unique(std::move(query))); }); return Status::OK(); } @@ -9298,7 +9699,7 @@ void Client::process_auth_phone_number_query(PromisedQueryPtr &query) { return fail_query(400, "Bad Request: currently not waiting for a phone_number", std::move(query)); } send_request(make_object(td::to_string(phone_number), nullptr), - std::make_unique(this, std::move(query), true)); + td::make_unique(this, std::move(query), true)); } void Client::process_authcode_query(PromisedQueryPtr &query) { @@ -9310,7 +9711,7 @@ void Client::process_authcode_query(PromisedQueryPtr &query) { return fail_query(400, "Bad Request: currently not waiting for a code", std::move(query)); } send_request(make_object(code.str()), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); } void Client::process_2fapassword_query(PromisedQueryPtr &query) { @@ -9322,7 +9723,7 @@ void Client::process_2fapassword_query(PromisedQueryPtr &query) { return fail_query(400, "Bad Request: currently not waiting for a password", std::move(query)); } send_request(make_object(password.str()), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); } void Client::process_register_user_query(PromisedQueryPtr &query) { @@ -9335,7 +9736,7 @@ void Client::process_register_user_query(PromisedQueryPtr &query) { return fail_query(400, "Bad Request: currently not waiting for registration", std::move(query)); } send_request(make_object(first_name.str(), last_name.str()), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); } //end custom auth methods impl @@ -9358,7 +9759,7 @@ void Client::do_get_file(object_ptr file, PromisedQueryPtr query) } send_request(make_object(file_id, 1, 0, 0, false), - std::make_unique(this, file_id)); + td::make_unique(this, file_id)); } bool Client::is_file_being_downloaded(int32 file_id) const { @@ -9421,7 +9822,7 @@ void Client::save_webhook() const { void Client::webhook_success() { next_bot_updates_warning_time_ = td::Time::now() + BOT_UPDATES_WARNING_DELAY; if (was_bot_updates_warning_) { - send_request(make_object(0, ""), std::make_unique()); + send_request(make_object(0, ""), td::make_unique()); was_bot_updates_warning_ = false; } } @@ -9435,7 +9836,7 @@ void Client::webhook_error(Status status) { if (pending_update_count >= MIN_PENDING_UPDATES_WARNING && td::Time::now() > next_bot_updates_warning_time_) { send_request(make_object(td::narrow_cast(pending_update_count), "Webhook error. " + last_webhook_error_.message().str()), - std::make_unique()); + td::make_unique()); next_bot_updates_warning_time_ = td::Time::now_cached() + BOT_UPDATES_WARNING_DELAY; was_bot_updates_warning_ = true; } @@ -9584,7 +9985,7 @@ void Client::do_send_message(object_ptr input_messa send_request(make_object(chat_id, 0, reply_to_message_id, get_message_send_options(disable_notification, protect_content, std::move(send_at)), std::move(reply_markup), std::move(input_message_content)), - std::make_unique(this, std::move(query))); + td::make_unique(this, std::move(query))); }; check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, AccessRights::Write, "replied message", std::move(query), std::move(on_success)); @@ -9594,8 +9995,10 @@ void Client::do_send_message(object_ptr input_messa td::int64 Client::get_send_message_query_id(PromisedQueryPtr query, bool is_multisend) { auto query_id = current_send_message_query_id_++; auto &pending_query = pending_send_message_queries_[query_id]; - pending_query.query = std::move(query); - pending_query.is_multisend = is_multisend; + CHECK(pending_query == nullptr); + pending_query = td::make_unique(); + pending_query->query = std::move(query); + pending_query->is_multisend = is_multisend; return query_id; } @@ -9617,7 +10020,7 @@ void Client::on_sent_message(object_ptr &&message, int64 query_ auto emplace_result = yet_unsent_messages_.emplace(yet_unsent_message_id, yet_unsent_message); CHECK(emplace_result.second); yet_unsent_message_count_[chat_id]++; - pending_send_message_queries_[query_id].awaited_message_count++; + pending_send_message_queries_[query_id]->awaited_message_count++; } void Client::abort_long_poll(bool from_set_webhook) { @@ -9750,7 +10153,7 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu previous_get_updates_finish_time_ = td::Time::now(); next_bot_updates_warning_time_ = td::Time::now() + BOT_UPDATES_WARNING_DELAY; if (total_size == updates.size() && was_bot_updates_warning_) { - send_request(make_object(0, ""), std::make_unique()); + send_request(make_object(0, ""), td::make_unique()); was_bot_updates_warning_ = false; } answer_query(JsonUpdates(updates), std::move(query)); @@ -9762,7 +10165,7 @@ void Client::long_poll_wakeup(bool force_flag) { if (pending_update_count >= MIN_PENDING_UPDATES_WARNING && td::Time::now() > next_bot_updates_warning_time_) { send_request(make_object(td::narrow_cast(pending_update_count), "The getUpdates method is not called for too long"), - std::make_unique()); + td::make_unique()); next_bot_updates_warning_time_ = td::Time::now_cached() + BOT_UPDATES_WARNING_DELAY; // do not send warnings too often was_bot_updates_warning_ = true; @@ -9783,8 +10186,7 @@ void Client::long_poll_wakeup(bool force_flag) { } } -void Client::add_user(std::unordered_map &users, object_ptr &&user) { - auto user_info = &users[user->id_]; +void Client::add_user(UserInfo *user_info, object_ptr &&user) { user_info->first_name = std::move(user->first_name_); user_info->last_name = std::move(user->last_name_); user_info->username = std::move(user->username_); @@ -9821,23 +10223,35 @@ void Client::add_user(std::unordered_map &users, object_ptrsecond; + if (emplace_result.second) { + user_info = td::make_unique(); + } else { + CHECK(user_info != nullptr); + } + return user_info.get(); +} + const Client::UserInfo *Client::get_user_info(int64 user_id) const { auto it = users_.find(user_id); - return it == users_.end() ? nullptr : &it->second; + return it == users_.end() ? nullptr : it->second.get(); +} + +void Client::set_user_photo(int64 user_id, object_ptr &&photo) { + add_user_info(user_id)->photo = std::move(photo); } void Client::set_user_bio(int64 user_id, td::string &&bio) { - auto user_info = &users_[user_id]; - user_info->bio = std::move(bio); + add_user_info(user_id)->bio = std::move(bio); } void Client::set_user_has_private_forwards(int64 user_id, bool has_private_forwards) { - auto user_info = &users_[user_id]; - user_info->has_private_forwards = has_private_forwards; + add_user_info(user_id)->has_private_forwards = has_private_forwards; } -void Client::add_group(std::unordered_map &groups, object_ptr &&group) { - auto group_info = &groups[group->id_]; +void Client::add_group(GroupInfo *group_info, object_ptr &&group) { group_info->member_count = group->member_count_; group_info->left = group->status_->get_id() == td_api::chatMemberStatusLeft::ID; group_info->kicked = group->status_->get_id() == td_api::chatMemberStatusBanned::ID; @@ -9848,24 +10262,35 @@ void Client::add_group(std::unordered_map &groups, object_ptr< } } +Client::GroupInfo *Client::add_group_info(int64 group_id) { + auto emplace_result = groups_.emplace(group_id, nullptr); + auto &group_info = emplace_result.first->second; + if (emplace_result.second) { + group_info = td::make_unique(); + } else { + CHECK(group_info != nullptr); + } + return group_info.get(); +} + const Client::GroupInfo *Client::get_group_info(int64 group_id) const { auto it = groups_.find(group_id); - return it == groups_.end() ? nullptr : &it->second; + return it == groups_.end() ? nullptr : it->second.get(); +} + +void Client::set_group_photo(int64 group_id, object_ptr &&photo) { + add_group_info(group_id)->photo = std::move(photo); } void Client::set_group_description(int64 group_id, td::string &&descripton) { - auto group_info = &groups_[group_id]; - group_info->description = std::move(descripton); + add_group_info(group_id)->description = std::move(descripton); } void Client::set_group_invite_link(int64 group_id, td::string &&invite_link) { - auto group_info = &groups_[group_id]; - group_info->invite_link = std::move(invite_link); + add_group_info(group_id)->invite_link = std::move(invite_link); } -void Client::add_supergroup(std::unordered_map &supergroups, - object_ptr &&supergroup) { - auto supergroup_info = &supergroups[supergroup->id_]; +void Client::add_supergroup(SupergroupInfo *supergroup_info, object_ptr &&supergroup) { supergroup_info->username = std::move(supergroup->username_); supergroup_info->date = supergroup->date_; supergroup_info->status = std::move(supergroup->status_); @@ -9878,49 +10303,64 @@ void Client::add_supergroup(std::unordered_map &supergrou // end custom properties } +void Client::set_supergroup_photo(int64 supergroup_id, object_ptr &&photo) { + add_supergroup_info(supergroup_id)->photo = std::move(photo); +} + void Client::set_supergroup_description(int64 supergroup_id, td::string &&descripton) { - auto supergroup_info = &supergroups_[supergroup_id]; - supergroup_info->description = std::move(descripton); + add_supergroup_info(supergroup_id)->description = std::move(descripton); } void Client::set_supergroup_invite_link(int64 supergroup_id, td::string &&invite_link) { - auto supergroup_info = &supergroups_[supergroup_id]; - supergroup_info->invite_link = std::move(invite_link); + add_supergroup_info(supergroup_id)->invite_link = std::move(invite_link); } void Client::set_supergroup_sticker_set_id(int64 supergroup_id, int64 sticker_set_id) { - auto supergroup_info = &supergroups_[supergroup_id]; - supergroup_info->sticker_set_id = sticker_set_id; + add_supergroup_info(supergroup_id)->sticker_set_id = sticker_set_id; } void Client::set_supergroup_can_set_sticker_set(int64 supergroup_id, bool can_set_sticker_set) { - auto supergroup_info = &supergroups_[supergroup_id]; - supergroup_info->can_set_sticker_set = can_set_sticker_set; + add_supergroup_info(supergroup_id)->can_set_sticker_set = can_set_sticker_set; } void Client::set_supergroup_slow_mode_delay(int64 supergroup_id, int32 slow_mode_delay) { - auto supergroup_info = &supergroups_[supergroup_id]; - supergroup_info->slow_mode_delay = slow_mode_delay; + add_supergroup_info(supergroup_id)->slow_mode_delay = slow_mode_delay; } void Client::set_supergroup_linked_chat_id(int64 supergroup_id, int64 linked_chat_id) { - auto supergroup_info = &supergroups_[supergroup_id]; - supergroup_info->linked_chat_id = linked_chat_id; + add_supergroup_info(supergroup_id)->linked_chat_id = linked_chat_id; } void Client::set_supergroup_location(int64 supergroup_id, object_ptr location) { - auto supergroup_info = &supergroups_[supergroup_id]; - supergroup_info->location = std::move(location); + add_supergroup_info(supergroup_id)->location = std::move(location); +} + +Client::SupergroupInfo *Client::add_supergroup_info(int64 supergroup_id) { + auto emplace_result = supergroups_.emplace(supergroup_id, nullptr); + auto &supergroup_info = emplace_result.first->second; + if (emplace_result.second) { + supergroup_info = td::make_unique(); + } else { + CHECK(supergroup_info != nullptr); + } + return supergroup_info.get(); } const Client::SupergroupInfo *Client::get_supergroup_info(int64 supergroup_id) const { auto it = supergroups_.find(supergroup_id); - return it == supergroups_.end() ? nullptr : &it->second; + return it == supergroups_.end() ? nullptr : it->second.get(); } Client::ChatInfo *Client::add_chat(int64 chat_id) { LOG(DEBUG) << "Update chat " << chat_id; - return &chats_[chat_id]; + auto emplace_result = chats_.emplace(chat_id, nullptr); + auto &chat_info = emplace_result.first->second; + if (emplace_result.second) { + chat_info = td::make_unique(); + } else { + CHECK(chat_info != nullptr); + } + return chat_info.get(); } const Client::ChatInfo *Client::get_chat(int64 chat_id) const { @@ -9928,7 +10368,7 @@ const Client::ChatInfo *Client::get_chat(int64 chat_id) const { if (it == chats_.end()) { return nullptr; } - return &it->second; + return it->second.get(); } Client::ChatType Client::get_chat_type(int64 chat_id) const { @@ -10058,6 +10498,25 @@ void Client::json_store_callback_query_payload(td::JsonObjectScope &object, } } +void Client::json_store_administrator_rights(td::JsonObjectScope &object, const td_api::chatAdministratorRights *rights, + ChatType chat_type) { + object("can_manage_chat", td::JsonBool(rights->can_manage_chat_)); + object("can_change_info", td::JsonBool(rights->can_change_info_)); + if (chat_type == ChatType::Channel) { + object("can_post_messages", td::JsonBool(rights->can_post_messages_)); + object("can_edit_messages", td::JsonBool(rights->can_edit_messages_)); + } + object("can_delete_messages", td::JsonBool(rights->can_delete_messages_)); + object("can_invite_users", td::JsonBool(rights->can_invite_users_)); + object("can_restrict_members", td::JsonBool(rights->can_restrict_members_)); + if (chat_type == ChatType::Group || chat_type == ChatType::Supergroup) { + object("can_pin_messages", td::JsonBool(rights->can_pin_messages_)); + } + object("can_promote_members", td::JsonBool(rights->can_promote_members_)); + object("can_manage_video_chats", td::JsonBool(rights->can_manage_video_chats_)); + object("is_anonymous", td::JsonBool(rights->is_anonymous_)); +} + void Client::json_store_permissions(td::JsonObjectScope &object, const td_api::chatPermissions *permissions) { object("can_send_messages", td::JsonBool(permissions->can_send_messages_)); object("can_send_media_messages", td::JsonBool(permissions->can_send_media_messages_)); @@ -10162,7 +10621,7 @@ bool Client::update_allowed_update_types(const Query *query) { value = make_object(allowed_update_types); } send_request(make_object("xallowed_update_types", std::move(value)), - std::make_unique()); + td::make_unique()); return true; } return false; @@ -10188,6 +10647,9 @@ void Client::add_update(UpdateType update_type, const T &update, int32 timeout, void Client::add_update_impl(UpdateType update_type, const td::VirtuallyJsonable &update, int32 timeout, int64 webhook_queue_id) { + update_last_synchronization_error_date(); + last_update_creation_time_ = td::Time::now(); + if (((allowed_update_types_ >> static_cast(update_type)) & 1) == 0) { return; } @@ -10230,10 +10692,7 @@ void Client::add_new_message(object_ptr &&message, bool is_edit } auto chat_id = message->chat_id_; - if (chat_id == 0) { - LOG(ERROR) << "Receive invalid chat in " << to_string(message); - return; - } + CHECK(chat_id != 0); new_message_queues_[chat_id].queue_.emplace(std::move(message), is_edited); process_new_message_queue(chat_id); } @@ -10267,10 +10726,7 @@ void Client::add_new_chosen_inline_result(int64 sender_user_id, object_ptr &&query) { CHECK(query != nullptr); auto user_id = query->sender_user_id_; - if (user_id == 0) { - LOG(ERROR) << "Receive invalid sender in " << to_string(query); - return; - } + CHECK(user_id != 0); new_callback_query_queues_[user_id].queue_.push(std::move(query)); process_new_callback_query_queue(user_id, 0); } @@ -10296,7 +10752,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { // get the message from the server queue.has_active_request_ = true; return send_request(make_object(chat_id, message_id, query->id_), - std::make_unique(this, user_id, state)); + td::make_unique(this, user_id, state)); } state = 1; } @@ -10306,7 +10762,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id) == nullptr) { queue.has_active_request_ = true; return send_request(make_object(chat_id, message_id), - std::make_unique(this, user_id, state)); + td::make_unique(this, user_id, state)); } state = 2; } @@ -10315,7 +10771,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { if (!have_sticker_set_name(message_sticker_set_id)) { queue.has_active_request_ = true; return send_request(make_object(message_sticker_set_id), - std::make_unique(this, message_sticker_set_id, user_id, 0)); + td::make_unique(this, message_sticker_set_id, user_id, 0)); } auto reply_to_message_id = message_info == nullptr || message_info->is_reply_to_message_deleted ? 0 : message_info->reply_to_message_id; @@ -10326,7 +10782,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { if (!have_sticker_set_name(reply_sticker_set_id)) { queue.has_active_request_ = true; return send_request(make_object(reply_sticker_set_id), - std::make_unique(this, reply_sticker_set_id, user_id, 0)); + td::make_unique(this, reply_sticker_set_id, user_id, 0)); } } } @@ -10473,24 +10929,24 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptrcontent_->get_id()) { case td_api::messagePhoto::ID: { - auto message_photo = static_cast(message->content_.get()); - if (message_photo->photo_ == nullptr) { + auto content = static_cast(message->content_.get()); + if (content->photo_ == nullptr) { LOG(ERROR) << "Got empty messagePhoto"; return true; } break; } case td_api::messageChatAddMembers::ID: { - auto message_add_members = static_cast(message->content_.get()); - if (message_add_members->member_user_ids_.empty()) { + auto content = static_cast(message->content_.get()); + if (content->member_user_ids_.empty()) { LOG(ERROR) << "Got empty messageChatAddMembers"; return true; } break; } case td_api::messageChatChangePhoto::ID: { - auto message_change_photo = static_cast(message->content_.get()); - if (message_change_photo->photo_ == nullptr) { + auto content = static_cast(message->content_.get()); + if (content->photo_ == nullptr) { LOG(ERROR) << "Got empty messageChatChangePhoto"; return true; } @@ -10504,8 +10960,8 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr(message->content_.get()); - auto pinned_message_id = message_pin_message->message_id_; + auto content = static_cast(message->content_.get()); + auto pinned_message_id = content->message_id_; if (pinned_message_id <= 0) { return true; } @@ -10517,10 +10973,9 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr(message->content_.get()); - return proximity_alert_triggered->traveler_id_->get_id() != td_api::messageSenderUser::ID || - proximity_alert_triggered->watcher_id_->get_id() != td_api::messageSenderUser::ID; + auto content = static_cast(message->content_.get()); + return content->traveler_id_->get_id() != td_api::messageSenderUser::ID || + content->watcher_id_->get_id() != td_api::messageSenderUser::ID; } case td_api::messageGameScore::ID: return true; @@ -10542,6 +10997,8 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr(rhs->type_.get()); return lhs_type->user_id_ == rhs_type->user_id_; } + case td_api::inlineKeyboardButtonTypeWebApp::ID: { + auto lhs_type = static_cast(lhs->type_.get()); + auto rhs_type = static_cast(rhs->type_.get()); + return lhs_type->url_ == rhs_type->url_; + } default: UNREACHABLE(); return false; @@ -10729,13 +11191,13 @@ void Client::process_new_message_queue(int64 chat_id) { if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id) == nullptr) { queue.has_active_request_ = true; return send_request(make_object(chat_id, message_id), - std::make_unique(this, chat_id)); + td::make_unique(this, chat_id)); } auto message_sticker_set_id = get_sticker_set_id(message_ref->content_); if (!have_sticker_set_name(message_sticker_set_id)) { queue.has_active_request_ = true; return send_request(make_object(message_sticker_set_id), - std::make_unique(this, message_sticker_set_id, 0, chat_id)); + td::make_unique(this, message_sticker_set_id, 0, chat_id)); } if (reply_to_message_id > 0) { auto reply_to_message_info = get_message(chat_id, reply_to_message_id); @@ -10744,7 +11206,7 @@ void Client::process_new_message_queue(int64 chat_id) { if (!have_sticker_set_name(reply_sticker_set_id)) { queue.has_active_request_ = true; return send_request(make_object(reply_sticker_set_id), - std::make_unique(this, reply_sticker_set_id, 0, chat_id)); + td::make_unique(this, reply_sticker_set_id, 0, chat_id)); } } @@ -10862,10 +11324,10 @@ Client::FullMessageId Client::add_message(object_ptr &&message, int64 message_id = message->id_; LOG(DEBUG) << "Add message " << message_id << " to chat " << chat_id; - std::unique_ptr message_info; + td::unique_ptr message_info; auto it = messages_.find({chat_id, message_id}); if (it == messages_.end()) { - message_info = std::make_unique(); + message_info = td::make_unique(); } else { message_info = std::move(it->second); } @@ -10982,7 +11444,7 @@ Client::FullMessageId Client::add_message(object_ptr &&message, auto sticker_set_id = get_sticker_set_id(message_info->content); if (!have_sticker_set_name(sticker_set_id)) { send_request(make_object(sticker_set_id), - std::make_unique(this, sticker_set_id, 0, 0)); + td::make_unique(this, sticker_set_id, 0, 0)); } } set_message_reply_markup(message_info.get(), std::move(message->reply_markup_)); @@ -11186,6 +11648,6 @@ constexpr Client::Slice Client::BOT_ONLY_ERROR_DESCRIPTION; constexpr int Client::USER_ONLY_ERROR_CODE; constexpr Client::Slice Client::USER_ONLY_ERROR_DESCRIPTION; -std::unordered_map Client::methods_; +td::FlatHashMap Client::methods_; } // namespace telegram_bot_api diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 6d4e7f2..8a149ac 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -19,6 +19,8 @@ #include "td/utils/common.h" #include "td/utils/Container.h" +#include "td/utils/FlatHashMap.h" +#include "td/utils/FlatHashSet.h" #include "td/utils/JsonBuilder.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" @@ -27,8 +29,6 @@ #include #include #include -#include -#include namespace telegram_bot_api { @@ -125,6 +125,7 @@ class Client final : public WebhookActor::Callback { class JsonEntity; class JsonVectorEntities; class JsonCallbackGame; + class JsonWebAppInfo; class JsonInlineKeyboardButton; class JsonInlineKeyboard; class JsonReplyMarkup; @@ -139,6 +140,8 @@ class Client final : public WebhookActor::Callback { class JsonShippingQuery; class JsonPreCheckoutQuery; class JsonBotCommand; + class JsonBotMenuButton; + class JsonChatAdministratorRights; class JsonChatPhotos; class JsonChatMember; class JsonChatMembers; @@ -151,6 +154,7 @@ class Client final : public WebhookActor::Callback { class JsonEncryptedPassportElement; class JsonEncryptedCredentials; class JsonPassportData; + class JsonWebAppData; class JsonProximityAlertTriggered; class JsonVideoChatScheduled; class JsonVideoChatStarted; @@ -160,6 +164,7 @@ class Client final : public WebhookActor::Callback { class JsonUpdateTypes; class JsonWebhookInfo; class JsonStickerSet; + class JsonSentWebAppMessage; class JsonCustomJson; //start custom Json objects @@ -188,7 +193,9 @@ class Client final : public WebhookActor::Callback { class TdOnGetEditedMessageCallback; class TdOnGetCallbackQueryMessageCallback; class TdOnGetStickerSetCallback; + class TdOnGetMenuButtonCallback; class TdOnGetMyCommandsCallback; + class TdOnGetMyDefaultAdministratorRightsCallback; class TdOnGetChatFullInfoCallback; class TdOnGetChatStickerSetCallback; class TdOnGetChatPinnedMessageCallback; @@ -199,6 +206,7 @@ class Client final : public WebhookActor::Callback { class TdOnReplacePrimaryChatInviteLinkCallback; class TdOnGetChatInviteLinkCallback; class TdOnGetGameHighScoresCallback; + class TdOnAnswerWebAppQueryCallback; class TdOnReturnFileCallback; class TdOnReturnStickerSetCallback; class TdOnDownloadFileCallback; @@ -323,8 +331,8 @@ class Client final : public WebhookActor::Callback { template void get_chat_member(int64 chat_id, int64 user_id, PromisedQueryPtr query, OnSuccess on_success); - void send_request(object_ptr &&f, std::unique_ptr handler); - void do_send_request(object_ptr &&f, std::unique_ptr handler); + void send_request(object_ptr &&f, td::unique_ptr handler); + void do_send_request(object_ptr &&f, td::unique_ptr handler); static object_ptr execute(object_ptr &&f); void on_update(object_ptr result); void on_result(td::uint64 id, object_ptr result); @@ -378,6 +386,8 @@ class Client final : public WebhookActor::Callback { object_ptr get_input_thumbnail(const Query *query, Slice field_name) const; + td::Result> get_inline_query_result(const Query *query); + td::Result> get_inline_query_result(td::JsonValue &&value); td::Result>> get_inline_query_results(const Query *query); @@ -403,6 +413,14 @@ class Client final : public WebhookActor::Callback { static td::Result>> get_bot_commands(const Query *query); + static td::Result> get_bot_menu_button(const Query *query); + + static td::Result> get_bot_menu_button(td::JsonValue &&value); + + static td::Result> get_chat_administrator_rights(td::JsonValue &&value); + + static td::Result> get_chat_administrator_rights(const Query *query); + static td::Result> get_mask_position(const Query *query, Slice field_name); static td::Result> get_mask_position(td::JsonValue &&value); @@ -506,6 +524,10 @@ class Client final : public WebhookActor::Callback { Status process_get_my_commands_query(PromisedQueryPtr &query); Status process_set_my_commands_query(PromisedQueryPtr &query); Status process_delete_my_commands_query(PromisedQueryPtr &query); + Status process_get_my_default_administrator_rights_query(PromisedQueryPtr &query); + Status process_set_my_default_administrator_rights_query(PromisedQueryPtr &query); + Status process_get_chat_menu_button_query(PromisedQueryPtr &query); + Status process_set_chat_menu_button_query(PromisedQueryPtr &query); Status process_get_user_profile_photos_query(PromisedQueryPtr &query); Status process_send_message_query(PromisedQueryPtr &query); Status process_send_animation_query(PromisedQueryPtr &query); @@ -536,6 +558,7 @@ class Client final : public WebhookActor::Callback { Status process_delete_message_query(PromisedQueryPtr &query); Status process_set_game_score_query(PromisedQueryPtr &query); Status process_get_game_high_scores_query(PromisedQueryPtr &query); + Status process_answer_web_app_query_query(PromisedQueryPtr &query); Status process_answer_inline_query_query(PromisedQueryPtr &query); Status process_answer_callback_query_query(PromisedQueryPtr &query); Status process_answer_shipping_query_query(PromisedQueryPtr &query); @@ -682,6 +705,7 @@ class Client final : public WebhookActor::Callback { td::string username; td::string language_code; + object_ptr photo; td::string bio; // start custom properties @@ -695,12 +719,15 @@ class Client final : public WebhookActor::Callback { bool is_inline_bot = false; bool has_private_forwards = false; }; - static void add_user(std::unordered_map &users, object_ptr &&user); + static void add_user(UserInfo *user_info, object_ptr &&user); + void set_user_photo(int64 user_id, object_ptr &&photo); void set_user_bio(int64 user_id, td::string &&bio); void set_user_has_private_forwards(int64 user_id, bool has_private_forwards); + UserInfo *add_user_info(int64 user_id); const UserInfo *get_user_info(int64 user_id) const; struct GroupInfo { + object_ptr photo; td::string description; td::string invite_link; int32 member_count = 0; @@ -709,13 +736,16 @@ class Client final : public WebhookActor::Callback { bool is_active = false; int64 upgraded_to_supergroup_id = 0; }; - static void add_group(std::unordered_map &groups, object_ptr &&group); + static void add_group(GroupInfo *group_info, object_ptr &&group); + void set_group_photo(int64 group_id, object_ptr &&photo); void set_group_description(int64 group_id, td::string &&descripton); void set_group_invite_link(int64 group_id, td::string &&invite_link); + GroupInfo *add_group_info(int64 group_id); const GroupInfo *get_group_info(int64 group_id) const; struct SupergroupInfo { td::string username; + object_ptr photo; td::string description; td::string invite_link; int64 sticker_set_id = 0; @@ -733,8 +763,8 @@ class Client final : public WebhookActor::Callback { bool is_scam = false; // end custom properties }; - static void add_supergroup(std::unordered_map &supergroups, - object_ptr &&supergroup); + static void add_supergroup(SupergroupInfo *supergroup_info, object_ptr &&supergroup); + void set_supergroup_photo(int64 supergroup_id, object_ptr &&photo); void set_supergroup_description(int64 supergroup_id, td::string &&descripton); void set_supergroup_invite_link(int64 supergroup_id, td::string &&invite_link); void set_supergroup_sticker_set_id(int64 supergroup_id, int64 sticker_set_id); @@ -742,6 +772,7 @@ class Client final : public WebhookActor::Callback { void set_supergroup_slow_mode_delay(int64 supergroup_id, int32 slow_mode_delay); void set_supergroup_linked_chat_id(int64 supergroup_id, int64 linked_chat_id); void set_supergroup_location(int64 supergroup_id, object_ptr location); + SupergroupInfo *add_supergroup_info(int64 supergroup_id); const SupergroupInfo *get_supergroup_info(int64 supergroup_id) const; struct ChatInfo { @@ -750,7 +781,7 @@ class Client final : public WebhookActor::Callback { td::string title; int32 message_auto_delete_time = 0; bool has_protected_content = false; - object_ptr photo; + object_ptr photo_info; object_ptr permissions; union { int64 user_id; @@ -834,6 +865,9 @@ class Client final : public WebhookActor::Callback { static void json_store_callback_query_payload(td::JsonObjectScope &object, const td_api::CallbackQueryPayload *payload); + static void json_store_administrator_rights(td::JsonObjectScope &object, + const td_api::chatAdministratorRights *rights, ChatType chat_type); + static void json_store_permissions(td::JsonObjectScope &object, const td_api::chatPermissions *permissions); void remove_replies_to_message(int64 chat_id, int64 reply_to_message_id, bool only_from_cache); @@ -947,6 +981,8 @@ class Client final : public WebhookActor::Callback { std::size_t get_pending_update_count() const; + void update_last_synchronization_error_date(); + static bool is_chat_member(const object_ptr &status); static td::string get_chat_member_status(const object_ptr &status); @@ -986,30 +1022,30 @@ class Client final : public WebhookActor::Callback { int64 channel_bot_user_id_ = 0; int64 service_notifications_user_id_ = 0; - static std::unordered_map methods_; + static td::FlatHashMap methods_; - std::unordered_map, FullMessageIdHash> messages_; // message cache - std::unordered_map users_; // user info cache - std::unordered_map groups_; // group info cache - std::unordered_map supergroups_; // supergroup info cache - std::unordered_map chats_; // chat info cache + td::FlatHashMap, FullMessageIdHash> messages_; // message cache + td::FlatHashMap> users_; // user info cache + td::FlatHashMap> groups_; // group info cache + td::FlatHashMap> supergroups_; // supergroup info cache + td::FlatHashMap> chats_; // chat info cache - std::unordered_map, FullMessageIdHash> + td::FlatHashMap, FullMessageIdHash> reply_message_ids_; // message -> replies to it - std::unordered_map, FullMessageIdHash> + td::FlatHashMap, FullMessageIdHash> yet_unsent_reply_message_ids_; // message -> replies to it - std::unordered_map> file_download_listeners_; - std::unordered_set download_started_file_ids_; + td::FlatHashMap> file_download_listeners_; + td::FlatHashSet download_started_file_ids_; struct YetUnsentMessage { int64 reply_to_message_id = 0; bool is_reply_to_message_deleted = false; int64 send_message_query_id = 0; }; - std::unordered_map yet_unsent_messages_; + td::FlatHashMap yet_unsent_messages_; - std::unordered_map yet_unsent_message_count_; + td::FlatHashMap yet_unsent_message_count_; // chat_id -> count struct PendingSendMessageQuery { PromisedQueryPtr query; @@ -1018,7 +1054,7 @@ class Client final : public WebhookActor::Callback { td::vector messages; object_ptr error; }; - std::unordered_map + td::FlatHashMap> pending_send_message_queries_; // query_id -> PendingSendMessageQuery int64 current_send_message_query_id_ = 1; @@ -1034,28 +1070,28 @@ class Client final : public WebhookActor::Callback { std::queue queue_; bool has_active_request_ = false; }; - std::unordered_map new_message_queues_; // chat_id -> queue + td::FlatHashMap new_message_queues_; // chat_id -> queue struct NewCallbackQueryQueue { std::queue> queue_; bool has_active_request_ = false; }; - std::unordered_map new_callback_query_queues_; // sender_user_id -> queue + td::FlatHashMap new_callback_query_queues_; // sender_user_id -> queue - std::unordered_map sticker_set_names_; + td::FlatHashMap sticker_set_names_; int64 cur_temp_bot_user_id_ = 1; - std::unordered_map bot_user_ids_; - std::unordered_set unresolved_bot_usernames_; - std::unordered_map temp_to_real_bot_user_id_; - std::unordered_map> awaiting_bot_resolve_queries_; + td::FlatHashMap bot_user_ids_; + td::FlatHashSet unresolved_bot_usernames_; + td::FlatHashMap temp_to_real_bot_user_id_; + td::FlatHashMap> awaiting_bot_resolve_queries_; struct PendingBotResolveQuery { std::size_t pending_resolve_count = 0; PromisedQueryPtr query; td::Promise on_success; }; - std::unordered_map pending_bot_resolve_queries_; + td::FlatHashMap pending_bot_resolve_queries_; int64 current_bot_resolve_query_id_ = 1; td::string dir_; @@ -1063,11 +1099,11 @@ class Client final : public WebhookActor::Callback { td::ActorContext context_; std::queue cmd_queue_; td::vector> pending_updates_; - td::Container> handlers_; + td::Container> handlers_; static constexpr int32 LONG_POLL_MAX_TIMEOUT = 50; - static constexpr double LONG_POLL_MAX_DELAY = 0.01; - static constexpr double LONG_POLL_WAIT_AFTER = 0.002; + static constexpr double LONG_POLL_MAX_DELAY = 0.002; + static constexpr double LONG_POLL_WAIT_AFTER = 0.001; int32 long_poll_limit_ = 0; int32 long_poll_offset_ = 0; bool long_poll_was_wakeup_ = false; @@ -1100,6 +1136,10 @@ class Client final : public WebhookActor::Callback { double local_unix_time_difference_ = 0; // Unix time - now() + double disconnection_time_ = 0; // the time when Connection state changed from "Ready", or 0 if it is "Ready" + double last_update_creation_time_ = 0; // the time when the last update was added + int32 last_synchronization_error_date_ = 0; // the date of the last connection error + int32 previous_get_updates_offset_ = -1; double previous_get_updates_start_time_ = 0; double previous_get_updates_finish_time_ = 0; diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 34f21a9..6bf7900 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -76,9 +76,13 @@ void ClientManager::send(PromisedQueryPtr query) { return fail_query(401, "Unauthorized: invalid token specified", std::move(query)); } auto r_user_id = td::to_integer_safe(query->token().substr(0, token.find(':'))); - if (r_user_id.is_error() || r_user_id.ok() < 0 || !token_range_(r_user_id.ok())) { + if (r_user_id.is_error() || !token_range_(r_user_id.ok())) { return fail_query(421, "Misdirected Request: unallowed token specified", std::move(query)); } + auto user_id = r_user_id.ok(); + if (user_id <= 0 || user_id >= (static_cast(1) << 54)) { + return fail_query(401, "Unauthorized: invalid token specified", std::move(query)); + } if (query->is_test_dc()) { token += "/test"; @@ -532,7 +536,7 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool args.emplace_back(add_string("url"), add_string(parser.read_all())); const auto method = add_string("setwebhook"); - auto query = std::make_unique(std::move(containers), token, is_user, is_test_dc, method, std::move(args), + auto query = td::make_unique(std::move(containers), token, is_user, is_test_dc, method, std::move(args), td::vector>(), td::vector(), std::move(shared_data), td::IPAddress(), true); return PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor>())); @@ -542,6 +546,7 @@ void ClientManager::raw_event(const td::Event::Raw &event) { auto id = get_link_token(); auto *info = clients_.get(id); CHECK(info != nullptr); + CHECK(info->tqueue_id_ != 0); auto &value = active_client_count_[info->tqueue_id_]; if (event.ptr != nullptr) { value++; diff --git a/telegram-bot-api/ClientManager.h b/telegram-bot-api/ClientManager.h index 93e58f3..ec1771e 100644 --- a/telegram-bot-api/ClientManager.h +++ b/telegram-bot-api/ClientManager.h @@ -16,11 +16,11 @@ #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/Container.h" +#include "td/utils/FlatHashMap.h" #include "td/utils/FloodControlFast.h" #include "td/utils/Slice.h" #include -#include #include namespace telegram_bot_api { @@ -64,9 +64,9 @@ class ClientManager final : public td::Actor { std::shared_ptr parameters_; TokenRange token_range_; - std::unordered_map token_to_id_; - std::unordered_map flood_controls_; - std::unordered_map active_client_count_; + td::FlatHashMap token_to_id_; + td::FlatHashMap flood_controls_; + td::FlatHashMap active_client_count_; bool close_flag_ = false; td::vector> close_promises_; diff --git a/telegram-bot-api/HttpConnection.cpp b/telegram-bot-api/HttpConnection.cpp index 499cabf..73265b1 100644 --- a/telegram-bot-api/HttpConnection.cpp +++ b/telegram-bot-api/HttpConnection.cpp @@ -59,7 +59,7 @@ void HttpConnection::handle(td::unique_ptr http_query, } - auto query = std::make_unique(std::move(http_query->container_), token, is_user, is_test_dc, method, + auto query = td::make_unique(std::move(http_query->container_), token, is_user, is_test_dc, method, std::move(http_query->args_), std::move(http_query->headers_), std::move(http_query->files_), shared_data_, http_query->peer_address_, false); diff --git a/telegram-bot-api/Query.cpp b/telegram-bot-api/Query.cpp index f9b9714..875c1a0 100644 --- a/telegram-bot-api/Query.cpp +++ b/telegram-bot-api/Query.cpp @@ -21,7 +21,7 @@ namespace telegram_bot_api { -std::unordered_map> empty_parameters; +td::FlatHashMap> empty_parameters; Query::Query(td::vector &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method, td::vector> &&args, @@ -97,8 +97,8 @@ void Query::set_error(int http_status_code, td::BufferSlice result) { void Query::set_retry_after_error(int retry_after) { retry_after_ = retry_after; - std::unordered_map> parameters; - parameters.emplace("retry_after", std::make_unique(retry_after)); + td::FlatHashMap> parameters; + parameters.emplace("retry_after", td::make_unique(retry_after)); set_error(429, td::json_encode( JsonQueryError(429, PSLICE() << "Too Many Requests: retry after " << retry_after, parameters))); } diff --git a/telegram-bot-api/Query.h b/telegram-bot-api/Query.h index 3461c09..43aecbb 100644 --- a/telegram-bot-api/Query.h +++ b/telegram-bot-api/Query.h @@ -15,6 +15,7 @@ #include "td/utils/buffer.h" #include "td/utils/common.h" +#include "td/utils/FlatHashMap.h" #include "td/utils/JsonBuilder.h" #include "td/utils/List.h" #include "td/utils/port/IPAddress.h" @@ -23,7 +24,6 @@ #include #include -#include #include namespace telegram_bot_api { @@ -168,11 +168,11 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query); // fix for outdated C++14 libraries // https://stackoverflow.com/questions/26947704/implicit-conversion-failure-from-initializer-list -extern std::unordered_map> empty_parameters; +extern td::FlatHashMap> empty_parameters; class JsonParameters final : public td::Jsonable { public: - explicit JsonParameters(const std::unordered_map> ¶meters) + explicit JsonParameters(const td::FlatHashMap> ¶meters) : parameters_(parameters) { } void store(td::JsonValueScope *scope) const { @@ -184,7 +184,7 @@ class JsonParameters final : public td::Jsonable { } private: - const std::unordered_map> ¶meters_; + const td::FlatHashMap> ¶meters_; }; template @@ -210,7 +210,7 @@ class JsonQueryError final : public td::Jsonable { public: JsonQueryError( int error_code, td::Slice description, - const std::unordered_map> ¶meters = empty_parameters) + const td::FlatHashMap> ¶meters = empty_parameters) : error_code_(error_code), description_(description), parameters_(parameters) { } void store(td::JsonValueScope *scope) const { @@ -226,7 +226,7 @@ class JsonQueryError final : public td::Jsonable { private: int error_code_; td::Slice description_; - const std::unordered_map> ¶meters_; + const td::FlatHashMap> ¶meters_; }; class PromiseDeleter { @@ -265,7 +265,7 @@ void answer_query(const Jsonable &result, PromisedQueryPtr query, td::Slice desc inline void fail_query( int http_status_code, td::Slice description, PromisedQueryPtr query, - const std::unordered_map> ¶meters = empty_parameters) { + const td::FlatHashMap> ¶meters = empty_parameters) { query->set_error(http_status_code, td::json_encode(JsonQueryError(http_status_code, description, parameters))); query.reset(); // send query into promise explicitly diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index 72c7a96..97a2f79 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -379,11 +379,14 @@ void WebhookActor::load_updates() { for (auto &update : updates) { VLOG(webhook) << "Load update " << update.id; - if (update_map_.find(update.id) != update_map_.end()) { - LOG(ERROR) << "Receive duplicated event " << update.id << " from tqueue"; + CHECK(update.id.is_valid()); + auto &dest_ptr = update_map_[update.id]; + if (dest_ptr != nullptr) { + LOG(ERROR) << "Receive duplicated event " << update.id << " from TQueue"; continue; } - auto &dest = update_map_[update.id]; + dest_ptr = td::make_unique(); + auto &dest = *dest_ptr; dest.id_ = update.id; dest.json_ = update.data.str(); dest.delay_ = 1; @@ -437,7 +440,7 @@ void WebhookActor::load_updates() { void WebhookActor::drop_event(td::TQueue::EventId event_id) { auto it = update_map_.find(event_id); CHECK(it != update_map_.end()); - auto queue_id = it->second.queue_id_; + auto queue_id = it->second->queue_id_; update_map_.erase(it); auto queue_updates_it = queue_updates_.find(queue_id); @@ -448,8 +451,10 @@ void WebhookActor::drop_event(td::TQueue::EventId event_id) { if (queue_updates_it->second.event_ids.empty()) { queue_updates_.erase(queue_updates_it); } else { - auto &update = update_map_[queue_updates_it->second.event_ids.front()]; - queues_.emplace(update.wakeup_at_, update.queue_id_); + auto update_id = queue_updates_it->second.event_ids.front(); + CHECK(update_id.is_valid()); + auto &update = update_map_[update_id]; + queues_.emplace(update->wakeup_at_, update->queue_id_); } parameters_->shared_data_->tqueue_->forget(tqueue_id_, event_id); @@ -462,7 +467,7 @@ void WebhookActor::on_update_ok(td::TQueue::EventId event_id) { auto it = update_map_.find(event_id); CHECK(it != update_map_.end()); - VLOG(webhook) << "Receive ok for update " << event_id << " in " << (last_success_time_ - it->second.last_send_time_) + VLOG(webhook) << "Receive ok for update " << event_id << " in " << (last_success_time_ - it->second->last_send_time_) << " seconds"; drop_event(event_id); @@ -474,21 +479,22 @@ void WebhookActor::on_update_error(td::TQueue::EventId event_id, td::Slice error auto it = update_map_.find(event_id); CHECK(it != update_map_.end()); + CHECK(it->second != nullptr); + auto &update = *it->second; const int MAX_RETRY_AFTER = 3600; retry_after = td::clamp(retry_after, 0, MAX_RETRY_AFTER); - int next_delay = it->second.delay_; + int next_delay = update.delay_; int next_effective_delay = retry_after; - if (retry_after == 0 && it->second.fail_count_ > 0) { + if (retry_after == 0 && update.fail_count_ > 0) { next_delay = td::min(WEBHOOK_MAX_RESEND_TIMEOUT, next_delay * 2); next_effective_delay = next_delay; } - if (parameters_->shared_data_->get_unix_time(now) + next_effective_delay > it->second.expires_at_) { + if (parameters_->shared_data_->get_unix_time(now) + next_effective_delay > update.expires_at_) { LOG(WARNING) << "Drop update " << event_id << ": " << error; drop_event(event_id); return; } - auto &update = it->second; update.delay_ = next_delay; update.wakeup_at_ = now + next_effective_delay; update.fail_count_++; @@ -514,10 +520,15 @@ td::Status WebhookActor::send_update() { } auto queue_id = it->id; + CHECK(queue_id != 0); queues_.erase(it); auto event_id = queue_updates_[queue_id].event_ids.front(); + CHECK(event_id.is_valid()); - auto &update = update_map_[event_id]; + auto update_map_it = update_map_.find(event_id); + CHECK(update_map_it != update_map_.end()); + CHECK(update_map_it->second != nullptr); + auto &update = *update_map_it->second; update.last_send_time_ = now; auto body = td::json_encode(JsonUpdate(update.id_.value(), update.json_)); @@ -586,7 +597,7 @@ void WebhookActor::handle(td::unique_ptr response) { if (!method.empty() && method != "deletewebhook" && method != "setwebhook" && method != "close" && method != "logout" && !td::begins_with(method, "get")) { VLOG(webhook) << "Receive request " << method << " in response to webhook"; - auto query = std::make_unique(std::move(response->container_), td::MutableSlice(), false, false, + auto query = td::make_unique(std::move(response->container_), td::MutableSlice(), false, false, td::MutableSlice(), std::move(response->args_), std::move(response->headers_), std::move(response->files_), parameters_->shared_data_, response->peer_address_, false); diff --git a/telegram-bot-api/WebhookActor.h b/telegram-bot-api/WebhookActor.h index 6067d73..71d702e 100644 --- a/telegram-bot-api/WebhookActor.h +++ b/telegram-bot-api/WebhookActor.h @@ -20,6 +20,7 @@ #include "td/utils/BufferedFd.h" #include "td/utils/common.h" #include "td/utils/Container.h" +#include "td/utils/FlatHashMap.h" #include "td/utils/FloodControlFast.h" #include "td/utils/HttpUrl.h" #include "td/utils/JsonBuilder.h" @@ -35,7 +36,6 @@ #include #include #include -#include namespace telegram_bot_api { @@ -127,8 +127,8 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback { return std::hash()(event_id.value()); } }; - std::unordered_map update_map_; - std::unordered_map queue_updates_; + td::FlatHashMap, EventIdHash> update_map_; + td::FlatHashMap queue_updates_; std::set queues_; td::int64 unique_queue_id_ = static_cast(1) << 60; diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index dc127d9..438f812 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -200,7 +200,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared(); auto parameters = std::make_unique(); - parameters->version_ = "5.7"; + parameters->version_ = "6.0.1"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats(); @@ -230,11 +230,11 @@ int main(int argc, char *argv[]) { } return 0; }(std::getenv("TELEGRAM_API_ID")); - parameters->api_hash_ = [](auto x) -> std::string { + parameters->api_hash_ = [](auto x) -> td::string { if (x) { return x; } - return std::string(); + return td::string(); }(std::getenv("TELEGRAM_API_HASH")); options.set_usage(td::Slice(argv[0]), "--api-id= --api-hash= [--local] [OPTION]...");