diff --git a/CMakeLists.txt b/CMakeLists.txt index b52e30d..994a060 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 6.9.2 LANGUAGES CXX) +project(TelegramBotApi VERSION 7.0 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 9184b3e..27c3eae 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 9184b3e62de59663a59d3500528aee7e5f0d83fa +Subproject commit 27c3eaeb4964bd5f18d8488e354abde1a4383e49 diff --git a/tdlight-api-openapi.yaml b/tdlight-api-openapi.yaml index 61cb5ac..b438f32 100644 --- a/tdlight-api-openapi.yaml +++ b/tdlight-api-openapi.yaml @@ -544,102 +544,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /deleteMessages: - post: - tags: - - added - description: |- - Delete all the messages with message_id in range between start and end. - The start parameter MUST be less than the end parameter - Both start and end must be positive non zero numbers - The method will always return true as a result, even if the messages cannot be deleted - This method does not work on private chat or normal groups It is not suggested to delete more than 200 messages per call. - - *NOTE* - The maximum number of messages to be deleted in a single batch is determined by the max-batch-operations parameter and is 10000 by default. - requestBody: - content: - application/x-www-form-urlencoded: - schema: - type: object - properties: - chat_id: - description: Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) - anyOf: - - type: integer - - type: string - start: - description: First message id to delete - type: integer - end: - description: Last message id to delete - type: integer - required: - - chat_id - - start - - end - multipart/form-data: - schema: - type: object - properties: - chat_id: - description: Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) - anyOf: - - type: integer - - type: string - start: - description: First message id to delete - type: integer - end: - description: Last message id to delete - type: integer - required: - - chat_id - - start - - end - application/json: - schema: - type: object - properties: - chat_id: - description: Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) - anyOf: - - type: integer - - type: string - start: - description: First message id to delete - type: integer - end: - description: Last message id to delete - type: integer - required: - - chat_id - - start - - end - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - ok: - default: true - type: boolean - result: - default: true - type: boolean - required: - - ok - - result - default: - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Error' /ping: post: tags: diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 291458b..da3fb92 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -251,9 +251,12 @@ bool Client::init_methods() { methods_.emplace("sendpoll", &Client::process_send_poll_query); methods_.emplace("stoppoll", &Client::process_stop_poll_query); methods_.emplace("copymessage", &Client::process_copy_message_query); + methods_.emplace("copymessages", &Client::process_copy_messages_query); methods_.emplace("forwardmessage", &Client::process_forward_message_query); + methods_.emplace("forwardmessages", &Client::process_forward_messages_query); methods_.emplace("sendmediagroup", &Client::process_send_media_group_query); methods_.emplace("sendchataction", &Client::process_send_chat_action_query); + methods_.emplace("setmessagereaction", &Client::process_set_message_reaction_query); methods_.emplace("editmessagetext", &Client::process_edit_message_text_query); methods_.emplace("editmessagelivelocation", &Client::process_edit_message_live_location_query); methods_.emplace("stopmessagelivelocation", &Client::process_edit_message_live_location_query); @@ -261,6 +264,7 @@ bool Client::init_methods() { methods_.emplace("editmessagecaption", &Client::process_edit_message_caption_query); methods_.emplace("editmessagereplymarkup", &Client::process_edit_message_reply_markup_query); methods_.emplace("deletemessage", &Client::process_delete_message_query); + methods_.emplace("deletemessages", &Client::process_delete_messages_query); methods_.emplace("createinvoicelink", &Client::process_create_invoice_link_query); methods_.emplace("setgamescore", &Client::process_set_game_score_query); methods_.emplace("getgamehighscores", &Client::process_get_game_high_scores_query); @@ -312,6 +316,7 @@ bool Client::init_methods() { methods_.emplace("unbanchatsenderchat", &Client::process_unban_chat_sender_chat_query); methods_.emplace("approvechatjoinrequest", &Client::process_approve_chat_join_request_query); methods_.emplace("declinechatjoinrequest", &Client::process_decline_chat_join_request_query); + methods_.emplace("getuserchatboosts", &Client::process_get_user_chat_boosts_query); methods_.emplace("getstickerset", &Client::process_get_sticker_set_query); methods_.emplace("getcustomemojistickers", &Client::process_get_custom_emoji_stickers_query); methods_.emplace("uploadstickerfile", &Client::process_upload_sticker_file_query); @@ -340,7 +345,6 @@ bool Client::init_methods() { methods_.emplace("getmessageinfo", &Client::process_get_message_info_query); methods_.emplace("getparticipants", &Client::process_get_chat_members_query); methods_.emplace("getchatmembers", &Client::process_get_chat_members_query); - methods_.emplace("deletemessages", &Client::process_delete_messages_query); methods_.emplace("togglegroupinvites", &Client::process_toggle_group_invites_query); methods_.emplace("ping", &Client::process_ping_query); methods_.emplace("getmemorystats", &Client::process_get_memory_stats_query); @@ -580,6 +584,9 @@ class Client::JsonEntity final : public td::Jsonable { object("custom_emoji_id", td::to_string(entity->custom_emoji_id_)); break; } + case td_api::textEntityTypeBlockQuote::ID: + object("type", "blockquote"); + break; default: UNREACHABLE(); } @@ -600,8 +607,7 @@ class Client::JsonVectorEntities final : public td::Jsonable { for (auto &entity : entities_) { auto entity_type = entity->type_->get_id(); if (entity_type != td_api::textEntityTypeBankCardNumber::ID && - entity_type != td_api::textEntityTypeMediaTimestamp::ID && - entity_type != td_api::textEntityTypeBlockQuote::ID) { + entity_type != td_api::textEntityTypeMediaTimestamp::ID) { array << JsonEntity(entity.get(), client_); } } @@ -648,6 +654,46 @@ class Client::JsonLocation final : public td::Jsonable { int32 proximity_alert_radius_; }; +class Client::JsonReactionType final : public td::Jsonable { + public: + explicit JsonReactionType(const td_api::ReactionType *reaction_type) : reaction_type_(reaction_type) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + CHECK(reaction_type_ != nullptr); + switch (reaction_type_->get_id()) { + case td_api::reactionTypeEmoji::ID: + object("type", "emoji"); + object("emoji", static_cast(reaction_type_)->emoji_); + break; + case td_api::reactionTypeCustomEmoji::ID: + object("type", "custom_emoji"); + object("custom_emoji_id", + td::to_string(static_cast(reaction_type_)->custom_emoji_id_)); + break; + default: + UNREACHABLE(); + } + } + + private: + const td_api::ReactionType *reaction_type_; +}; + +class Client::JsonReactionCount final : public td::Jsonable { + public: + explicit JsonReactionCount(const td_api::messageReaction *message_reaction) : message_reaction_(message_reaction) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("type", JsonReactionType(message_reaction_->type_.get())); + object("total_count", message_reaction_->total_count_); + } + + private: + const td_api::messageReaction *message_reaction_; +}; + class Client::JsonChatPermissions final : public td::Jsonable { public: explicit JsonChatPermissions(const td_api::chatPermissions *chat_permissions) : chat_permissions_(chat_permissions) { @@ -755,10 +801,10 @@ class Client::JsonMessage final : public td::Jsonable { class Client::JsonChat final : public td::Jsonable { public: - JsonChat(int64 chat_id, bool is_full, const Client *client, int64 pinned_message_id = -1, int32 distance = -1) + JsonChat(int64 chat_id, const Client *client, bool is_full = false, int64 pinned_message_id = -1, int32 distance = -1) : chat_id_(chat_id) - , is_full_(is_full) , client_(client) + , is_full_(is_full) , pinned_message_id_(pinned_message_id) , distance_(distance) { } @@ -797,12 +843,6 @@ class Client::JsonChat final : public td::Jsonable { object("active_usernames", td::json_array(user_info->active_usernames, [](td::Slice username) { return td::JsonString(username); })); } - if (user_info->emoji_status_custom_emoji_id != 0) { - object("emoji_status_custom_emoji_id", td::to_string(user_info->emoji_status_custom_emoji_id)); - if (user_info->emoji_status_expiration_date != 0) { - object("emoji_status_expiration_date", user_info->emoji_status_expiration_date); - } - } if (!user_info->bio.empty()) { object("bio", user_info->bio); } @@ -892,6 +932,9 @@ class Client::JsonChat final : public td::Jsonable { if (supergroup_info->can_set_sticker_set) { object("can_set_sticker_set", td::JsonTrue()); } + if (supergroup_info->is_all_history_available) { + object("has_visible_history", td::JsonTrue()); + } if (supergroup_info->is_supergroup) { object("permissions", JsonChatPermissions(chat_info->permissions.get())); } @@ -966,6 +1009,28 @@ class Client::JsonChat final : public td::Jsonable { if (chat_info->message_auto_delete_time != 0) { object("message_auto_delete_time", chat_info->message_auto_delete_time); } + if (chat_info->emoji_status_custom_emoji_id != 0) { + object("emoji_status_custom_emoji_id", td::to_string(chat_info->emoji_status_custom_emoji_id)); + if (chat_info->emoji_status_expiration_date != 0) { + object("emoji_status_expiration_date", chat_info->emoji_status_expiration_date); + } + } + if (chat_info->available_reactions != nullptr) { + object("available_reactions", + td::json_array(chat_info->available_reactions->reactions_, + [](const auto &reaction) { return JsonReactionType(reaction.get()); })); + } + CHECK(chat_info->accent_color_id != -1); + object("accent_color_id", chat_info->accent_color_id); + if (chat_info->background_custom_emoji_id != 0) { + object("background_custom_emoji_id", td::to_string(chat_info->background_custom_emoji_id)); + } + if (chat_info->profile_accent_color_id != -1) { + object("profile_accent_color_id", chat_info->profile_accent_color_id); + } + if (chat_info->profile_background_custom_emoji_id != 0) { + object("profile_background_custom_emoji_id", td::to_string(chat_info->profile_background_custom_emoji_id)); + } if (chat_info->has_protected_content) { object("has_protected_content", td::JsonTrue()); } @@ -981,12 +1046,30 @@ class Client::JsonChat final : public td::Jsonable { private: int64 chat_id_; - bool is_full_; const Client *client_; + bool is_full_; int64 pinned_message_id_; int32 distance_; }; +class Client::JsonInaccessibleMessage final : public td::Jsonable { + public: + JsonInaccessibleMessage(int64 chat_id, int64 message_id, const Client *client) + : chat_id_(chat_id), message_id_(message_id), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("message_id", as_client_message_id(message_id_)); + object("chat", JsonChat(chat_id_, client_)); + object("date", 0); + } + + private: + int64 chat_id_; + int64 message_id_; + const Client *client_; +}; + class Client::JsonMessageSender final : public td::Jsonable { public: JsonMessageSender(const td_api::MessageSender *sender_id, const Client *client) @@ -1002,7 +1085,7 @@ class Client::JsonMessageSender final : public td::Jsonable { } case td_api::messageSenderChat::ID: { auto sender_chat_id = static_cast(sender_id_)->chat_id_; - JsonChat(sender_chat_id, false, client_).store(scope); + JsonChat(sender_chat_id, client_).store(scope); break; } default: @@ -1015,6 +1098,59 @@ class Client::JsonMessageSender final : public td::Jsonable { const Client *client_; }; +class Client::JsonMessageOrigin final : public td::Jsonable { + public: + JsonMessageOrigin(const td_api::MessageOrigin *message_origin, int32 initial_send_date, const Client *client) + : message_origin_(message_origin), initial_send_date_(initial_send_date), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + switch (message_origin_->get_id()) { + case td_api::messageOriginUser::ID: { + auto origin = static_cast(message_origin_); + object("type", "user"); + object("sender_user", JsonUser(origin->sender_user_id_, client_)); + break; + } + case td_api::messageOriginChat::ID: { + auto origin = static_cast(message_origin_); + object("type", "chat"); + object("sender_chat", JsonChat(origin->sender_chat_id_, client_)); + if (!origin->author_signature_.empty()) { + object("author_signature", origin->author_signature_); + } + break; + } + case td_api::messageOriginHiddenUser::ID: { + auto origin = static_cast(message_origin_); + object("type", "hidden_user"); + if (!origin->sender_name_.empty()) { + object("sender_user_name", origin->sender_name_); + } + break; + } + case td_api::messageOriginChannel::ID: { + auto origin = static_cast(message_origin_); + object("type", "channel"); + object("chat", JsonChat(origin->chat_id_, client_)); + object("message_id", as_client_message_id(origin->message_id_)); + if (!origin->author_signature_.empty()) { + object("author_signature", origin->author_signature_); + } + break; + } + default: + UNREACHABLE(); + } + object("date", initial_send_date_); + } + + private: + const td_api::MessageOrigin *message_origin_; + int32 initial_send_date_; + const Client *client_; +}; + class Client::JsonMessages final : public td::Jsonable { public: explicit JsonMessages(const td::vector &messages) : messages_(messages) { @@ -1030,6 +1166,35 @@ class Client::JsonMessages final : public td::Jsonable { const td::vector &messages_; }; +class Client::JsonLinkPreviewOptions final : public td::Jsonable { + public: + JsonLinkPreviewOptions(const td_api::linkPreviewOptions *link_preview_options, const Client *client) + : link_preview_options_(link_preview_options), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + if (link_preview_options_->is_disabled_) { + object("is_disabled", td::JsonTrue()); + } + if (!link_preview_options_->url_.empty()) { + object("url", link_preview_options_->url_); + } + if (link_preview_options_->force_small_media_) { + object("prefer_small_media", td::JsonTrue()); + } + if (link_preview_options_->force_large_media_) { + object("prefer_large_media", td::JsonTrue()); + } + if (link_preview_options_->show_above_text_) { + object("show_above_text", td::JsonTrue()); + } + } + + private: + const td_api::linkPreviewOptions *link_preview_options_; + const Client *client_; +}; + class Client::JsonAnimation final : public td::Jsonable { public: JsonAnimation(const td_api::animation *animation, bool as_document, const Client *client) @@ -1524,7 +1689,7 @@ class Client::JsonPollAnswer final : public td::Jsonable { case td_api::messageSenderChat::ID: { auto voter_chat_id = static_cast(poll_answer_->voter_id_.get())->chat_id_; object("user", JsonUser(client_->channel_bot_user_id_, client_)); - object("voter_chat", JsonChat(voter_chat_id, false, client_)); + object("voter_chat", JsonChat(voter_chat_id, client_)); break; } default: @@ -1888,16 +2053,30 @@ class Client::JsonWriteAccessAllowed final : public td::Jsonable { class Client::JsonUserShared final : public td::Jsonable { public: - explicit JsonUserShared(const td_api::messageUserShared *user_shared) : user_shared_(user_shared) { + explicit JsonUserShared(const td_api::messageUsersShared *users_shared) : users_shared_(users_shared) { } void store(td::JsonValueScope *scope) const { auto object = scope->enter_object(); - object("user_id", user_shared_->user_id_); - object("request_id", user_shared_->button_id_); + object("user_id", users_shared_->user_ids_[0]); + object("request_id", users_shared_->button_id_); } private: - const td_api::messageUserShared *user_shared_; + const td_api::messageUsersShared *users_shared_; +}; + +class Client::JsonUsersShared final : public td::Jsonable { + public: + explicit JsonUsersShared(const td_api::messageUsersShared *users_shared) : users_shared_(users_shared) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("user_ids", td::json_array(users_shared_->user_ids_, [](int64 user_id) { return user_id; })); + object("request_id", users_shared_->button_id_); + } + + private: + const td_api::messageUsersShared *users_shared_; }; class Client::JsonChatShared final : public td::Jsonable { @@ -1914,6 +2093,106 @@ class Client::JsonChatShared final : public td::Jsonable { const td_api::messageChatShared *chat_shared_; }; +class Client::JsonGiveaway final : public td::Jsonable { + public: + JsonGiveaway(const td_api::messagePremiumGiveaway *giveaway, const Client *client) + : giveaway_(giveaway), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + td::vector chat_ids; + chat_ids.push_back(giveaway_->parameters_->boosted_chat_id_); + for (auto chat_id : giveaway_->parameters_->additional_chat_ids_) { + chat_ids.push_back(chat_id); + } + object("chats", td::json_array(chat_ids, [client = client_](auto &chat_id) { return JsonChat(chat_id, client); })); + object("winners_selection_date", giveaway_->parameters_->winners_selection_date_); + object("winner_count", giveaway_->winner_count_); + if (giveaway_->parameters_->only_new_members_) { + object("only_new_members", td::JsonTrue()); + } + if (giveaway_->parameters_->has_public_winners_) { + object("has_public_winners", td::JsonTrue()); + } + if (!giveaway_->parameters_->country_codes_.empty()) { + object("country_codes", td::json_array(giveaway_->parameters_->country_codes_, + [](td::Slice country_code) { return td::JsonString(country_code); })); + } + if (!giveaway_->parameters_->prize_description_.empty()) { + object("prize_description", giveaway_->parameters_->prize_description_); + } + if (giveaway_->month_count_ > 0) { + object("premium_subscription_month_count", giveaway_->month_count_); + } + } + + private: + const td_api::messagePremiumGiveaway *giveaway_; + const Client *client_; +}; + +class Client::JsonGiveawayWinners final : public td::Jsonable { + public: + JsonGiveawayWinners(const td_api::messagePremiumGiveawayWinners *giveaway_winners, const Client *client) + : giveaway_winners_(giveaway_winners), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("chat", JsonChat(giveaway_winners_->boosted_chat_id_, client_)); + object("giveaway_message_id", as_client_message_id(giveaway_winners_->giveaway_message_id_)); + if (giveaway_winners_->additional_chat_count_ > 0) { + object("additional_chat_count", giveaway_winners_->additional_chat_count_); + } + object("winners_selection_date", giveaway_winners_->actual_winners_selection_date_); + if (giveaway_winners_->only_new_members_) { + object("only_new_members", td::JsonTrue()); + } + if (giveaway_winners_->was_refunded_) { + object("was_refunded", td::JsonTrue()); + } + if (giveaway_winners_->month_count_ > 0) { + object("premium_subscription_month_count", giveaway_winners_->month_count_); + } + if (!giveaway_winners_->prize_description_.empty()) { + object("prize_description", giveaway_winners_->prize_description_); + } + object("winner_count", giveaway_winners_->winner_count_); + if (giveaway_winners_->unclaimed_prize_count_ > 0) { + object("unclaimed_prize_count", giveaway_winners_->unclaimed_prize_count_); + } + object("winners", JsonUsers(giveaway_winners_->winner_user_ids_, client_)); + } + + private: + const td_api::messagePremiumGiveawayWinners *giveaway_winners_; + const Client *client_; +}; + +class Client::JsonGiveawayCompleted final : public td::Jsonable { + public: + JsonGiveawayCompleted(const td_api::messagePremiumGiveawayCompleted *giveaway_completed, int64 chat_id, + const Client *client) + : giveaway_completed_(giveaway_completed), chat_id_(chat_id), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("winner_count", giveaway_completed_->winner_count_); + if (giveaway_completed_->unclaimed_prize_count_ > 0) { + object("unclaimed_prize_count", giveaway_completed_->unclaimed_prize_count_); + } + const MessageInfo *giveaway_message = + client_->get_message(chat_id_, giveaway_completed_->giveaway_message_id_, true); + if (giveaway_message != nullptr) { + object("giveaway_message", JsonMessage(giveaway_message, true, "giveaway completed", client_)); + } + } + + private: + const td_api::messagePremiumGiveawayCompleted *giveaway_completed_; + int64 chat_id_; + const Client *client_; +}; + class Client::JsonWebAppInfo final : public td::Jsonable { public: explicit JsonWebAppInfo(const td::string &url) : url_(url) { @@ -2039,6 +2318,162 @@ class Client::JsonReplyMarkup final : public td::Jsonable { const td_api::ReplyMarkup *reply_markup_; }; +class Client::JsonExternalReplyInfo final : public td::Jsonable { + public: + JsonExternalReplyInfo(const td_api::messageReplyToMessage *reply, const Client *client) + : reply_(reply), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("origin", JsonMessageOrigin(reply_->origin_.get(), reply_->origin_send_date_, client_)); + if (reply_->chat_id_ != 0) { + object("chat", JsonChat(reply_->chat_id_, client_)); + if (reply_->message_id_ != 0) { + object("message_id", as_client_message_id(reply_->message_id_)); + } + } + if (reply_->content_ != nullptr) { + switch (reply_->content_->get_id()) { + case td_api::messageText::ID: { + auto content = static_cast(reply_->content_.get()); + if (content->link_preview_options_ != nullptr) { + object("link_preview_options", JsonLinkPreviewOptions(content->link_preview_options_.get(), client_)); + } + break; + } + case td_api::messageAnimation::ID: { + auto content = static_cast(reply_->content_.get()); + object("animation", JsonAnimation(content->animation_.get(), false, client_)); + add_media_spoiler(object, content->has_spoiler_); + break; + } + case td_api::messageAudio::ID: { + auto content = static_cast(reply_->content_.get()); + object("audio", JsonAudio(content->audio_.get(), client_)); + break; + } + case td_api::messageDocument::ID: { + auto content = static_cast(reply_->content_.get()); + object("document", JsonDocument(content->document_.get(), client_)); + break; + } + case td_api::messagePhoto::ID: { + auto content = static_cast(reply_->content_.get()); + CHECK(content->photo_ != nullptr); + object("photo", JsonPhoto(content->photo_.get(), client_)); + add_media_spoiler(object, content->has_spoiler_); + break; + } + case td_api::messageSticker::ID: { + auto content = static_cast(reply_->content_.get()); + object("sticker", JsonSticker(content->sticker_.get(), client_)); + break; + } + case td_api::messageVideo::ID: { + auto content = static_cast(reply_->content_.get()); + object("video", JsonVideo(content->video_.get(), client_)); + add_media_spoiler(object, content->has_spoiler_); + break; + } + case td_api::messageVideoNote::ID: { + auto content = static_cast(reply_->content_.get()); + object("video_note", JsonVideoNote(content->video_note_.get(), client_)); + break; + } + case td_api::messageVoiceNote::ID: { + auto content = static_cast(reply_->content_.get()); + object("voice", JsonVoiceNote(content->voice_note_.get(), client_)); + break; + } + case td_api::messageContact::ID: { + auto content = static_cast(reply_->content_.get()); + object("contact", JsonContact(content->contact_.get())); + break; + } + case td_api::messageDice::ID: { + auto content = static_cast(reply_->content_.get()); + object("dice", JsonDice(content->emoji_, content->value_)); + break; + } + case td_api::messageGame::ID: { + auto content = static_cast(reply_->content_.get()); + object("game", JsonGame(content->game_.get(), client_)); + break; + } + case td_api::messageInvoice::ID: { + auto content = static_cast(reply_->content_.get()); + object("invoice", JsonInvoice(content)); + break; + } + case td_api::messageLocation::ID: { + auto content = static_cast(reply_->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 content = static_cast(reply_->content_.get()); + object("venue", JsonVenue(content->venue_.get())); + break; + } + case td_api::messagePoll::ID: { + auto content = static_cast(reply_->content_.get()); + object("poll", JsonPoll(content->poll_.get(), client_)); + break; + } + case td_api::messageUnsupported::ID: + break; + case td_api::messagePremiumGiveaway::ID: { + auto content = static_cast(reply_->content_.get()); + object("giveaway", JsonGiveaway(content, client_)); + break; + } + case td_api::messagePremiumGiveawayWinners::ID: { + auto content = static_cast(reply_->content_.get()); + object("giveaway_winners", JsonGiveawayWinners(content, client_)); + break; + } + case td_api::messageStory::ID: + object("story", JsonEmptyObject()); + break; + default: + LOG(ERROR) << "Receive external reply with " << to_string(reply_->content_); + } + } + } + + private: + const td_api::messageReplyToMessage *reply_; + const Client *client_; + + void add_media_spoiler(td::JsonObjectScope &object, bool has_spoiler) const { + if (has_spoiler) { + object("has_media_spoiler", td::JsonTrue()); + } + } +}; + +class Client::JsonTextQuote final : public td::Jsonable { + public: + JsonTextQuote(const td_api::textQuote *quote, const Client *client) : quote_(quote), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("text", quote_->text_->text_); + if (!quote_->text_->entities_.empty()) { + object("entities", JsonVectorEntities(quote_->text_->entities_, client_)); + } + object("position", quote_->position_); + if (quote_->is_manual_) { + object("is_manual", td::JsonTrue()); + } + } + + private: + const td_api::textQuote *quote_; + const Client *client_; +}; + void Client::JsonMessage::store(td::JsonValueScope *scope) const { CHECK(message_ != nullptr); auto object = scope->enter_object(); @@ -2054,9 +2489,9 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("author_signature", message_->author_signature); } if (message_->sender_chat_id != 0) { - object("sender_chat", JsonChat(message_->sender_chat_id, false, client_)); + object("sender_chat", JsonChat(message_->sender_chat_id, client_)); } - object("chat", JsonChat(message_->chat_id, false, client_)); + object("chat", JsonChat(message_->chat_id, client_)); object("date", message_->date); if (message_->edit_date > 0) { object("edit_date", message_->edit_date); @@ -2077,32 +2512,49 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("message_thread_id", as_client_message_id(message_->message_thread_id)); } if (message_->initial_send_date > 0) { - if (message_->initial_sender_user_id != 0) { - object("forward_from", JsonUser(message_->initial_sender_user_id, client_)); - } - if (message_->initial_sender_chat_id != 0) { - object("forward_from_chat", JsonChat(message_->initial_sender_chat_id, false, client_)); - } - if (message_->initial_chat_id != 0) { - object("forward_from_chat", JsonChat(message_->initial_chat_id, false, client_)); - if (message_->initial_message_id != 0) { - object("forward_from_message_id", as_client_message_id(message_->initial_message_id)); - } - } - if (!message_->initial_author_signature.empty()) { - object("forward_signature", message_->initial_author_signature); - } - if (!message_->initial_sender_name.empty()) { - object("forward_sender_name", message_->initial_sender_name); - } + CHECK(message_->forward_origin != nullptr); + object("forward_origin", JsonMessageOrigin(message_->forward_origin.get(), message_->initial_send_date, client_)); if (message_->is_automatic_forward) { object("is_automatic_forward", td::JsonTrue()); } + + switch (message_->forward_origin->get_id()) { + case td_api::messageOriginUser::ID: { + auto forward_info = static_cast(message_->forward_origin.get()); + object("forward_from", JsonUser(forward_info->sender_user_id_, client_)); + break; + } + case td_api::messageOriginChat::ID: { + auto forward_info = static_cast(message_->forward_origin.get()); + object("forward_from_chat", JsonChat(forward_info->sender_chat_id_, client_)); + if (!forward_info->author_signature_.empty()) { + object("forward_signature", forward_info->author_signature_); + } + break; + } + case td_api::messageOriginHiddenUser::ID: { + auto forward_info = static_cast(message_->forward_origin.get()); + if (!forward_info->sender_name_.empty()) { + object("forward_sender_name", forward_info->sender_name_); + } + break; + } + case td_api::messageOriginChannel::ID: { + auto forward_info = static_cast(message_->forward_origin.get()); + object("forward_from_chat", JsonChat(forward_info->chat_id_, client_)); + object("forward_from_message_id", as_client_message_id(forward_info->message_id_)); + if (!forward_info->author_signature_.empty()) { + object("forward_signature", forward_info->author_signature_); + } + break; + } + default: + UNREACHABLE(); + } object("forward_date", message_->initial_send_date); } if (need_reply_) { - auto reply_to_message_id = - get_same_chat_reply_to_message_id(message_->reply_to_message.get(), message_->message_thread_id); + auto reply_to_message_id = get_same_chat_reply_to_message_id(message_); if (reply_to_message_id > 0) { // internal reply const MessageInfo *reply_to_message = client_->get_message(message_->chat_id, reply_to_message_id, true); @@ -2115,7 +2567,10 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } } if (message_->reply_to_message != nullptr && message_->reply_to_message->origin_ != nullptr) { - // external reply + object("external_reply", JsonExternalReplyInfo(message_->reply_to_message.get(), client_)); + } + if (message_->reply_to_message != nullptr && message_->reply_to_message->quote_ != nullptr) { + object("quote", JsonTextQuote(message_->reply_to_message->quote_.get(), client_)); } if (message_->media_album_id != 0) { object("media_group_id", td::to_string(message_->media_album_id)); @@ -2127,6 +2582,9 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { if (!content->text_->entities_.empty()) { object("entities", JsonVectorEntities(content->text_->entities_, client_)); } + if (content->link_preview_options_ != nullptr) { + object("link_preview_options", JsonLinkPreviewOptions(content->link_preview_options_.get(), client_)); + } break; } case td_api::messageAnimation::ID: { @@ -2333,6 +2791,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("pinned_message", JsonMessage(pinned_message, false, "pin in " + source_, client_)); } else if (need_reply_) { LOG(INFO) << "Pinned unknown, inaccessible or deleted message " << message_id; + object("pinned_message", JsonInaccessibleMessage(message_->chat_id, message_id, client_)); } } break; @@ -2432,9 +2891,12 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } break; } - case td_api::messageUserShared::ID: { - auto content = static_cast(message_->content.get()); - object("user_shared", JsonUserShared(content)); + case td_api::messageUsersShared::ID: { + auto content = static_cast(message_->content.get()); + if (content->user_ids_.size() == 1) { + object("user_shared", JsonUserShared(content)); + } + object("users_shared", JsonUsersShared(content)); break; } case td_api::messageChatShared::ID: { @@ -2442,17 +2904,31 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("chat_shared", JsonChatShared(content)); break; } + case td_api::messageStory::ID: + object("story", JsonEmptyObject()); + break; case td_api::messageChatSetBackground::ID: break; case td_api::messagePremiumGiftCode::ID: break; case td_api::messagePremiumGiveawayCreated::ID: + object("giveaway_created", JsonEmptyObject()); break; - case td_api::messagePremiumGiveaway::ID: + case td_api::messagePremiumGiveaway::ID: { + auto content = static_cast(message_->content.get()); + object("giveaway", JsonGiveaway(content, client_)); break; - case td_api::messageStory::ID: - object("story", JsonEmptyObject()); + } + case td_api::messagePremiumGiveawayWinners::ID: { + auto content = static_cast(message_->content.get()); + object("giveaway_winners", JsonGiveawayWinners(content, client_)); break; + } + case td_api::messagePremiumGiveawayCompleted::ID: { + auto content = static_cast(message_->content.get()); + object("giveaway_completed", JsonGiveawayCompleted(content, message_->chat_id, client_)); + break; + } default: UNREACHABLE(); } @@ -2470,24 +2946,6 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } } -class Client::JsonDeletedMessage final : public td::Jsonable { - public: - JsonDeletedMessage(int64 chat_id, int64 message_id, const Client *client) - : chat_id_(chat_id), message_id_(message_id), client_(client) { - } - void store(td::JsonValueScope *scope) const { - auto object = scope->enter_object(); - object("message_id", as_client_message_id(message_id_)); - object("chat", JsonChat(chat_id_, false, client_)); - object("date", 0); - } - - private: - int64 chat_id_; - int64 message_id_; - const Client *client_; -}; - class Client::JsonMessageId final : public td::Jsonable { public: explicit JsonMessageId(int64 message_id) : message_id_(message_id) { @@ -2623,7 +3081,7 @@ class Client::JsonCallbackQuery final : public td::Jsonable { if (message_info_ != nullptr) { object("message", JsonMessage(message_info_, true, "callback query", client_)); } else { - object("message", JsonDeletedMessage(chat_id_, message_id_, client_)); + object("message", JsonInaccessibleMessage(chat_id_, message_id_, client_)); } object("chat_instance", td::to_string(chat_instance_)); client_->json_store_callback_query_payload(object, payload_); @@ -2977,7 +3435,7 @@ class Client::JsonChatMemberUpdated final : public td::Jsonable { } void store(td::JsonValueScope *scope) const { auto object = scope->enter_object(); - object("chat", JsonChat(update_->chat_id_, false, client_)); + object("chat", JsonChat(update_->chat_id_, client_)); object("from", JsonUser(update_->actor_user_id_, client_)); object("date", update_->date_); auto chat_type = client_->get_chat_type(update_->chat_id_); @@ -3003,7 +3461,7 @@ class Client::JsonChatJoinRequest final : public td::Jsonable { } void store(td::JsonValueScope *scope) const { auto object = scope->enter_object(); - object("chat", JsonChat(update_->chat_id_, false, client_)); + object("chat", JsonChat(update_->chat_id_, client_)); object("from", JsonUser(update_->request_->user_id_, client_)); object("user_chat_id", update_->user_chat_id_); object("date", update_->request_->date_); @@ -3020,6 +3478,113 @@ class Client::JsonChatJoinRequest final : public td::Jsonable { const Client *client_; }; +class Client::JsonChatBoostSource final : public td::Jsonable { + public: + JsonChatBoostSource(const td_api::ChatBoostSource *boost_source, const Client *client) + : boost_source_(boost_source), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + CHECK(boost_source_ != nullptr); + switch (boost_source_->get_id()) { + case td_api::chatBoostSourcePremium::ID: { + const auto *source = static_cast(boost_source_); + object("source", "premium"); + object("user", JsonUser(source->user_id_, client_)); + break; + } + case td_api::chatBoostSourceGiftCode::ID: { + const auto *source = static_cast(boost_source_); + object("source", "gift_code"); + object("user", JsonUser(source->user_id_, client_)); + break; + } + case td_api::chatBoostSourceGiveaway::ID: { + const auto *source = static_cast(boost_source_); + object("source", "giveaway"); + object("giveaway_message_id", as_client_message_id_unchecked(source->giveaway_message_id_)); + if (source->user_id_ != 0) { + object("user", JsonUser(source->user_id_, client_)); + } else if (source->is_unclaimed_) { + object("is_unclaimed", td::JsonTrue()); + } + break; + } + default: + UNREACHABLE(); + } + } + + private: + const td_api::ChatBoostSource *boost_source_; + const Client *client_; +}; + +class Client::JsonChatBoost final : public td::Jsonable { + public: + JsonChatBoost(const td_api::chatBoost *boost, const Client *client) : boost_(boost), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("boost_id", boost_->id_); + object("add_date", boost_->start_date_); + object("expiration_date", boost_->expiration_date_); + object("source", JsonChatBoostSource(boost_->source_.get(), client_)); + } + + private: + const td_api::chatBoost *boost_; + const Client *client_; +}; + +class Client::JsonChatBoostUpdated final : public td::Jsonable { + public: + JsonChatBoostUpdated(const td_api::updateChatBoost *update, const Client *client) : update_(update), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("chat", JsonChat(update_->chat_id_, client_)); + object("boost", JsonChatBoost(update_->boost_.get(), client_)); + } + + private: + const td_api::updateChatBoost *update_; + const Client *client_; +}; + +class Client::JsonChatBoostRemoved final : public td::Jsonable { + public: + JsonChatBoostRemoved(const td_api::updateChatBoost *update, const Client *client) : update_(update), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("chat", JsonChat(update_->chat_id_, client_)); + object("boost_id", update_->boost_->id_); + object("remove_date", update_->boost_->start_date_); + object("source", JsonChatBoostSource(update_->boost_->source_.get(), client_)); + } + + private: + const td_api::updateChatBoost *update_; + const Client *client_; +}; + +class Client::JsonChatBoosts final : public td::Jsonable { + public: + JsonChatBoosts(const td_api::foundChatBoosts *chat_boosts, const Client *client) + : chat_boosts_(chat_boosts), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("boosts", td::json_array(chat_boosts_->boosts_, + [client = client_](auto &boost) { return JsonChatBoost(boost.get(), client); })); + } + + private: + const td_api::foundChatBoosts *chat_boosts_; + const Client *client_; +}; + class Client::JsonGameHighScore final : public td::Jsonable { public: JsonGameHighScore(const td_api::gameHighScore *score, const Client *client) : score_(score), client_(client) { @@ -3037,6 +3602,60 @@ class Client::JsonGameHighScore final : public td::Jsonable { const Client *client_; }; +class Client::JsonMessageReactionUpdated final : public td::Jsonable { + public: + JsonMessageReactionUpdated(const td_api::updateMessageReaction *update, const Client *client) + : update_(update), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("chat", JsonChat(update_->chat_id_, client_)); + object("message_id", as_client_message_id(update_->message_id_)); + switch (update_->actor_id_->get_id()) { + case td_api::messageSenderUser::ID: { + auto user_id = static_cast(update_->actor_id_.get())->user_id_; + object("user", JsonUser(user_id, client_)); + break; + } + case td_api::messageSenderChat::ID: { + auto actor_chat_id = static_cast(update_->actor_id_.get())->chat_id_; + object("actor_chat", JsonChat(actor_chat_id, client_)); + break; + } + default: + UNREACHABLE(); + } + object("date", update_->date_); + object("old_reaction", td::json_array(update_->old_reaction_types_, + [](const auto &reaction) { return JsonReactionType(reaction.get()); })); + object("new_reaction", td::json_array(update_->new_reaction_types_, + [](const auto &reaction) { return JsonReactionType(reaction.get()); })); + } + + private: + const td_api::updateMessageReaction *update_; + const Client *client_; +}; + +class Client::JsonMessageReactionCountUpdated final : public td::Jsonable { + public: + JsonMessageReactionCountUpdated(const td_api::updateMessageReactions *update, const Client *client) + : update_(update), client_(client) { + } + void store(td::JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("chat", JsonChat(update_->chat_id_, client_)); + object("message_id", as_client_message_id(update_->message_id_)); + object("date", update_->date_); + object("reactions", + td::json_array(update_->reactions_, [](const auto &reaction) { return JsonReactionCount(reaction.get()); })); + } + + private: + const td_api::updateMessageReactions *update_; + const Client *client_; +}; + class Client::JsonUpdateTypes final : public td::Jsonable { public: explicit JsonUpdateTypes(td::uint32 update_types) : update_types_(update_types) { @@ -3224,7 +3843,7 @@ class Client::JsonChats : public td::Jsonable { void store(td::JsonValueScope *scope) const { auto array = scope->enter_array(); for (auto &chat : chats_->chat_ids_) { - array << JsonChat(chat, false, client_); + array << JsonChat(chat, client_); } } @@ -3241,10 +3860,10 @@ class Client::JsonChatsNearby : public td::Jsonable { void store(td::JsonValueScope *scope) const { auto array = scope->enter_array(); for (auto &chat : chats_nearby_->users_nearby_) { - array << JsonChat(chat->chat_id_, false, client_, -1, chat->distance_); + array << JsonChat(chat->chat_id_, client_, false, -1, chat->distance_); } for (auto &chat : chats_nearby_->supergroups_nearby_) { - array << JsonChat(chat->chat_id_, false, client_, -1, chat->distance_); + array << JsonChat(chat->chat_id_, client_, false, -1, chat->distance_); } } @@ -3521,6 +4140,44 @@ class Client::TdOnSendMessageAlbumCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; +class Client::TdOnForwardMessagesCallback final : public TdQueryCallback { + public: + TdOnForwardMessagesCallback(Client *client, int64 chat_id, std::size_t message_count, PromisedQueryPtr query) + : client_(client), chat_id_(chat_id), message_count_(message_count), query_(std::move(query)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + if (message_count_ > 0) { + client_->decrease_yet_unsent_message_count(chat_id_, static_cast(message_count_)); + } + return fail_query_with_error(std::move(query_), move_object_as(result)); + } + + CHECK(result->get_id() == td_api::messages::ID); + auto messages = move_object_as(result); + CHECK(messages->messages_.size() == message_count_); + td::remove_if(messages->messages_, [](const auto &message) { return message == nullptr; }); + if (messages->messages_.size() != message_count_) { + client_->decrease_yet_unsent_message_count(chat_id_, + static_cast(message_count_ - messages->messages_.size())); + } + if (messages->messages_.empty()) { + return fail_query_with_error(std::move(query_), 400, "Messages can't be forwarded"); + } + auto query_id = client_->get_send_message_query_id(std::move(query_), true); + for (auto &message : messages->messages_) { + client_->on_sent_message(std::move(message), query_id); + } + } + + private: + Client *client_; + int64 chat_id_; + std::size_t message_count_; + PromisedQueryPtr query_; +}; + class Client::TdOnDeleteFailedToSendMessageCallback final : public TdQueryCallback { public: TdOnDeleteFailedToSendMessageCallback(Client *client, int64 chat_id, int64 message_id) @@ -3878,15 +4535,66 @@ class Client::TdOnCheckMessageCallback final : public TdQueryCallback { OnSuccess on_success_; }; +template +class Client::TdOnCheckMessagesCallback final : public TdQueryCallback { + public: + TdOnCheckMessagesCallback(Client *client, int64 chat_id, bool allow_empty, td::Slice message_type, + PromisedQueryPtr query, OnSuccess on_success) + : client_(client) + , chat_id_(chat_id) + , allow_empty_(allow_empty) + , message_type_(message_type) + , query_(std::move(query)) + , on_success_(std::move(on_success)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + auto error = move_object_as(result); + if (error->code_ == 429) { + LOG(WARNING) << "Failed to get messages in " << chat_id_ << ": " << message_type_; + } + if (allow_empty_) { + return on_success_(chat_id_, td::vector(), std::move(query_)); + } + return fail_query_with_error(std::move(query_), std::move(error), PSLICE() << message_type_ << " not found"); + } + + CHECK(result->get_id() == td_api::messages::ID); + auto messages = move_object_as(result); + td::vector message_ids; + for (auto &message : messages->messages_) { + if (message == nullptr) { + if (!allow_empty_) { + return fail_query_with_error(std::move(query_), 400, PSLICE() << message_type_ << " not found"); + } + continue; + } + auto full_message_id = client_->add_message(std::move(message)); + CHECK(full_message_id.chat_id == chat_id_); + message_ids.push_back(full_message_id.message_id); + } + on_success_(chat_id_, std::move(message_ids), std::move(query_)); + } + + private: + Client *client_; + int64 chat_id_; + bool allow_empty_; + td::Slice message_type_; + PromisedQueryPtr query_; + OnSuccess on_success_; +}; + template class Client::TdOnCheckMessageThreadCallback final : public TdQueryCallback { public: - TdOnCheckMessageThreadCallback(Client *client, int64 chat_id, int64 message_thread_id, int64 reply_to_message_id, - PromisedQueryPtr query, OnSuccess on_success) + TdOnCheckMessageThreadCallback(Client *client, int64 chat_id, int64 message_thread_id, + CheckedReplyParameters reply_parameters, PromisedQueryPtr query, OnSuccess on_success) : client_(client) , chat_id_(chat_id) , message_thread_id_(message_thread_id) - , reply_to_message_id_(reply_to_message_id) + , reply_parameters_(std::move(reply_parameters)) , query_(std::move(query)) , on_success_(std::move(on_success)) { } @@ -3915,14 +4623,14 @@ class Client::TdOnCheckMessageThreadCallback final : public TdQueryCallback { "Message thread is not a forum topic thread"); } - on_success_(chat_id_, message_thread_id_, reply_to_message_id_, std::move(query_)); + on_success_(chat_id_, message_thread_id_, std::move(reply_parameters_), std::move(query_)); } private: Client *client_; int64 chat_id_; int64 message_thread_id_; - int64 reply_to_message_id_; + CheckedReplyParameters reply_parameters_; PromisedQueryPtr query_; OnSuccess on_success_; }; @@ -4111,14 +4819,14 @@ class Client::TdOnGetChatStickerSetCallback final : public TdQueryCallback { auto chat_info = client_->get_chat(chat_id_); CHECK(chat_info != nullptr); CHECK(chat_info->type == ChatInfo::Type::Supergroup); - client_->set_supergroup_sticker_set_id(chat_info->supergroup_id, 0); + client_->add_supergroup_info(chat_info->supergroup_id)->sticker_set_id = 0; } else { CHECK(result->get_id() == td_api::stickerSet::ID); auto sticker_set = move_object_as(result); client_->on_get_sticker_set_name(sticker_set->id_, sticker_set->name_); } - answer_query(JsonChat(chat_id_, true, client_, pinned_message_id_), std::move(query_)); + answer_query(JsonChat(chat_id_, client_, true, pinned_message_id_), std::move(query_)); } private: @@ -4165,7 +4873,7 @@ class Client::TdOnGetChatPinnedMessageCallback final : public TdQueryCallback { } } - answer_query(JsonChat(chat_id_, true, client_, pinned_message_id), std::move(query_)); + answer_query(JsonChat(chat_id_, client_, true, pinned_message_id), std::move(query_)); } private: @@ -4420,9 +5128,9 @@ class Client::TdOnGetSupergroupMembersCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; -class Client::TdOnGetSupergroupMembersCountCallback final : public TdQueryCallback { +class Client::TdOnGetSupergroupMemberCountCallback final : public TdQueryCallback { public: - explicit TdOnGetSupergroupMembersCountCallback(PromisedQueryPtr query) : query_(std::move(query)) { + explicit TdOnGetSupergroupMemberCountCallback(PromisedQueryPtr query) : query_(std::move(query)) { } void on_result(object_ptr result) final { @@ -4546,6 +5254,26 @@ class Client::TdOnAnswerWebAppQueryCallback final : public TdQueryCallback { PromisedQueryPtr query_; }; +class Client::TdOnGetUserChatBoostsCallback final : public TdQueryCallback { + public: + TdOnGetUserChatBoostsCallback(Client *client, PromisedQueryPtr query) : client_(client), query_(std::move(query)) { + } + + void on_result(object_ptr result) final { + if (result->get_id() == td_api::error::ID) { + return fail_query_with_error(std::move(query_), move_object_as(result)); + } + + CHECK(result->get_id() == td_api::foundChatBoosts::ID); + auto chat_boosts = move_object_as(result); + answer_query(JsonChatBoosts(chat_boosts.get(), client_), std::move(query_)); + } + + private: + Client *client_; + PromisedQueryPtr query_; +}; + class Client::TdOnReturnFileCallback final : public TdQueryCallback { public: TdOnReturnFileCallback(const Client *client, PromisedQueryPtr query) : client_(client), query_(std::move(query)) { @@ -4769,7 +5497,7 @@ class Client::TdOnJoinChatCallback : public TdQueryCallback { } CHECK(result->get_id() == td_api::ok::ID); - answer_query(JsonChat(chat_id_, false, client_), std::move(query_)); + answer_query(JsonChat(chat_id_, client_), std::move(query_)); } private: @@ -4791,7 +5519,7 @@ class Client::TdOnReturnChatCallback : public TdQueryCallback { CHECK(result->get_id() == td_api::chat::ID); auto chat = move_object_as(result); - answer_query(JsonChat(chat->id_, false, client_), std::move(query_)); + answer_query(JsonChat(chat->id_, client_), std::move(query_)); } private: @@ -5133,8 +5861,7 @@ void Client::on_get_callback_query_message(object_ptr message, process_new_callback_query_queue(user_id, state); return; } - auto reply_to_message_id = - get_same_chat_reply_to_message_id(message_info->reply_to_message.get(), message_info->message_thread_id); + auto reply_to_message_id = get_same_chat_reply_to_message_id(message_info); LOG(INFO) << "Can't find callback query reply to message " << reply_to_message_id << " in chat " << chat_id << ". It may be already deleted"; } @@ -5264,7 +5991,6 @@ void Client::check_chat_access(int64 chat_id, AccessRights access_rights, const case ChatInfo::Type::Supergroup: { auto supergroup_info = get_supergroup_info(chat_info->supergroup_id); CHECK(supergroup_info != nullptr); - bool is_public = !supergroup_info->active_usernames.empty() || supergroup_info->has_location; if (supergroup_info->status->get_id() == td_api::chatMemberStatusBanned::ID) { if (supergroup_info->is_supergroup) { return fail_query(403, "Forbidden: bot was kicked from the supergroup chat", std::move(query)); @@ -5272,8 +5998,9 @@ void Client::check_chat_access(int64 chat_id, AccessRights access_rights, const return fail_query(403, "Forbidden: bot was kicked from the channel chat", std::move(query)); } } + bool is_public = !supergroup_info->active_usernames.empty() || supergroup_info->has_location; bool need_more_access_rights = is_public ? need_edit_access : need_read_access; - if (supergroup_info->status->get_id() == td_api::chatMemberStatusLeft::ID && need_more_access_rights) { + if (!is_chat_member(supergroup_info->status) && need_more_access_rights) { if (supergroup_info->is_supergroup) { return fail_query(403, "Forbidden: bot is not a member of the supergroup chat", std::move(query)); } else { @@ -5429,27 +6156,89 @@ void Client::check_message(td::Slice chat_id_str, int64 message_id, bool allow_e } template -void Client::check_message_thread(int64 chat_id, int64 message_thread_id, int64 reply_to_message_id, - PromisedQueryPtr query, OnSuccess on_success) { - if (message_thread_id <= 0) { - return on_success(chat_id, 0, reply_to_message_id, std::move(query)); - } +void Client::check_messages(td::Slice chat_id_str, td::vector message_ids, bool allow_empty, + AccessRights access_rights, td::Slice message_type, PromisedQueryPtr query, + OnSuccess on_success) { + check_chat(chat_id_str, access_rights, std::move(query), + [this, message_ids = std::move(message_ids), allow_empty, message_type, + on_success = std::move(on_success)](int64 chat_id, PromisedQueryPtr query) mutable { + if (!have_message_access(chat_id)) { + return fail_query_with_error(std::move(query), 400, "MESSAGE_NOT_FOUND", + PSLICE() << message_type << " not found"); + } - if (reply_to_message_id != 0) { - const MessageInfo *message_info = get_message(chat_id, reply_to_message_id, true); - CHECK(message_info != nullptr); - if (message_info->message_thread_id != message_thread_id) { - return fail_query_with_error(std::move(query), 400, "MESSAGE_THREAD_INVALID", - "Replied message is not in the specified message thread"); - } - } - if (reply_to_message_id == message_thread_id) { - return on_success(chat_id, message_thread_id, reply_to_message_id, std::move(query)); - } + send_request(make_object(chat_id, std::move(message_ids)), + td::make_unique>( + this, chat_id, allow_empty, message_type, std::move(query), std::move(on_success))); + }); +} - send_request(make_object(chat_id, message_thread_id), - td::make_unique>( - this, chat_id, message_thread_id, reply_to_message_id, std::move(query), std::move(on_success))); +template +void Client::check_reply_parameters(td::Slice chat_id_str, InputReplyParameters &&reply_parameters, + int64 message_thread_id, PromisedQueryPtr query, OnSuccess on_success) { + if (chat_id_str == reply_parameters.reply_in_chat_id) { + reply_parameters.reply_in_chat_id.clear(); + } + check_chat( + chat_id_str, AccessRights::Write, std::move(query), + [this, reply_parameters = std::move(reply_parameters), message_thread_id, on_success = std::move(on_success)]( + int64 chat_id, PromisedQueryPtr query) mutable { + auto on_reply_message_resolved = [this, chat_id, message_thread_id, quote = std::move(reply_parameters.quote), + on_success = std::move(on_success)](int64 reply_in_chat_id, + int64 reply_to_message_id, + PromisedQueryPtr query) mutable { + CheckedReplyParameters reply_parameters; + reply_parameters.reply_to_message_id = reply_to_message_id; + if (reply_to_message_id > 0) { + reply_parameters.reply_in_chat_id = reply_in_chat_id; + reply_parameters.quote = std::move(quote); + } + + if (message_thread_id <= 0) { + // if message thread isn't specified, then the message to reply can be only from a different chat + if (reply_parameters.reply_in_chat_id == chat_id) { + reply_parameters.reply_in_chat_id = 0; + } + return on_success(chat_id, 0, std::move(reply_parameters), std::move(query)); + } + + // reply_in_chat_id must be non-zero only for replies in different chats or different topics + if (reply_to_message_id > 0 && reply_parameters.reply_in_chat_id == chat_id) { + const MessageInfo *message_info = get_message(reply_in_chat_id, reply_to_message_id, true); + CHECK(message_info != nullptr); + if (message_info->message_thread_id == message_thread_id) { + reply_parameters.reply_in_chat_id = 0; + } + } + + send_request(make_object(chat_id, message_thread_id), + td::make_unique>( + this, chat_id, message_thread_id, std::move(reply_parameters), std::move(query), + std::move(on_success))); + }; + if (reply_parameters.reply_to_message_id <= 0) { + return on_reply_message_resolved(0, 0, std::move(query)); + } + + auto on_reply_chat_resolved = [this, reply_to_message_id = reply_parameters.reply_to_message_id, + allow_sending_without_reply = reply_parameters.allow_sending_without_reply, + on_success = std::move(on_reply_message_resolved)]( + int64 reply_in_chat_id, PromisedQueryPtr query) mutable { + if (!have_message_access(reply_in_chat_id)) { + return fail_query_with_error(std::move(query), 400, "MESSAGE_NOT_FOUND", "message to reply not found"); + } + + send_request(make_object(reply_in_chat_id, reply_to_message_id), + td::make_unique>( + this, reply_in_chat_id, reply_to_message_id, allow_sending_without_reply, "message to reply", + std::move(query), std::move(on_success))); + }; + if (reply_parameters.reply_in_chat_id.empty()) { + return on_reply_chat_resolved(chat_id, std::move(query)); + } + check_chat(reply_parameters.reply_in_chat_id, AccessRights::Read, std::move(query), + std::move(on_reply_chat_resolved)); + }); } template @@ -5905,6 +6694,17 @@ void Client::on_update(object_ptr result) { chat_info->photo_info = std::move(chat->photo_); chat_info->permissions = std::move(chat->permissions_); chat_info->message_auto_delete_time = chat->message_auto_delete_time_; + chat_info->emoji_status_custom_emoji_id = + chat->emoji_status_ != nullptr ? chat->emoji_status_->custom_emoji_id_ : 0; + chat_info->emoji_status_expiration_date = + chat->emoji_status_ != nullptr ? chat->emoji_status_->expiration_date_ : 0; + if (chat->available_reactions_->get_id() == td_api::chatAvailableReactionsSome::ID) { + chat_info->available_reactions = move_object_as(chat->available_reactions_); + } + chat_info->accent_color_id = chat->accent_color_id_; + chat_info->background_custom_emoji_id = chat->background_custom_emoji_id_; + chat_info->profile_accent_color_id = chat->profile_accent_color_id_; + chat_info->profile_background_custom_emoji_id = chat->profile_background_custom_emoji_id_; chat_info->has_protected_content = chat->has_protected_content_; break; } @@ -5936,6 +6736,38 @@ void Client::on_update(object_ptr result) { chat_info->message_auto_delete_time = update->message_auto_delete_time_; break; } + case td_api::updateChatEmojiStatus::ID: { + auto update = move_object_as(result); + auto chat_info = add_chat(update->chat_id_); + CHECK(chat_info->type != ChatInfo::Type::Unknown); + chat_info->emoji_status_custom_emoji_id = + update->emoji_status_ != nullptr ? update->emoji_status_->custom_emoji_id_ : 0; + chat_info->emoji_status_expiration_date = + update->emoji_status_ != nullptr ? update->emoji_status_->expiration_date_ : 0; + break; + } + case td_api::updateChatAvailableReactions::ID: { + auto update = move_object_as(result); + auto chat_info = add_chat(update->chat_id_); + CHECK(chat_info->type != ChatInfo::Type::Unknown); + if (update->available_reactions_->get_id() == td_api::chatAvailableReactionsSome::ID) { + chat_info->available_reactions = + move_object_as(update->available_reactions_); + } else { + chat_info->available_reactions = nullptr; + } + break; + } + case td_api::updateChatAccentColors::ID: { + auto update = move_object_as(result); + auto chat_info = add_chat(update->chat_id_); + CHECK(chat_info->type != ChatInfo::Type::Unknown); + chat_info->accent_color_id = update->accent_color_id_; + chat_info->background_custom_emoji_id = update->background_custom_emoji_id_; + chat_info->profile_accent_color_id = update->profile_accent_color_id_; + chat_info->profile_background_custom_emoji_id = update->profile_background_custom_emoji_id_; + break; + } case td_api::updateChatHasProtectedContent::ID: { auto update = move_object_as(result); auto chat_info = add_chat(update->chat_id_); @@ -5953,14 +6785,12 @@ void Client::on_update(object_ptr result) { auto update = move_object_as(result); auto user_id = update->user_id_; auto full_info = update->user_full_info_.get(); - set_user_photo(user_id, - full_info->photo_ == nullptr ? std::move(full_info->public_photo_) : std::move(full_info->photo_)); - if (full_info->bio_ != nullptr) { - set_user_bio(user_id, std::move(full_info->bio_->text_)); - } - set_user_has_private_forwards(user_id, full_info->has_private_forwards_); - set_user_has_restricted_voice_and_video_messages(user_id, - full_info->has_restricted_voice_and_video_note_messages_); + auto user_info = add_user_info(user_id); + user_info->photo = + full_info->photo_ == nullptr ? std::move(full_info->public_photo_) : std::move(full_info->photo_); + user_info->bio = full_info->bio_ != nullptr ? std::move(full_info->bio_->text_) : td::string(); + user_info->has_private_forwards = full_info->has_private_forwards_; + user_info->has_restricted_voice_and_video_messages = full_info->has_restricted_voice_and_video_note_messages_; break; } case td_api::updateUserStatus::ID: { @@ -5979,11 +6809,11 @@ void Client::on_update(object_ptr result) { auto update = move_object_as(result); auto group_id = update->basic_group_id_; 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_) - : td::string()); + auto group_info = add_group_info(group_id); + group_info->photo = std::move(full_info->photo_); + group_info->description = std::move(full_info->description_); + group_info->invite_link = std::move( + full_info->invite_link_ != nullptr ? std::move(full_info->invite_link_->invite_link_) : td::string()); break; } case td_api::updateSupergroup::ID: { @@ -5996,18 +6826,19 @@ void Client::on_update(object_ptr result) { auto update = move_object_as(result); auto supergroup_id = update->supergroup_id_; 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_) - : td::string()); - set_supergroup_sticker_set_id(supergroup_id, full_info->sticker_set_id_); - set_supergroup_can_set_sticker_set(supergroup_id, full_info->can_set_sticker_set_); - set_supergroup_slow_mode_delay(supergroup_id, full_info->slow_mode_delay_); - set_supergroup_linked_chat_id(supergroup_id, full_info->linked_chat_id_); - set_supergroup_location(supergroup_id, std::move(full_info->location_)); - set_supergroup_has_hidden_members(supergroup_id, full_info->has_hidden_members_); - set_supergroup_has_aggressive_anti_spam_enabled(supergroup_id, full_info->has_aggressive_anti_spam_enabled_); + auto supergroup_info = add_supergroup_info(supergroup_id); + supergroup_info->photo = std::move(full_info->photo_); + supergroup_info->description = std::move(full_info->description_); + supergroup_info->invite_link = std::move( + full_info->invite_link_ != nullptr ? std::move(full_info->invite_link_->invite_link_) : td::string()); + supergroup_info->sticker_set_id = full_info->sticker_set_id_; + supergroup_info->can_set_sticker_set = full_info->can_set_sticker_set_; + supergroup_info->is_all_history_available = full_info->is_all_history_available_; + supergroup_info->slow_mode_delay = full_info->slow_mode_delay_; + supergroup_info->linked_chat_id = full_info->linked_chat_id_; + supergroup_info->location = std::move(full_info->location_); + supergroup_info->has_hidden_members = full_info->has_hidden_members_; + supergroup_info->has_aggressive_anti_spam_enabled = full_info->has_aggressive_anti_spam_enabled_; break; } case td_api::updateOption::ID: { @@ -6102,6 +6933,15 @@ 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::updateChatBoost::ID: + add_update_chat_boost(move_object_as(result)); + break; + case td_api::updateMessageReaction::ID: + add_update_message_reaction(move_object_as(result)); + break; + case td_api::updateMessageReactions::ID: + add_update_message_reaction_count(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) { @@ -6247,13 +7087,60 @@ bool Client::to_bool(td::MutableSlice value) { return value == "true" || value == "yes" || value == "1"; } -td_api::object_ptr Client::get_input_message_reply_to(int64 reply_to_message_id) { - if (reply_to_message_id > 0) { - return make_object(0, reply_to_message_id, nullptr); +td_api::object_ptr Client::get_input_message_reply_to( + CheckedReplyParameters &&reply_parameters) { + if (reply_parameters.reply_to_message_id > 0) { + return make_object( + reply_parameters.reply_in_chat_id, reply_parameters.reply_to_message_id, std::move(reply_parameters.quote)); } return nullptr; } +td::Result Client::get_reply_parameters(const Query *query) { + if (!query->has_arg("reply_parameters")) { + InputReplyParameters result; + result.reply_to_message_id = get_message_id(query, "reply_to_message_id"); + result.allow_sending_without_reply = to_bool(query->arg("allow_sending_without_reply")); + return std::move(result); + } + + auto reply_parameters = query->arg("reply_parameters"); + if (reply_parameters.empty()) { + return InputReplyParameters(); + } + + LOG(INFO) << "Parsing JSON object: " << reply_parameters; + auto r_value = json_decode(reply_parameters); + if (r_value.is_error()) { + LOG(INFO) << "Can't parse JSON object: " << r_value.error(); + return td::Status::Error(400, "Can't parse reply parameters JSON object"); + } + + return get_reply_parameters(r_value.move_as_ok()); +} + +td::Result Client::get_reply_parameters(td::JsonValue &&value) { + if (value.type() != td::JsonValue::Type::Object) { + return td::Status::Error(400, "Object expected as reply parameters"); + } + auto &object = value.get_object(); + TRY_RESULT(chat_id, object.get_optional_string_field("chat_id")); + TRY_RESULT(message_id, object.get_required_int_field("message_id")); + TRY_RESULT(allow_sending_without_reply, object.get_optional_bool_field("allow_sending_without_reply")); + TRY_RESULT(input_quote, object.get_optional_string_field("quote")); + TRY_RESULT(parse_mode, object.get_optional_string_field("quote_parse_mode")); + TRY_RESULT(quote, + get_formatted_text(std::move(input_quote), std::move(parse_mode), object.extract_field("quote_entities"))); + TRY_RESULT(quote_position, object.get_optional_int_field("quote_position")); + + InputReplyParameters result; + result.reply_in_chat_id = std::move(chat_id); + result.reply_to_message_id = as_tdlib_message_id(td::max(message_id, 0)); + result.allow_sending_without_reply = allow_sending_without_reply; + result.quote = td_api::make_object(std::move(quote), quote_position); + return std::move(result); +} + td::Result> Client::get_keyboard_button(td::JsonValue &button) { if (button.type() == td::JsonValue::Type::Object) { auto &object = button.get_object(); @@ -6295,17 +7182,23 @@ td::Result> Client::get_keyboard_butt return make_object(text, make_object(url)); } - if (object.has_field("request_user")) { - TRY_RESULT(request_user, object.extract_required_field("request_user", td::JsonValue::Type::Object)); + if (object.has_field("request_user") || object.has_field("request_users")) { + td::JsonValue request_user; + if (object.has_field("request_users")) { + TRY_RESULT_ASSIGN(request_user, object.extract_required_field("request_users", td::JsonValue::Type::Object)); + } else { + TRY_RESULT_ASSIGN(request_user, object.extract_required_field("request_user", td::JsonValue::Type::Object)); + } auto &request_user_object = request_user.get_object(); TRY_RESULT(id, request_user_object.get_required_int_field("request_id")); auto restrict_user_is_bot = request_user_object.has_field("user_is_bot"); TRY_RESULT(user_is_bot, request_user_object.get_optional_bool_field("user_is_bot")); auto restrict_user_is_premium = request_user_object.has_field("user_is_premium"); TRY_RESULT(user_is_premium, request_user_object.get_optional_bool_field("user_is_premium")); + TRY_RESULT(max_quantity, request_user_object.get_optional_int_field("max_quantity", 1)); return make_object( - text, make_object(id, restrict_user_is_bot, user_is_bot, - restrict_user_is_premium, user_is_premium)); + text, make_object( + id, restrict_user_is_bot, user_is_bot, restrict_user_is_premium, user_is_premium, max_quantity)); } if (object.has_field("request_chat")) { @@ -6783,10 +7676,18 @@ td::Result> Client::get_input_me TRY_RESULT(message_text, object.get_optional_string_field("message_text")); if (!message_text.empty()) { - TRY_RESULT(disable_web_page_preview, object.get_optional_bool_field("disable_web_page_preview")); + object_ptr link_preview_options; + if (object.has_field("link_preview_options")) { + TRY_RESULT(options, object.extract_required_field("link_preview_options", td::JsonValue::Type::Object)); + CHECK(options.type() == td::JsonValue::Type::Object); + TRY_RESULT_ASSIGN(link_preview_options, get_link_preview_options(std::move(options))); + } else { + TRY_RESULT(disable_web_page_preview, object.get_optional_bool_field("disable_web_page_preview")); + link_preview_options = get_link_preview_options(disable_web_page_preview); + } TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); auto entities = object.extract_field("entities"); - TRY_RESULT(input_message_text, get_input_message_text(std::move(message_text), disable_web_page_preview, + TRY_RESULT(input_message_text, get_input_message_text(std::move(message_text), std::move(link_preview_options), std::move(parse_mode), std::move(entities))); return std::move(input_message_text); } @@ -7018,6 +7919,7 @@ td::Result> Client::get_inlin TRY_RESULT(input_message_content_obj, object.extract_optional_field("input_message_content", td::JsonValue::Type::Object)); if (input_message_content_obj.type() == td::JsonValue::Type::Null) { + // legacy TRY_RESULT(message_text, is_input_message_content_required ? object.get_required_string_field("message_text") : object.get_optional_string_field("message_text")); TRY_RESULT(disable_web_page_preview, object.get_optional_bool_field("disable_web_page_preview")); @@ -7025,8 +7927,9 @@ td::Result> Client::get_inlin auto entities = object.extract_field("entities"); if (is_input_message_content_required || !message_text.empty()) { - TRY_RESULT(input_message_text, get_input_message_text(std::move(message_text), disable_web_page_preview, - std::move(parse_mode), std::move(entities))); + TRY_RESULT(input_message_text, + get_input_message_text(std::move(message_text), get_link_preview_options(disable_web_page_preview), + std::move(parse_mode), std::move(entities))); input_message_content = std::move(input_message_text); } } else { @@ -7948,6 +8851,9 @@ td::Result> Client::get_text_entity_t TRY_RESULT(custom_emoji_id, object.get_required_long_field("custom_emoji_id")); return make_object(custom_emoji_id); } + if (type == "blockquote") { + return make_object(); + } if (type == "mention" || type == "hashtag" || type == "cashtag" || type == "bot_command" || type == "url" || type == "email" || type == "phone_number" || type == "bank_card_number") { return nullptr; @@ -8019,22 +8925,60 @@ td::Result> Client::get_formatted_text return make_object(text, std::move(entities)); } +td_api::object_ptr Client::get_link_preview_options(bool disable_web_page_preview) { + // legacy + if (!disable_web_page_preview) { + return nullptr; + } + return make_object(true, td::string(), false, false, false); +} + +td::Result> Client::get_link_preview_options(const Query *query) { + auto link_preview_options = query->arg("link_preview_options"); + if (link_preview_options.empty()) { + return get_link_preview_options(to_bool(query->arg("disable_web_page_preview"))); + } + + LOG(INFO) << "Parsing JSON object: " << link_preview_options; + auto r_value = json_decode(link_preview_options); + if (r_value.is_error()) { + LOG(INFO) << "Can't parse JSON object: " << r_value.error(); + return td::Status::Error(400, "Can't parse link preview options JSON object"); + } + + return get_link_preview_options(r_value.move_as_ok()); +} + +td::Result> Client::get_link_preview_options(td::JsonValue &&value) { + if (value.type() != td::JsonValue::Type::Object) { + return td::Status::Error(400, "Object expected as link preview options"); + } + auto &object = value.get_object(); + TRY_RESULT(is_disabled, object.get_optional_bool_field("is_disabled")); + TRY_RESULT(url, object.get_optional_string_field("url")); + TRY_RESULT(prefer_small_media, object.get_optional_bool_field("prefer_small_media")); + TRY_RESULT(prefer_large_media, object.get_optional_bool_field("prefer_large_media")); + TRY_RESULT(show_above_text, object.get_optional_bool_field("show_above_text")); + return make_object(is_disabled, url, prefer_small_media, prefer_large_media, + show_above_text); +} + td::Result> Client::get_input_message_text(const Query *query) { - return get_input_message_text(query->arg("text").str(), to_bool(query->arg("disable_web_page_preview")), + TRY_RESULT(link_preview_options, get_link_preview_options(query)); + return get_input_message_text(query->arg("text").str(), std::move(link_preview_options), query->arg("parse_mode").str(), get_input_entities(query, "entities")); } td::Result> Client::get_input_message_text( - td::string text, bool disable_web_page_preview, td::string parse_mode, td::JsonValue &&input_entities) { + td::string text, object_ptr link_preview_options, td::string parse_mode, + td::JsonValue &&input_entities) { if (text.empty()) { return td::Status::Error(400, "Message text is empty"); } TRY_RESULT(formatted_text, get_formatted_text(std::move(text), std::move(parse_mode), std::move(input_entities))); - return make_object( - std::move(formatted_text), - make_object(disable_web_page_preview, td::string(), false, false, false), false); + return make_object(std::move(formatted_text), std::move(link_preview_options), false); } td::Result> Client::get_location(const Query *query) { @@ -8408,6 +9352,53 @@ td::Result> Client::get_poll_options(const Query *query) return std::move(options); } +td::Result> Client::get_reaction_type(td::JsonValue &&value) { + if (value.type() != td::JsonValue::Type::Object) { + return td::Status::Error(400, "expected an Object"); + } + + auto &object = value.get_object(); + + TRY_RESULT(type, object.get_required_string_field("type")); + if (type == "emoji") { + TRY_RESULT(emoji, object.get_required_string_field("emoji")); + return make_object(emoji); + } + if (type == "custom_emoji") { + TRY_RESULT(custom_emoji_id, object.get_required_long_field("custom_emoji_id")); + return make_object(custom_emoji_id); + } + return td::Status::Error(400, "invalid reaction type specified"); +} + +td::Result>> Client::get_reaction_types(const Query *query) { + auto types = query->arg("reaction"); + if (types.empty()) { + return td::vector>(); + } + LOG(INFO) << "Parsing JSON object: " << types; + auto r_value = json_decode(types); + if (r_value.is_error()) { + LOG(INFO) << "Can't parse JSON object: " << r_value.error(); + return td::Status::Error(400, "Can't parse reaction types JSON object"); + } + + auto value = r_value.move_as_ok(); + if (value.type() != td::JsonValue::Type::Array) { + return td::Status::Error(400, "Expected an Array of ReactionType"); + } + + td::vector> reaction_types; + for (auto &type : value.get_array()) { + auto r_reaction_type = get_reaction_type(std::move(type)); + if (r_reaction_type.is_error()) { + return td::Status::Error(400, PSLICE() << "Can't parse ReactionType: " << r_reaction_type.error().message()); + } + reaction_types.push_back(r_reaction_type.move_as_ok()); + } + return std::move(reaction_types); +} + td::int32 Client::get_integer_arg(const Query *query, td::Slice field_name, int32 default_value, int32 min_value, int32 max_value) { auto s_arg = query->arg(field_name); @@ -8437,6 +9428,46 @@ td::int64 Client::get_message_id(const Query *query, td::Slice field_name) { return as_tdlib_message_id(arg); } +td::Result> Client::get_message_ids(const Query *query, size_t max_count, td::Slice field_name) { + auto message_ids_str = query->arg(field_name); + if (message_ids_str.empty()) { + return td::Status::Error(400, "Message identifiers are not specified"); + } + + auto r_value = json_decode(message_ids_str); + if (r_value.is_error()) { + return td::Status::Error(400, PSLICE() << "Can't parse " << field_name << " JSON object"); + } + auto value = r_value.move_as_ok(); + if (value.type() != td::JsonValue::Type::Array) { + return td::Status::Error(400, "Expected an Array of message identifiers"); + } + if (value.get_array().size() > max_count) { + return td::Status::Error(400, "Too many message identifiers specified"); + } + + td::vector message_ids; + for (auto &message_id : value.get_array()) { + td::Slice number; + if (message_id.type() == td::JsonValue::Type::Number) { + number = message_id.get_number(); + } else if (message_id.type() == td::JsonValue::Type::String) { + number = message_id.get_string(); + } else { + return td::Status::Error(400, "Message identifier must be a Number"); + } + auto parsed_message_id = td::to_integer_safe(number); + if (parsed_message_id.is_error()) { + return td::Status::Error(400, "Can't parse message identifier as Number"); + } + if (parsed_message_id.ok() <= 0) { + return td::Status::Error(400, "Invalid message identifier specified"); + } + message_ids.push_back(as_tdlib_message_id(parsed_message_id.ok())); + } + return std::move(message_ids); +} + td::Result Client::get_inline_message_id(const Query *query, td::Slice field_name) { auto s_arg = query->arg(field_name); if (s_arg.empty()) { @@ -8616,7 +9647,11 @@ void Client::on_message_send_succeeded(object_ptr &&message, in auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_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))); + if (query.query->method() == "forwardmessages" || query.query->method() == "copymessages") { + query.messages.push_back(td::json_encode(JsonMessageId(new_message_id))); + } else { + query.messages.push_back(td::json_encode(JsonMessage(message_info, true, "sent message", this))); + } query.awaited_message_count--; if (query.awaited_message_count == 0) { @@ -9184,6 +10219,51 @@ td::Status Client::process_copy_message_query(PromisedQueryPtr &query) { return td::Status::OK(); } +td::Status Client::process_copy_messages_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + auto message_thread_id = get_message_id(query.get(), "message_thread_id"); + TRY_RESULT(from_chat_id, get_required_string_arg(query.get(), "from_chat_id")); + TRY_RESULT(message_ids, get_message_ids(query.get(), 100)); + if (message_ids.empty()) { + return td::Status::Error(400, "Message identifiers are not specified"); + } + auto disable_notification = to_bool(query->arg("disable_notification")); + auto protect_content = to_bool(query->arg("protect_content")); + auto remove_caption = to_bool(query->arg("remove_caption")); + TRY_RESULT(send_at, get_message_scheduling_state(query.get())); + + auto on_success = [this, from_chat_id = from_chat_id.str(), message_ids = std::move(message_ids), + disable_notification, protect_content, send_at = std::move(send_at), + remove_caption](int64 chat_id, int64 message_thread_id, CheckedReplyParameters, + PromisedQueryPtr query) mutable { + auto it = yet_unsent_message_count_.find(chat_id); + if (it != yet_unsent_message_count_.end() && it->second >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + + check_messages( + from_chat_id, std::move(message_ids), true, AccessRights::Read, "message to forward", std::move(query), + [this, chat_id, message_thread_id, disable_notification, protect_content, remove_caption, send_at = std::move(send_at)]( + int64 from_chat_id, td::vector message_ids, PromisedQueryPtr query) mutable { + auto &count = yet_unsent_message_count_[chat_id]; + if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + + auto message_count = message_ids.size(); + count += static_cast(message_count); + + send_request(make_object( + chat_id, message_thread_id, from_chat_id, std::move(message_ids), + get_message_send_options(disable_notification, protect_content, std::move(send_at)), true, remove_caption), + td::make_unique(this, chat_id, message_count, std::move(query))); + }); + }; + check_reply_parameters(chat_id, InputReplyParameters(), message_thread_id, std::move(query), std::move(on_success)); + + return td::Status::OK(); +} + td::Status Client::process_forward_message_query(PromisedQueryPtr &query) { TRY_RESULT(from_chat_id, get_required_string_arg(query.get(), "from_chat_id")); auto message_id = get_message_id(query.get()); @@ -9196,11 +10276,54 @@ td::Status Client::process_forward_message_query(PromisedQueryPtr &query) { return td::Status::OK(); } +td::Status Client::process_forward_messages_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + auto message_thread_id = get_message_id(query.get(), "message_thread_id"); + TRY_RESULT(from_chat_id, get_required_string_arg(query.get(), "from_chat_id")); + TRY_RESULT(message_ids, get_message_ids(query.get(), 100)); + if (message_ids.empty()) { + return td::Status::Error(400, "Message identifiers are not specified"); + } + auto disable_notification = to_bool(query->arg("disable_notification")); + auto protect_content = to_bool(query->arg("protect_content")); + TRY_RESULT(send_at, get_message_scheduling_state(query.get())); + + auto on_success = [this, from_chat_id = from_chat_id.str(), message_ids = std::move(message_ids), + send_at = std::move(send_at), + disable_notification, protect_content](int64 chat_id, int64 message_thread_id, + CheckedReplyParameters, PromisedQueryPtr query) mutable { + auto it = yet_unsent_message_count_.find(chat_id); + if (it != yet_unsent_message_count_.end() && it->second >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + + check_messages( + from_chat_id, std::move(message_ids), true, AccessRights::Read, "message to forward", std::move(query), + [this, chat_id, message_thread_id, disable_notification, protect_content, send_at = std::move(send_at)]( + int64 from_chat_id, td::vector message_ids, PromisedQueryPtr query) mutable { + auto &count = yet_unsent_message_count_[chat_id]; + if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + + auto message_count = message_ids.size(); + count += static_cast(message_count); + + send_request(make_object( + chat_id, message_thread_id, from_chat_id, std::move(message_ids), + get_message_send_options(disable_notification, protect_content, std::move(send_at)), false, false), + td::make_unique(this, chat_id, message_count, std::move(query))); + }); + }; + check_reply_parameters(chat_id, InputReplyParameters(), message_thread_id, std::move(query), std::move(on_success)); + + return td::Status::OK(); +} + td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { auto chat_id = query->arg("chat_id"); auto message_thread_id = get_message_id(query.get(), "message_thread_id"); - auto reply_to_message_id = get_message_id(query.get(), "reply_to_message_id"); - auto allow_sending_without_reply = to_bool(query->arg("allow_sending_without_reply")); + TRY_RESULT(reply_parameters, get_reply_parameters(query.get())); auto disable_notification = to_bool(query->arg("disable_notification")); auto protect_content = to_bool(query->arg("protect_content")); // TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); @@ -9210,36 +10333,31 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_thread_id, reply_to_message_id, allow_sending_without_reply, disable_notification, - protect_content, input_message_contents = std::move(input_message_contents), send_at = std::move(send_at)]( + [this, chat_id = chat_id.str(), message_thread_id, reply_parameters = std::move(reply_parameters), + send_at = std::move(send_at), + disable_notification, protect_content, input_message_contents = std::move(input_message_contents)]( object_ptr reply_markup, PromisedQueryPtr query) mutable { - auto on_success = [this, message_thread_id, disable_notification, protect_content, + auto on_success = [this, disable_notification, protect_content, input_message_contents = std::move(input_message_contents), - reply_markup = std::move(reply_markup), send_at = std::move(send_at)](int64 chat_id, int64 reply_to_message_id, + send_at = std::move(send_at), + reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_thread_id, + CheckedReplyParameters reply_parameters, PromisedQueryPtr query) mutable { - auto on_message_thread_checked = [this, disable_notification, protect_content, - input_message_contents = std::move(input_message_contents), - reply_markup = std::move(reply_markup), send_at = std::move(send_at)]( - int64 chat_id, int64 message_thread_id, int64 reply_to_message_id, - PromisedQueryPtr query) mutable { - auto &count = yet_unsent_message_count_[chat_id]; - if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { - return fail_query_flood_limit_exceeded(std::move(query)); - } - auto message_count = input_message_contents.size(); - count += static_cast(message_count); + auto &count = yet_unsent_message_count_[chat_id]; + if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + auto message_count = input_message_contents.size(); + count += static_cast(message_count); - send_request( - make_object( - chat_id, message_thread_id, get_input_message_reply_to(reply_to_message_id), - get_message_send_options(disable_notification, protect_content, std::move(send_at)), std::move(input_message_contents)), - td::make_unique(this, chat_id, message_count, std::move(query))); - }; - check_message_thread(chat_id, message_thread_id, reply_to_message_id, std::move(query), - std::move(on_message_thread_checked)); + send_request( + make_object( + chat_id, message_thread_id, get_input_message_reply_to(std::move(reply_parameters)), + get_message_send_options(disable_notification, protect_content, std::move(send_at)), std::move(input_message_contents)), + td::make_unique(this, chat_id, message_count, std::move(query))); }; - check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, - AccessRights::Write, "message to reply", std::move(query), std::move(on_success)); + check_reply_parameters(chat_id, std::move(reply_parameters), message_thread_id, std::move(query), + std::move(on_success)); }); return td::Status::OK(); } @@ -9260,6 +10378,22 @@ td::Status Client::process_send_chat_action_query(PromisedQueryPtr &query) { return td::Status::OK(); } +td::Status Client::process_set_message_reaction_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + auto message_id = get_message_id(query.get()); + auto is_big = to_bool(query->arg("is_big")); + TRY_RESULT(reaction_types, get_reaction_types(query.get())); + + check_message(chat_id, message_id, false, AccessRights::Read, "message to react", std::move(query), + [this, reaction_types = std::move(reaction_types), is_big](int64 chat_id, int64 message_id, + PromisedQueryPtr query) mutable { + send_request( + make_object(chat_id, message_id, std::move(reaction_types), is_big), + td::make_unique(std::move(query))); + }); + return td::Status::OK(); +} + td::Status Client::process_edit_message_text_query(PromisedQueryPtr &query) { TRY_RESULT(input_message_text, get_input_message_text(query.get())); auto chat_id = query->arg("chat_id"); @@ -9459,6 +10593,20 @@ td::Status Client::process_delete_message_query(PromisedQueryPtr &query) { return td::Status::OK(); } +td::Status Client::process_delete_messages_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + TRY_RESULT(message_ids, get_message_ids(query.get(), 100)); + if (message_ids.empty()) { + return td::Status::Error(400, "Message identifiers are not specified"); + } + check_messages(chat_id, std::move(message_ids), true, AccessRights::Write, "message to delete", std::move(query), + [this](int64 chat_id, td::vector message_ids, PromisedQueryPtr query) { + send_request(make_object(chat_id, std::move(message_ids), true), + td::make_unique(std::move(query))); + }); + return td::Status::OK(); +} + td::Status Client::process_create_invoice_link_query(PromisedQueryPtr &query) { TRY_RESULT(input_message_invoice, get_input_message_invoice(query.get())); send_request(make_object(std::move(input_message_invoice)), @@ -10055,7 +11203,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), - td::make_unique(std::move(query))); + td::make_unique(std::move(query))); case ChatInfo::Type::Unknown: default: UNREACHABLE(); @@ -10317,6 +11465,19 @@ td::Status Client::process_decline_chat_join_request_query(PromisedQueryPtr &que return td::Status::OK(); } +td::Status Client::process_get_user_chat_boosts_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + TRY_RESULT(user_id, get_user_id(query.get())); + + 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), + td::make_unique(this, std::move(query))); + }); + }); + return td::Status::OK(); +} + 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)) { @@ -10734,48 +11895,6 @@ td::Status Client::process_get_chat_members_query(PromisedQueryPtr &query) { return td::Status::OK(); } -td::Status Client::process_delete_messages_query(PromisedQueryPtr &query) { - auto chat_id = query->arg("chat_id"); - - if (chat_id.empty()) { - return td::Status::Error(400, "Chat identifier is not specified"); - } - - auto start = as_client_message_id(get_message_id(query.get(), "start")); - auto end = as_client_message_id(get_message_id(query.get(), "end")); - - if (start == 0 || end == 0) { - return td::Status::Error(400, "Message identifier is not specified"); - } - - if (start >= end) { - return td::Status::Error(400, "Initial message identifier is not lower than last message identifier"); - } - - if (static_cast(end-start) > parameters_->max_batch_operations) { - return td::Status::Error(400, PSLICE() << "Too many operations: maximum number of batch operation is " << parameters_->max_batch_operations); - } - - check_chat(chat_id, AccessRights::Write, std::move(query), [this, start, end](int64 chat_id, PromisedQueryPtr query) { - if (get_chat_type(chat_id) != ChatType::Supergroup) { - return fail_query(400, "Bad Request: method is available only for supergroups", std::move(query)); - } - - td::vector ids; - ids.reserve(end-start+1); - for (td::int32 i = start; i <= end; i++) { - ids.push_back(as_tdlib_message_id(i)); - } - - if (!ids.empty()) { - send_request(make_object(chat_id, std::move(ids), true), - td::make_unique(std::move(query))); - } - }); - - return td::Status::OK(); -} - td::Status Client::process_toggle_group_invites_query(PromisedQueryPtr &query) { answer_query(td::JsonFalse(), std::move(query), "Not implemented"); return td::Status::OK(); @@ -11435,8 +12554,11 @@ void Client::do_send_message(object_ptr input_messa bool force) { auto chat_id = query->arg("chat_id"); auto message_thread_id = get_message_id(query.get(), "message_thread_id"); - auto reply_to_message_id = get_message_id(query.get(), "reply_to_message_id"); - auto allow_sending_without_reply = to_bool(query->arg("allow_sending_without_reply")); + auto r_reply_parameters = get_reply_parameters(query.get()); + if (r_reply_parameters.is_error()) { + return fail_query_with_error(std::move(query), 400, r_reply_parameters.error().message()); + } + auto reply_parameters = r_reply_parameters.move_as_ok(); auto disable_notification = to_bool(query->arg("disable_notification")); auto protect_content = to_bool(query->arg("protect_content")); auto r_reply_markup = get_reply_markup(query.get(), bot_user_ids_); @@ -11456,34 +12578,30 @@ void Client::do_send_message(object_ptr input_messa resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), - [this, chat_id = chat_id.str(), message_thread_id, reply_to_message_id, allow_sending_without_reply, disable_notification, - protect_content, input_message_content = std::move(input_message_content), send_at = std::move(send_at)]( + [this, chat_id = chat_id.str(), message_thread_id, reply_parameters = std::move(reply_parameters), + send_at = std::move(send_at), + disable_notification, protect_content, input_message_content = std::move(input_message_content)]( object_ptr reply_markup, PromisedQueryPtr query) mutable { - auto on_success = [this, message_thread_id, disable_notification, protect_content, + auto on_success = [this, disable_notification, protect_content, input_message_content = std::move(input_message_content), - reply_markup = std::move(reply_markup), send_at = std::move(send_at)](int64 chat_id, int64 reply_to_message_id, + send_at = std::move(send_at), + reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_thread_id, + CheckedReplyParameters reply_parameters, PromisedQueryPtr query) mutable { - auto on_message_thread_checked = - [this, disable_notification, protect_content, input_message_content = std::move(input_message_content), - reply_markup = std::move(reply_markup), send_at = std::move(send_at)](int64 chat_id, int64 message_thread_id, - int64 reply_to_message_id, PromisedQueryPtr query) mutable { - auto &count = yet_unsent_message_count_[chat_id]; - if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { - return fail_query_flood_limit_exceeded(std::move(query)); - } - count++; + auto &count = yet_unsent_message_count_[chat_id]; + if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + count++; - send_request( - make_object(chat_id, message_thread_id, get_input_message_reply_to(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)), - td::make_unique(this, chat_id, std::move(query))); - }; - check_message_thread(chat_id, message_thread_id, reply_to_message_id, std::move(query), - std::move(on_message_thread_checked)); + send_request(make_object(chat_id, message_thread_id, + get_input_message_reply_to(std::move(reply_parameters)), + get_message_send_options(disable_notification, protect_content, std::move(send_at)), + std::move(reply_markup), std::move(input_message_content)), + td::make_unique(this, chat_id, std::move(query))); }; - check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, - AccessRights::Write, "message to reply", std::move(query), std::move(on_success)); + check_reply_parameters(chat_id, std::move(reply_parameters), message_thread_id, std::move(query), + std::move(on_success)); }); } @@ -11731,8 +12849,6 @@ void Client::add_user(UserInfo *user_info, object_ptr &&user) { user_info->editable_username = std::move(user->usernames_->editable_username_); } user_info->language_code = std::move(user->language_code_); - user_info->emoji_status_custom_emoji_id = user->emoji_status_ != nullptr ? user->emoji_status_->custom_emoji_id_ : 0; - user_info->emoji_status_expiration_date = user->emoji_status_ != nullptr ? user->emoji_status_->expiration_date_ : 0; // start custom properties user_info->is_verified = user->is_verified_; @@ -11780,23 +12896,6 @@ const Client::UserInfo *Client::get_user_info(int64 user_id) const { return users_.get_pointer(user_id); } -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) { - add_user_info(user_id)->bio = std::move(bio); -} - -void Client::set_user_has_private_forwards(int64 user_id, bool has_private_forwards) { - add_user_info(user_id)->has_private_forwards = has_private_forwards; -} - -void Client::set_user_has_restricted_voice_and_video_messages(int64 user_id, - bool has_restricted_voice_and_video_messages) { - add_user_info(user_id)->has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages; -} - void Client::set_user_status(int64 user_id, object_ptr &&status) { add_user_info(user_id)->status = std::move(status); } @@ -11824,18 +12923,6 @@ const Client::GroupInfo *Client::get_group_info(int64 group_id) const { return groups_.get_pointer(group_id); } -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 &&description) { - add_group_info(group_id)->description = std::move(description); -} - -void Client::set_group_invite_link(int64 group_id, td::string &&invite_link) { - add_group_info(group_id)->invite_link = std::move(invite_link); -} - void Client::add_supergroup(SupergroupInfo *supergroup_info, object_ptr &&supergroup) { if (supergroup->usernames_ == nullptr) { supergroup_info->active_usernames.clear(); @@ -11858,47 +12945,6 @@ void Client::add_supergroup(SupergroupInfo *supergroup_info, object_ptr &&photo) { - add_supergroup_info(supergroup_id)->photo = std::move(photo); -} - -void Client::set_supergroup_description(int64 supergroup_id, td::string &&description) { - add_supergroup_info(supergroup_id)->description = std::move(description); -} - -void Client::set_supergroup_invite_link(int64 supergroup_id, td::string &&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) { - 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) { - 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) { - 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) { - add_supergroup_info(supergroup_id)->linked_chat_id = linked_chat_id; -} - -void Client::set_supergroup_location(int64 supergroup_id, object_ptr location) { - add_supergroup_info(supergroup_id)->location = std::move(location); -} - -void Client::set_supergroup_has_hidden_members(int64 supergroup_id, bool has_hidden_members) { - add_supergroup_info(supergroup_id)->has_hidden_members = has_hidden_members; -} - -void Client::set_supergroup_has_aggressive_anti_spam_enabled(int64 supergroup_id, - bool has_aggressive_anti_spam_enabled) { - add_supergroup_info(supergroup_id)->has_aggressive_anti_spam_enabled = has_aggressive_anti_spam_enabled; -} - Client::SupergroupInfo *Client::add_supergroup_info(int64 supergroup_id) { auto &supergroup_info = supergroups_[supergroup_id]; if (supergroup_info == nullptr) { @@ -12157,6 +13203,14 @@ td::Slice Client::get_update_type_name(UpdateType update_type) { return td::Slice("chat_member"); case UpdateType::ChatJoinRequest: return td::Slice("chat_join_request"); + case UpdateType::ChatBoostUpdated: + return td::Slice("chat_boost"); + case UpdateType::ChatBoostRemoved: + return td::Slice("removed_chat_boost"); + case UpdateType::MessageReaction: + return td::Slice("message_reaction"); + case UpdateType::MessageReactionCount: + return td::Slice("message_reaction_count"); default: UNREACHABLE(); return td::Slice(); @@ -12360,10 +13414,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { state = 1; } if (state == 1) { - auto reply_to_message_id = message_info == nullptr - ? 0 - : get_same_chat_reply_to_message_id(message_info->reply_to_message.get(), - message_info->message_thread_id); + auto reply_to_message_id = get_same_chat_reply_to_message_id(message_info); if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, false) == nullptr) { queue.has_active_request_ = true; return send_request(make_object(chat_id, message_id), @@ -12378,10 +13429,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { return send_request(make_object(message_sticker_set_id), td::make_unique(this, message_sticker_set_id, user_id, 0)); } - auto reply_to_message_id = message_info == nullptr - ? 0 - : get_same_chat_reply_to_message_id(message_info->reply_to_message.get(), - message_info->message_thread_id); + auto reply_to_message_id = get_same_chat_reply_to_message_id(message_info); if (reply_to_message_id > 0) { auto reply_to_message_info = get_message(chat_id, reply_to_message_id, true); auto reply_sticker_set_id = @@ -12471,6 +13519,48 @@ void Client::add_update_chat_join_request(object_ptr &&update) { + CHECK(update != nullptr); + auto left_time = update->boost_->start_date_ + 86400 - get_unix_time(); + if (left_time > 0) { + auto webhook_queue_id = update->chat_id_ + (static_cast(7) << 33); + if (update->boost_->expiration_date_ == 0) { + add_update(UpdateType::ChatBoostRemoved, JsonChatBoostRemoved(update.get(), this), left_time, webhook_queue_id); + } else { + add_update(UpdateType::ChatBoostUpdated, JsonChatBoostUpdated(update.get(), this), left_time, webhook_queue_id); + } + } else { + LOG(DEBUG) << "Skip updateChatBoost with date " << update->boost_->start_date_ << ", because current date is " + << get_unix_time(); + } +} + +void Client::add_update_message_reaction(object_ptr &&update) { + CHECK(update != nullptr); + auto left_time = update->date_ + 86400 - get_unix_time(); + if (left_time > 0) { + auto webhook_queue_id = update->chat_id_ + (static_cast(8) << 33); + add_update(UpdateType::MessageReaction, JsonMessageReactionUpdated(update.get(), this), left_time, + webhook_queue_id); + } else { + LOG(DEBUG) << "Skip updateMessageReaction with date " << update->date_ << ", because current date is " + << get_unix_time(); + } +} + +void Client::add_update_message_reaction_count(object_ptr &&update) { + CHECK(update != nullptr); + auto left_time = update->date_ + 86400 - get_unix_time(); + if (left_time > 0) { + auto webhook_queue_id = update->chat_id_ + (static_cast(9) << 33); + add_update(UpdateType::MessageReactionCount, JsonMessageReactionCountUpdated(update.get(), this), left_time, + webhook_queue_id); + } else { + LOG(DEBUG) << "Skip updateMessageReactions with date " << update->date_ << ", because current date is " + << get_unix_time(); + } +} + td::int64 Client::choose_added_member_id(const td_api::messageChatAddMembers *message_add_members) const { CHECK(message_add_members != nullptr); for (auto &member_user_id : message_add_members->member_user_ids_) { @@ -12504,6 +13594,10 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr(message->content_.get()) ->old_background_message_id_; + case td_api::messagePremiumGiveawayCompleted::ID: + return static_cast(message->content_.get()) + ->giveaway_message_id_; case td_api::messagePaymentSuccessful::ID: UNREACHABLE(); return static_cast(0); @@ -12679,7 +13767,16 @@ td::int64 Client::get_same_chat_reply_to_message_id(const object_ptrreply_to_ == nullptr); return content_message_id; } - return get_same_chat_reply_to_message_id(message->reply_to_, message->message_thread_id_); + return get_same_chat_reply_to_message_id( + message->reply_to_, message->message_thread_id_ < message->id_ ? message->message_thread_id_ : 0); +} + +td::int64 Client::get_same_chat_reply_to_message_id(const MessageInfo *message_info) { + if (message_info == nullptr) { + return 0; + } + auto message_thread_id = message_info->message_thread_id < message_info->id ? message_info->message_thread_id : 0; + return get_same_chat_reply_to_message_id(message_info->reply_to_message.get(), message_thread_id); } void Client::drop_internal_reply_to_message_in_another_chat(object_ptr &message) { @@ -12688,7 +13785,9 @@ void Client::drop_internal_reply_to_message_in_another_chat(object_ptrchat_id_; if (reply_in_chat_id != message->chat_id_ && reply_to->origin_ == nullptr) { LOG(ERROR) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ - << " from another chat " << reply_in_chat_id; + << " from another chat " << reply_in_chat_id << " sent at " << message->date_ + << " and originally sent at " + << (message->forward_info_ != nullptr ? message->forward_info_->date_ : -1); message->reply_to_ = nullptr; } } @@ -13005,51 +14104,20 @@ Client::FullMessageId Client::add_message(object_ptr &&message, message_info->via_bot_user_id = message->via_bot_user_id_; message_info->message_thread_id = message->message_thread_id_; - message_info->initial_chat_id = 0; - message_info->initial_sender_user_id = 0; - message_info->initial_sender_chat_id = 0; - message_info->initial_send_date = 0; - message_info->initial_message_id = 0; - message_info->initial_author_signature = td::string(); - message_info->initial_sender_name = td::string(); - message_info->is_automatic_forward = false; if (message->forward_info_ != nullptr) { message_info->initial_send_date = message->forward_info_->date_; - auto origin = std::move(message->forward_info_->origin_); - switch (origin->get_id()) { - case td_api::messageOriginUser::ID: { - auto forward_info = move_object_as(origin); - message_info->initial_sender_user_id = forward_info->sender_user_id_; - break; - } - case td_api::messageOriginChat::ID: { - auto forward_info = move_object_as(origin); - message_info->initial_sender_chat_id = forward_info->sender_chat_id_; - message_info->initial_author_signature = std::move(forward_info->author_signature_); - break; - } - case td_api::messageOriginHiddenUser::ID: { - auto forward_info = move_object_as(origin); - message_info->initial_sender_name = std::move(forward_info->sender_name_); - break; - } - case td_api::messageOriginChannel::ID: { - auto forward_info = move_object_as(origin); - message_info->initial_chat_id = forward_info->chat_id_; - message_info->initial_message_id = forward_info->message_id_; - message_info->initial_author_signature = std::move(forward_info->author_signature_); - break; - } - default: - UNREACHABLE(); - } + message_info->forward_origin = std::move(message->forward_info_->origin_); + auto from_chat_id = message->forward_info_->from_chat_id_; message_info->is_automatic_forward = from_chat_id != 0 && from_chat_id != chat_id && message->forward_info_->from_message_id_ != 0 && get_chat_type(chat_id) == ChatType::Supergroup && get_chat_type(from_chat_id) == ChatType::Channel; } else if (message->import_info_ != nullptr) { message_info->initial_send_date = message->import_info_->date_; - message_info->initial_sender_name = std::move(message->import_info_->sender_name_); + message_info->forward_origin = make_object(message->import_info_->sender_name_); + } else { + message_info->initial_send_date = 0; + message_info->forward_origin = nullptr; } CHECK(message->sender_id_ != nullptr); @@ -13293,6 +14361,14 @@ td::int32 Client::as_scheduled_message_id(int64 message_id) { return -static_cast((message_id >> 3) & ((1 << 18) - 1)); } +td::int32 Client::as_client_message_id_unchecked(int64 message_id) { + auto result = static_cast(message_id >> 20); + if (as_tdlib_message_id(result) != message_id) { + return 0; + } + return result; +} + td::int64 Client::get_supergroup_chat_id(int64 supergroup_id) { return static_cast(-1000000000000ll) - supergroup_id; } diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 0ce31ab..ef7ec91 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -70,7 +70,7 @@ class Client final : public WebhookActor::Callback { static constexpr int32 MAX_CERTIFICATE_FILE_SIZE = 3 << 20; static constexpr int32 MAX_DOWNLOAD_FILE_SIZE = 20 << 20; - static constexpr int32 MAX_CONCURRENTLY_SENT_CHAT_MESSAGES = 250; // some unreasonably big value + static constexpr int32 MAX_CONCURRENTLY_SENT_CHAT_MESSAGES = 310; // some unreasonably big value static constexpr std::size_t MIN_PENDING_UPDATES_WARNING = 200; @@ -104,12 +104,18 @@ class Client final : public WebhookActor::Callback { class JsonDatedFiles; class JsonUser; class JsonUsers; + class JsonReactionType; + class JsonReactionCount; class JsonChatPermissions; class JsonChatPhotoInfo; class JsonChatLocation; class JsonChatInviteLink; class JsonChat; class JsonMessageSender; + class JsonMessageOrigin; + class JsonExternalReplyInfo; + class JsonTextQuote; + class JsonLinkPreviewOptions; class JsonAnimation; class JsonAudio; class JsonDocument; @@ -140,7 +146,7 @@ class Client final : public WebhookActor::Callback { class JsonReplyMarkup; class JsonMessage; class JsonMessages; - class JsonDeletedMessage; + class JsonInaccessibleMessage; class JsonMessageId; class JsonInlineQuery; class JsonChosenInlineResult; @@ -159,10 +165,17 @@ class Client final : public WebhookActor::Callback { class JsonChatMembers; class JsonChatMemberUpdated; class JsonChatJoinRequest; + class JsonChatBoostSource; + class JsonChatBoost; + class JsonChatBoostUpdated; + class JsonChatBoostRemoved; + class JsonChatBoosts; class JsonForumTopicCreated; class JsonForumTopicEdited; class JsonForumTopicInfo; class JsonGameHighScore; + class JsonMessageReactionUpdated; + class JsonMessageReactionCountUpdated; class JsonAddress; class JsonOrderInfo; class JsonSuccessfulPaymentBot; @@ -177,7 +190,11 @@ class Client final : public WebhookActor::Callback { class JsonChatSetMessageAutoDeleteTime; class JsonWriteAccessAllowed; class JsonUserShared; + class JsonUsersShared; class JsonChatShared; + class JsonGiveaway; + class JsonGiveawayWinners; + class JsonGiveawayCompleted; class JsonUpdateTypes; class JsonWebhookInfo; class JsonStickerSet; @@ -202,6 +219,7 @@ class Client final : public WebhookActor::Callback { class TdOnGetUserProfilePhotosCallback; class TdOnSendMessageCallback; class TdOnSendMessageAlbumCallback; + class TdOnForwardMessagesCallback; class TdOnDeleteFailedToSendMessageCallback; class TdOnEditMessageCallback; class TdOnEditInlineMessageCallback; @@ -224,7 +242,8 @@ class Client final : public WebhookActor::Callback { class TdOnGetChatPinnedMessageToUnpinCallback; class TdOnGetGroupMembersCallback; class TdOnGetSupergroupMembersCallback; - class TdOnGetSupergroupMembersCountCallback; + class TdOnGetSupergroupMemberCountCallback; + class TdOnGetUserChatBoostsCallback; class TdOnCreateInvoiceLinkCallback; class TdOnReplacePrimaryChatInviteLinkCallback; class TdOnGetChatInviteLinkCallback; @@ -274,6 +293,19 @@ class Client final : public WebhookActor::Callback { virtual ~TdQueryCallback() = default; }; + struct InputReplyParameters { + td::string reply_in_chat_id; + int64 reply_to_message_id = 0; + bool allow_sending_without_reply = false; + object_ptr quote; + }; + + struct CheckedReplyParameters { + int64 reply_in_chat_id = 0; + int64 reply_to_message_id = 0; + object_ptr quote; + }; + struct UserInfo; struct ChatInfo; struct BotCommandScope; @@ -298,6 +330,8 @@ class Client final : public WebhookActor::Callback { template class TdOnCheckMessageCallback; template + class TdOnCheckMessagesCallback; + template class TdOnCheckMessageThreadCallback; template class TdOnCheckRemoteFileIdCallback; @@ -339,8 +373,12 @@ class Client final : public WebhookActor::Callback { td::Slice message_type, PromisedQueryPtr query, OnSuccess on_success); template - void check_message_thread(int64 chat_id, int64 message_thread_id, int64 reply_to_message_id, PromisedQueryPtr query, - OnSuccess on_success); + void check_messages(td::Slice chat_id_str, td::vector message_ids, bool allow_empty, + AccessRights access_rights, td::Slice message_type, PromisedQueryPtr query, OnSuccess on_success); + + template + void check_reply_parameters(td::Slice chat_id_str, InputReplyParameters &&reply_parameters, int64 message_thread_id, + PromisedQueryPtr query, OnSuccess on_success); template void resolve_sticker_set(const td::string &sticker_set_name, PromisedQueryPtr query, OnSuccess on_success); @@ -376,7 +414,11 @@ class Client final : public WebhookActor::Callback { static bool to_bool(td::MutableSlice value); - static object_ptr get_input_message_reply_to(int64 reply_to_message_id); + static object_ptr get_input_message_reply_to(CheckedReplyParameters &&reply_parameters); + + static td::Result get_reply_parameters(const Query *query); + + static td::Result get_reply_parameters(td::JsonValue &&value); static td::Result> get_keyboard_button(td::JsonValue &button); @@ -502,12 +544,17 @@ class Client final : public WebhookActor::Callback { static td::Result> get_formatted_text(td::string text, td::string parse_mode, td::JsonValue &&input_entities); + static object_ptr get_link_preview_options(bool disable_web_page_preview); + + static td::Result> get_link_preview_options(const Query *query); + + static td::Result> get_link_preview_options(td::JsonValue &&value); + static td::Result> get_input_message_text(const Query *query); - static td::Result> get_input_message_text(td::string text, - bool disable_web_page_preview, - td::string parse_mode, - td::JsonValue &&input_entities); + static td::Result> get_input_message_text( + td::string text, object_ptr link_preview_options, td::string parse_mode, + td::JsonValue &&input_entities); static td::Result> get_location(const Query *query); @@ -533,6 +580,10 @@ class Client final : public WebhookActor::Callback { static td::Result> get_poll_options(const Query *query); + static td::Result> get_reaction_type(td::JsonValue &&value); + + static td::Result>> get_reaction_types(const Query *query); + static int32 get_integer_arg(const Query *query, td::Slice field_name, int32 default_value, int32 min_value = std::numeric_limits::min(), int32 max_value = std::numeric_limits::max()); @@ -541,6 +592,9 @@ class Client final : public WebhookActor::Callback { static int64 get_message_id(const Query *query, td::Slice field_name = td::Slice("message_id")); + static td::Result> get_message_ids(const Query *query, size_t max_count, + td::Slice field_name = td::Slice("message_ids")); + static td::Result get_inline_message_id(const Query *query, td::Slice field_name = td::Slice("inline_message_id")); @@ -612,15 +666,19 @@ class Client final : public WebhookActor::Callback { td::Status process_send_poll_query(PromisedQueryPtr &query); td::Status process_stop_poll_query(PromisedQueryPtr &query); td::Status process_copy_message_query(PromisedQueryPtr &query); + td::Status process_copy_messages_query(PromisedQueryPtr &query); td::Status process_forward_message_query(PromisedQueryPtr &query); + td::Status process_forward_messages_query(PromisedQueryPtr &query); td::Status process_send_media_group_query(PromisedQueryPtr &query); td::Status process_send_chat_action_query(PromisedQueryPtr &query); + td::Status process_set_message_reaction_query(PromisedQueryPtr &query); td::Status process_edit_message_text_query(PromisedQueryPtr &query); td::Status process_edit_message_live_location_query(PromisedQueryPtr &query); td::Status process_edit_message_media_query(PromisedQueryPtr &query); td::Status process_edit_message_caption_query(PromisedQueryPtr &query); td::Status process_edit_message_reply_markup_query(PromisedQueryPtr &query); td::Status process_delete_message_query(PromisedQueryPtr &query); + td::Status process_delete_messages_query(PromisedQueryPtr &query); td::Status process_create_invoice_link_query(PromisedQueryPtr &query); td::Status process_set_game_score_query(PromisedQueryPtr &query); td::Status process_get_game_high_scores_query(PromisedQueryPtr &query); @@ -670,6 +728,7 @@ class Client final : public WebhookActor::Callback { td::Status process_unban_chat_sender_chat_query(PromisedQueryPtr &query); td::Status process_approve_chat_join_request_query(PromisedQueryPtr &query); td::Status process_decline_chat_join_request_query(PromisedQueryPtr &query); + td::Status process_get_user_chat_boosts_query(PromisedQueryPtr &query); td::Status process_get_sticker_set_query(PromisedQueryPtr &query); td::Status process_get_custom_emoji_stickers_query(PromisedQueryPtr &query); td::Status process_upload_sticker_file_query(PromisedQueryPtr &query); @@ -695,7 +754,6 @@ class Client final : public WebhookActor::Callback { //custom methods td::Status process_get_message_info_query(PromisedQueryPtr &query); td::Status process_get_chat_members_query(PromisedQueryPtr &query); - td::Status process_delete_messages_query(PromisedQueryPtr &query); td::Status process_toggle_group_invites_query(PromisedQueryPtr &query); td::Status process_ping_query(PromisedQueryPtr &query); td::Status process_get_memory_stats_query(PromisedQueryPtr &query); @@ -813,8 +871,6 @@ class Client final : public WebhookActor::Callback { td::vector active_usernames; td::string editable_username; td::string language_code; - int64 emoji_status_custom_emoji_id; - int32 emoji_status_expiration_date; object_ptr photo; td::string bio; @@ -835,16 +891,11 @@ class Client final : public WebhookActor::Callback { bool added_to_attachment_menu = false; }; static void add_user(UserInfo *user_info, object_ptr &&user); - void set_user_photo(int64 user_id, object_ptr &&photo); - void set_user_bio(int64 user_id, td::string &&bio); - void set_user_has_private_forwards(int64 user_id, bool has_private_forwards); - void set_user_has_restricted_voice_and_video_messages(int64 user_id, bool has_restricted_voice_and_video_messages); - - void set_user_status(int64 user_id, object_ptr &&status); - UserInfo *add_user_info(int64 user_id); const UserInfo *get_user_info(int64 user_id) const; + void set_user_status(int64 user_id, object_ptr &&status); + struct GroupInfo { object_ptr photo; td::string description; @@ -856,9 +907,6 @@ class Client final : public WebhookActor::Callback { int64 upgraded_to_supergroup_id = 0; }; 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 &&description); - 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; @@ -877,6 +925,7 @@ class Client final : public WebhookActor::Callback { bool is_supergroup = false; bool is_forum = false; bool can_set_sticker_set = false; + bool is_all_history_available = false; bool has_location = false; bool join_to_send_messages = false; bool join_by_request = false; @@ -889,16 +938,6 @@ class Client final : public WebhookActor::Callback { // end custom properties }; 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 &&description); - 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); - void set_supergroup_can_set_sticker_set(int64 supergroup_id, bool can_set_sticker_set); - 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); - void set_supergroup_has_hidden_members(int64 supergroup_id, bool has_hidden_members); - void set_supergroup_has_aggressive_anti_spam_enabled(int64 supergroup_id, bool has_aggressive_anti_spam_enabled); SupergroupInfo *add_supergroup_info(int64 supergroup_id); const SupergroupInfo *get_supergroup_info(int64 supergroup_id) const; @@ -907,7 +946,14 @@ class Client final : public WebhookActor::Callback { Type type = Type::Unknown; td::string title; int32 message_auto_delete_time = 0; + int64 emoji_status_custom_emoji_id = 0; + int32 emoji_status_expiration_date = 0; + int32 accent_color_id = -1; + int32 profile_accent_color_id = -1; + int64 background_custom_emoji_id = 0; + int64 profile_background_custom_emoji_id = 0; bool has_protected_content = false; + object_ptr available_reactions; object_ptr photo_info; object_ptr permissions; union { @@ -933,13 +979,8 @@ class Client final : public WebhookActor::Callback { int64 message_thread_id = 0; int32 date = 0; int32 edit_date = 0; - int64 initial_chat_id = 0; - int64 initial_sender_user_id = 0; - int64 initial_sender_chat_id = 0; int32 initial_send_date = 0; - int64 initial_message_id = 0; - td::string initial_author_signature; - td::string initial_sender_name; + object_ptr forward_origin; td::string author_signature; object_ptr reply_to_message; int64 media_album_id = 0; @@ -969,6 +1010,8 @@ class Client final : public WebhookActor::Callback { static int64 get_same_chat_reply_to_message_id(const object_ptr &message); + static int64 get_same_chat_reply_to_message_id(const MessageInfo *message_info); + static void drop_internal_reply_to_message_in_another_chat(object_ptr &message); static td::Slice get_sticker_type(const object_ptr &type); @@ -1054,6 +1097,8 @@ class Client final : public WebhookActor::Callback { static int32 as_scheduled_message_id(int64 message_id); + static int32 as_client_message_id_unchecked(int64 message_id); + static int64 get_supergroup_chat_id(int64 supergroup_id); static int64 get_basic_group_chat_id(int64 basic_group_id); @@ -1087,6 +1132,12 @@ class Client final : public WebhookActor::Callback { void add_update_chat_join_request(object_ptr &&update); + void add_update_chat_boost(object_ptr &&update); + + void add_update_message_reaction(object_ptr &&update); + + void add_update_message_reaction_count(object_ptr &&update); + // append only before Size enum class UpdateType : int32 { Message, @@ -1105,6 +1156,10 @@ class Client final : public WebhookActor::Callback { MyChatMember, ChatMember, ChatJoinRequest, + ChatBoostUpdated, + ChatBoostRemoved, + MessageReaction, + MessageReactionCount, Size }; @@ -1134,9 +1189,11 @@ class Client final : public WebhookActor::Callback { bool have_message_access(int64 chat_id) const; - // by default ChatMember updates are disabled + // by default ChatMember, MessageReaction, and MessageReactionCount updates are disabled static constexpr td::uint32 DEFAULT_ALLOWED_UPDATE_TYPES = - (1 << static_cast(UpdateType::Size)) - 1 - (1 << static_cast(UpdateType::ChatMember)); + (1 << static_cast(UpdateType::Size)) - 1 - (1 << static_cast(UpdateType::ChatMember)) - + (1 << static_cast(UpdateType::MessageReaction)) - + (1 << static_cast(UpdateType::MessageReactionCount)); object_ptr authorization_state_; bool was_authorized_ = false; diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 04755f9..ebc6898 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared(); auto parameters = std::make_unique(); - parameters->version_ = "6.9.2"; + parameters->version_ = "7.0"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats();