From 1a34273163f0477fbe84af62b92e09dfb8779c5b Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 9 Nov 2023 18:24:50 +0300 Subject: [PATCH 01/43] Add Client::get_same_chat_reply_to_message_id(const MessageInfo *message_info). --- telegram-bot-api/Client.cpp | 27 ++++++++++++++------------- telegram-bot-api/Client.h | 2 ++ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 291458b..47b0806 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2101,8 +2101,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { 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); @@ -5133,8 +5132,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"; } @@ -12360,10 +12358,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 +12373,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 = @@ -12679,7 +12671,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) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 0ce31ab..45c9805 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -969,6 +969,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); From 8fe04fc33a7eb3db25ecdaa424e474008a5ba3df Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 9 Nov 2023 19:15:42 +0300 Subject: [PATCH 02/43] Support td_api::textEntityTypeBlockQuote. --- telegram-bot-api/Client.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 47b0806..8222bda 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -580,6 +580,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 +603,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_); } } @@ -7946,6 +7948,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; From 80406b7028131132683819dc6b5166a0f63ede71 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 18:22:04 +0300 Subject: [PATCH 03/43] Add Message.giveaway_created. --- telegram-bot-api/Client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 8222bda..d955cc0 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2448,6 +2448,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { case td_api::messagePremiumGiftCode::ID: break; case td_api::messagePremiumGiveawayCreated::ID: + object("giveaway_created", JsonEmptyObject()); break; case td_api::messagePremiumGiveaway::ID: break; @@ -12501,6 +12502,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr Date: Mon, 13 Nov 2023 18:38:57 +0300 Subject: [PATCH 04/43] Add Message.giveaway. --- telegram-bot-api/Client.cpp | 41 ++++++++++++++++++++++++++++++++++--- telegram-bot-api/Client.h | 1 + 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index d955cc0..957c705 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1916,6 +1916,39 @@ 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, false, 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_->country_codes_.empty()) { + object("country_codes", td::json_array(giveaway_->parameters_->country_codes_, + [](td::Slice country_code) { return td::JsonString(country_code); })); + } + if (giveaway_->month_count_ > 0) { + object("premium_subscription_month_count", giveaway_->month_count_); + } + } + + private: + const td_api::messagePremiumGiveaway *giveaway_; + const Client *client_; +}; + class Client::JsonWebAppInfo final : public td::Jsonable { public: explicit JsonWebAppInfo(const td::string &url) : url_(url) { @@ -2450,8 +2483,11 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { 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()); break; @@ -12503,6 +12539,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr Date: Sat, 3 Feb 2024 11:21:00 +0100 Subject: [PATCH 05/43] Rebase: Simplify update*FullInfo handling. --- telegram-bot-api/Client.cpp | 120 ++++++++---------------------------- telegram-bot-api/Client.h | 22 +------ 2 files changed, 26 insertions(+), 116 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 957c705..19b76eb 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -4149,7 +4149,7 @@ 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); @@ -5990,14 +5990,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: { @@ -6016,11 +6014,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: { @@ -6033,18 +6031,18 @@ 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->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: { @@ -11820,23 +11818,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); } @@ -11864,18 +11845,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(); @@ -11898,47 +11867,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) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 2b2cb4e..26af317 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -836,16 +836,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; @@ -857,9 +852,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; @@ -890,16 +882,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; From 0da8d14430dd247b09c1f59a4c12d40618873caf Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 18:56:15 +0300 Subject: [PATCH 06/43] Add Chat.has_visible_history. --- telegram-bot-api/Client.cpp | 4 ++++ telegram-bot-api/Client.h | 1 + 2 files changed, 5 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 19b76eb..a86cf10 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -894,6 +894,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())); } @@ -6038,6 +6041,7 @@ void Client::on_update(object_ptr result) { 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_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 26af317..a7089a7 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -870,6 +870,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; From a2a226ac42f76a5caaae627e378023334fe51c3c Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 19:08:21 +0300 Subject: [PATCH 07/43] Add Chat.accent_color_id. --- telegram-bot-api/Client.cpp | 10 ++++++++++ telegram-bot-api/Client.h | 1 + 2 files changed, 11 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index a86cf10..425be56 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -971,6 +971,8 @@ 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); } + CHECK(chat_info->accent_color_id != -1); + object("accent_color_id", chat_info->accent_color_id); if (chat_info->has_protected_content) { object("has_protected_content", td::JsonTrue()); } @@ -5945,6 +5947,7 @@ 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->accent_color_id = chat->accent_color_id_; chat_info->has_protected_content = chat->has_protected_content_; break; } @@ -5976,6 +5979,13 @@ void Client::on_update(object_ptr result) { chat_info->message_auto_delete_time = update->message_auto_delete_time_; break; } + case td_api::updateChatAccentColor::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_; + break; + } case td_api::updateChatHasProtectedContent::ID: { auto update = move_object_as(result); auto chat_info = add_chat(update->chat_id_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index a7089a7..7d06257 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -891,6 +891,7 @@ class Client final : public WebhookActor::Callback { Type type = Type::Unknown; td::string title; int32 message_auto_delete_time = 0; + int32 accent_color_id = -1; bool has_protected_content = false; object_ptr photo_info; object_ptr permissions; From d02a9fe5c30d54c50c5a8a027d5ace6c5fa6c078 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 19:15:19 +0300 Subject: [PATCH 08/43] Add Chat.background_custom_emoji_id. --- telegram-bot-api/Client.cpp | 11 +++++++++++ telegram-bot-api/Client.h | 1 + 2 files changed, 12 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 425be56..2c699d4 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -973,6 +973,9 @@ class Client::JsonChat final : public td::Jsonable { } 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->has_protected_content) { object("has_protected_content", td::JsonTrue()); } @@ -5948,6 +5951,7 @@ void Client::on_update(object_ptr result) { chat_info->permissions = std::move(chat->permissions_); chat_info->message_auto_delete_time = chat->message_auto_delete_time_; chat_info->accent_color_id = chat->accent_color_id_; + chat_info->background_custom_emoji_id = chat->background_custom_emoji_id_; chat_info->has_protected_content = chat->has_protected_content_; break; } @@ -5986,6 +5990,13 @@ void Client::on_update(object_ptr result) { chat_info->accent_color_id = update->accent_color_id_; break; } + case td_api::updateChatBackgroundCustomEmoji::ID: { + auto update = move_object_as(result); + auto chat_info = add_chat(update->chat_id_); + CHECK(chat_info->type != ChatInfo::Type::Unknown); + chat_info->background_custom_emoji_id = update->background_custom_emoji_id_; + break; + } case td_api::updateChatHasProtectedContent::ID: { auto update = move_object_as(result); auto chat_info = add_chat(update->chat_id_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 7d06257..b01e66e 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -892,6 +892,7 @@ class Client final : public WebhookActor::Callback { td::string title; int32 message_auto_delete_time = 0; int32 accent_color_id = -1; + int64 background_custom_emoji_id = 0; bool has_protected_content = false; object_ptr photo_info; object_ptr permissions; From 121e2d8a188ea53d754c8d1b6eaeb64bc9bee012 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 3 Feb 2024 11:23:59 +0100 Subject: [PATCH 09/43] Rebase: Add "chat_boost" and "removed_chat_boost" updates. --- telegram-bot-api/Client.cpp | 122 ++++++++++++++++++++++++++++++++++++ telegram-bot-api/Client.h | 10 +++ 2 files changed, 132 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 2c699d4..3b2ea05 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -3066,6 +3066,97 @@ 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_, false, 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_, false, 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::JsonGameHighScore final : public td::Jsonable { public: JsonGameHighScore(const td_api::gameHighScore *score, const Client *client) : score_(score), client_(client) { @@ -6162,6 +6253,9 @@ 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::updateConnectionState::ID: { auto update = move_object_as(result); if (update->state_->get_id() == td_api::connectionStateReady::ID) { @@ -12150,6 +12244,10 @@ 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"); default: UNREACHABLE(); return td::Slice(); @@ -12458,6 +12556,22 @@ 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(); + } +} + 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_) { @@ -13287,6 +13401,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 b01e66e..9e4ad14 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -159,6 +159,10 @@ class Client final : public WebhookActor::Callback { class JsonChatMembers; class JsonChatMemberUpdated; class JsonChatJoinRequest; + class JsonChatBoostSource; + class JsonChatBoost; + class JsonChatBoostUpdated; + class JsonChatBoostRemoved; class JsonForumTopicCreated; class JsonForumTopicEdited; class JsonForumTopicInfo; @@ -1042,6 +1046,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); @@ -1075,6 +1081,8 @@ class Client final : public WebhookActor::Callback { void add_update_chat_join_request(object_ptr &&update); + void add_update_chat_boost(object_ptr &&update); + // append only before Size enum class UpdateType : int32 { Message, @@ -1093,6 +1101,8 @@ class Client final : public WebhookActor::Callback { MyChatMember, ChatMember, ChatJoinRequest, + ChatBoostUpdated, + ChatBoostRemoved, Size }; From 34f9b8a86089816dcf077ca0a7c1d3ba742c9a3a Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 20:16:11 +0300 Subject: [PATCH 10/43] Add getUserChatBoosts. --- telegram-bot-api/Client.cpp | 56 +++++++++++++++++++++++++++++++++++-- telegram-bot-api/Client.h | 5 +++- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 3b2ea05..d8ba354 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -312,6 +312,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); @@ -3157,6 +3158,22 @@ class Client::JsonChatBoostRemoved final : public td::Jsonable { 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) { @@ -4557,9 +4574,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 { @@ -4683,6 +4700,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)) { @@ -10212,7 +10249,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(); @@ -10474,6 +10511,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)) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 9e4ad14..5a0be84 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -163,6 +163,7 @@ class Client final : public WebhookActor::Callback { class JsonChatBoost; class JsonChatBoostUpdated; class JsonChatBoostRemoved; + class JsonChatBoosts; class JsonForumTopicCreated; class JsonForumTopicEdited; class JsonForumTopicInfo; @@ -229,7 +230,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; @@ -675,6 +677,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); From ad84bfc2141a6b8f99da9f7c0bd38a172bd7ccdb Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 21:19:35 +0300 Subject: [PATCH 11/43] Allow to specify link preview options for sent text messages. --- telegram-bot-api/Client.cpp | 66 ++++++++++++++++++++++++++++++++----- telegram-bot-api/Client.h | 13 +++++--- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index d8ba354..b8a2799 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -6974,10 +6974,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); } @@ -7209,6 +7217,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")); @@ -7216,8 +7225,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 { @@ -8213,22 +8223,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) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 5a0be84..d245aee 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -509,12 +509,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); From c3999a2144bd3efd4a3b6b7c30c8c008597ddf84 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 22:08:07 +0300 Subject: [PATCH 12/43] Add message.link_preview_options. --- telegram-bot-api/Client.cpp | 32 ++++++++++++++++++++++++++++++++ telegram-bot-api/Client.h | 1 + 2 files changed, 33 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index b8a2799..189ed29 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1041,6 +1041,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) @@ -2170,6 +2199,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: { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index d245aee..a2ebe0c 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -110,6 +110,7 @@ class Client final : public WebhookActor::Callback { class JsonChatInviteLink; class JsonChat; class JsonMessageSender; + class JsonLinkPreviewOptions; class JsonAnimation; class JsonAudio; class JsonDocument; From 05d9cd05b7bafb10da5aa4886354df5f68848bbe Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 22:41:02 +0300 Subject: [PATCH 13/43] Store td_api::MessageOrigin in MessageInfo. --- telegram-bot-api/Client.cpp | 91 ++++++++++++++++--------------------- telegram-bot-api/Client.h | 7 +-- 2 files changed, 39 insertions(+), 59 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 189ed29..440bbc6 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2150,23 +2150,39 @@ 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)); + CHECK(message_->forward_origin != nullptr); + 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; } - } - 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); + case td_api::messageOriginChat::ID: { + auto forward_info = static_cast(message_->forward_origin.get()); + object("forward_from_chat", JsonChat(forward_info->sender_chat_id_, false, 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_, false, 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(); } if (message_->is_automatic_forward) { object("is_automatic_forward", td::JsonTrue()); @@ -13243,51 +13259,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); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index a2ebe0c..150e2a2 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -932,13 +932,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; From a7f7cd0a7dbbda07ffbfd17ad8ff451f5161f71f Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 13 Nov 2023 23:17:15 +0300 Subject: [PATCH 14/43] Add Message.forward_origin. --- telegram-bot-api/Client.cpp | 61 +++++++++++++++++++++++++++++++++++-- telegram-bot-api/Client.h | 1 + 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 440bbc6..6417305 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1026,6 +1026,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_, false, 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_, false, 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) { @@ -2151,6 +2204,11 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } if (message_->initial_send_date > 0) { 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()); @@ -2184,9 +2242,6 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { default: UNREACHABLE(); } - if (message_->is_automatic_forward) { - object("is_automatic_forward", td::JsonTrue()); - } object("forward_date", message_->initial_send_date); } if (need_reply_) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 150e2a2..f954fd5 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -110,6 +110,7 @@ class Client final : public WebhookActor::Callback { class JsonChatInviteLink; class JsonChat; class JsonMessageSender; + class JsonMessageOrigin; class JsonLinkPreviewOptions; class JsonAnimation; class JsonAudio; From ab0f7878bb2509b364e525e99b56dc4090a668ff Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 14 Nov 2023 12:56:32 +0300 Subject: [PATCH 15/43] Add Message.external_reply. --- telegram-bot-api/Client.cpp | 132 +++++++++++++++++++++++++++++++++++- telegram-bot-api/Client.h | 1 + 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 6417305..25b40ec 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2165,6 +2165,136 @@ 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_, false, 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::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()); + } + } +}; + void Client::JsonMessage::store(td::JsonValueScope *scope) const { CHECK(message_ != nullptr); auto object = scope->enter_object(); @@ -2258,7 +2388,7 @@ 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_->media_album_id != 0) { object("media_group_id", td::to_string(message_->media_album_id)); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index f954fd5..c5b1e6a 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -111,6 +111,7 @@ class Client final : public WebhookActor::Callback { class JsonChat; class JsonMessageSender; class JsonMessageOrigin; + class JsonExternalReplyInfo; class JsonLinkPreviewOptions; class JsonAnimation; class JsonAudio; From 4b13a450ae70d64b5a56d96d5f2e50097d71f5c9 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 14 Nov 2023 13:00:27 +0300 Subject: [PATCH 16/43] Add Message.quote and Message.quote_entities. --- telegram-bot-api/Client.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 25b40ec..e448b81 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2390,6 +2390,12 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { if (message_->reply_to_message != nullptr && message_->reply_to_message->origin_ != nullptr) { object("external_reply", JsonExternalReplyInfo(message_->reply_to_message.get(), client_)); } + if (message_->reply_to_message != nullptr && message_->reply_to_message->quote_ != nullptr) { + object("quote", message_->reply_to_message->quote_->text_); + if (!message_->reply_to_message->quote_->entities_.empty()) { + object("quote_entities", JsonVectorEntities(message_->reply_to_message->quote_->entities_, client_)); + } + } if (message_->media_album_id != 0) { object("media_group_id", td::to_string(message_->media_album_id)); } From 49df33acd7a7eec1026af887d30426dae2910609 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 3 Feb 2024 11:28:37 +0100 Subject: [PATCH 17/43] Rebase: Simplify JsonChat usage. --- telegram-bot-api/Client.cpp | 51 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index e448b81..4e0a4a8 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -758,10 +758,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) { } @@ -992,8 +992,8 @@ 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_; }; @@ -1013,7 +1013,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: @@ -1043,7 +1043,7 @@ class Client::JsonMessageOrigin final : public td::Jsonable { case td_api::messageOriginChat::ID: { auto origin = static_cast(message_origin_); object("type", "chat"); - object("sender_chat", JsonChat(origin->sender_chat_id_, false, client_)); + object("sender_chat", JsonChat(origin->sender_chat_id_, client_)); if (!origin->author_signature_.empty()) { object("author_signature", origin->author_signature_); } @@ -1060,7 +1060,7 @@ class Client::JsonMessageOrigin final : public td::Jsonable { case td_api::messageOriginChannel::ID: { auto origin = static_cast(message_origin_); object("type", "channel"); - object("chat", JsonChat(origin->chat_id_, false, client_)); + 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_); @@ -1617,7 +1617,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: @@ -2019,8 +2019,7 @@ class Client::JsonGiveaway final : public td::Jsonable { 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, false, client); })); + 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_) { @@ -2174,7 +2173,7 @@ class Client::JsonExternalReplyInfo final : public td::Jsonable { 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_, false, client_)); + object("chat", JsonChat(reply_->chat_id_, client_)); if (reply_->message_id_ != 0) { object("message_id", as_client_message_id(reply_->message_id_)); } @@ -2310,9 +2309,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); @@ -2347,7 +2346,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } case td_api::messageOriginChat::ID: { auto forward_info = static_cast(message_->forward_origin.get()); - object("forward_from_chat", JsonChat(forward_info->sender_chat_id_, false, client_)); + object("forward_from_chat", JsonChat(forward_info->sender_chat_id_, client_)); if (!forward_info->author_signature_.empty()) { object("forward_signature", forward_info->author_signature_); } @@ -2362,7 +2361,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } case td_api::messageOriginChannel::ID: { auto forward_info = static_cast(message_->forward_origin.get()); - object("forward_from_chat", JsonChat(forward_info->chat_id_, false, client_)); + 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_); @@ -2764,7 +2763,7 @@ class Client::JsonDeletedMessage final : public td::Jsonable { 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("chat", JsonChat(chat_id_, client_)); object("date", 0); } @@ -3263,7 +3262,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_); @@ -3289,7 +3288,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_); @@ -3371,7 +3370,7 @@ class Client::JsonChatBoostUpdated 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("boost", JsonChatBoost(update_->boost_.get(), client_)); } @@ -3386,7 +3385,7 @@ class Client::JsonChatBoostRemoved 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("boost_id", update_->boost_->id_); object("remove_date", update_->boost_->start_date_); object("source", JsonChatBoostSource(update_->boost_->source_.get(), client_)); @@ -3617,7 +3616,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_); } } @@ -3634,10 +3633,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_); } } @@ -4511,7 +4510,7 @@ class Client::TdOnGetChatStickerSetCallback final : public TdQueryCallback { 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: @@ -4558,7 +4557,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: @@ -5182,7 +5181,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: @@ -5204,7 +5203,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: From 1084ebd5c8978c2da8295d641ea010c29a920fa5 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 3 Feb 2024 11:37:40 +0100 Subject: [PATCH 18/43] Rebase: Add Client::check_reply_parameters. --- telegram-bot-api/Client.cpp | 121 +++++++++++++++++------------------- telegram-bot-api/Client.h | 4 +- 2 files changed, 60 insertions(+), 65 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 4e0a4a8..9fe5402 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5840,27 +5840,33 @@ 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_reply_parameters(td::Slice chat_id_str, int64 reply_to_message_id, bool allow_sending_without_reply, + int64 message_thread_id, PromisedQueryPtr query, OnSuccess on_success) { + check_message(chat_id_str, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, + AccessRights::Write, "message to reply", std::move(query), + [this, message_thread_id, on_success = std::move(on_success)](int64 chat_id, int64 reply_to_message_id, + PromisedQueryPtr query) mutable { + if (message_thread_id <= 0) { + return on_success(chat_id, 0, reply_to_message_id, std::move(query)); + } - 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)); - } + 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, message_thread_id), - td::make_unique>( - this, chat_id, message_thread_id, reply_to_message_id, 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 @@ -9693,33 +9699,27 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &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)]( 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, + int64 reply_to_message_id, 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(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(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, reply_to_message_id, allow_sending_without_reply, message_thread_id, + std::move(query), std::move(on_success)); }); return td::Status::OK(); } @@ -11952,31 +11952,26 @@ void Client::do_send_message(object_ptr input_messa [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)]( 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, + int64 reply_to_message_id, 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(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(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, reply_to_message_id, allow_sending_without_reply, message_thread_id, + std::move(query), std::move(on_success)); }); } diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index c5b1e6a..7b4382c 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -349,8 +349,8 @@ 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_reply_parameters(td::Slice chat_id_str, int64 reply_to_message_id, bool allow_sending_without_reply, + 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); From e117cfa33f79f1cfcd526789870456fe43b60e46 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 3 Feb 2024 11:42:02 +0100 Subject: [PATCH 19/43] Rebase: Add class ReplyParameters and fields "reply_parameters". --- telegram-bot-api/Client.cpp | 112 +++++++++++++++++++++++++----------- telegram-bot-api/Client.h | 20 ++++++- 2 files changed, 96 insertions(+), 36 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 9fe5402..ff797a1 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -4273,12 +4273,12 @@ class Client::TdOnCheckMessageCallback final : public TdQueryCallback { 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)) { } @@ -4307,14 +4307,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_; }; @@ -5840,14 +5840,18 @@ void Client::check_message(td::Slice chat_id_str, int64 message_id, bool allow_e } template -void Client::check_reply_parameters(td::Slice chat_id_str, int64 reply_to_message_id, bool allow_sending_without_reply, +void Client::check_reply_parameters(td::Slice chat_id_str, InputReplyParameters &&reply_parameters, int64 message_thread_id, PromisedQueryPtr query, OnSuccess on_success) { - check_message(chat_id_str, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, + check_message(chat_id_str, reply_parameters.reply_to_message_id, + reply_parameters.reply_to_message_id <= 0 || reply_parameters.allow_sending_without_reply, AccessRights::Write, "message to reply", std::move(query), [this, message_thread_id, on_success = std::move(on_success)](int64 chat_id, int64 reply_to_message_id, PromisedQueryPtr query) mutable { + CheckedReplyParameters reply_parameters; + reply_parameters.reply_to_message_id = reply_to_message_id; + if (message_thread_id <= 0) { - return on_success(chat_id, 0, reply_to_message_id, std::move(query)); + return on_success(chat_id, 0, std::move(reply_parameters), std::move(query)); } if (reply_to_message_id != 0) { @@ -5859,12 +5863,12 @@ void Client::check_reply_parameters(td::Slice chat_id_str, int64 reply_to_messag } } if (reply_to_message_id == message_thread_id) { - return on_success(chat_id, message_thread_id, reply_to_message_id, std::move(query)); + return on_success(chat_id, message_thread_id, std::move(reply_parameters), std::move(query)); } 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), + this, chat_id, message_thread_id, std::move(reply_parameters), std::move(query), std::move(on_success))); }); } @@ -6682,13 +6686,51 @@ 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( + const CheckedReplyParameters &reply_parameters) { + if (reply_parameters.reply_to_message_id > 0) { + return make_object(0, reply_parameters.reply_to_message_id, nullptr); } 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(message_id, object.get_required_int_field("message_id")); + TRY_RESULT(allow_sending_without_reply, object.get_optional_bool_field("allow_sending_without_reply")); + + InputReplyParameters result; + result.reply_to_message_id = as_tdlib_message_id(td::max(message_id, 0)); + result.allow_sending_without_reply = allow_sending_without_reply; + 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(); @@ -9685,8 +9727,7 @@ td::Status Client::process_forward_message_query(PromisedQueryPtr &query) { 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_)); @@ -9696,14 +9737,15 @@ 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, disable_notification, protect_content, input_message_contents = std::move(input_message_contents), send_at = std::move(send_at), reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_thread_id, - int64 reply_to_message_id, + CheckedReplyParameters reply_parameters, PromisedQueryPtr query) mutable { auto &count = yet_unsent_message_count_[chat_id]; if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { @@ -9714,12 +9756,12 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { send_request( make_object( - chat_id, message_thread_id, get_input_message_reply_to(reply_to_message_id), + chat_id, message_thread_id, get_input_message_reply_to(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_reply_parameters(chat_id, reply_to_message_id, allow_sending_without_reply, message_thread_id, - 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(); } @@ -11928,8 +11970,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_); @@ -11949,14 +11994,15 @@ 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, disable_notification, protect_content, input_message_content = std::move(input_message_content), send_at = std::move(send_at), reply_markup = std::move(reply_markup)](int64 chat_id, int64 message_thread_id, - int64 reply_to_message_id, + CheckedReplyParameters reply_parameters, PromisedQueryPtr query) mutable { auto &count = yet_unsent_message_count_[chat_id]; if (count >= MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) { @@ -11964,14 +12010,14 @@ void Client::do_send_message(object_ptr input_messa } 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))); + send_request( + make_object(chat_id, message_thread_id, get_input_message_reply_to(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_reply_parameters(chat_id, reply_to_message_id, allow_sending_without_reply, message_thread_id, - 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)); }); } diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 7b4382c..7542cce 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -284,6 +284,15 @@ class Client final : public WebhookActor::Callback { virtual ~TdQueryCallback() = default; }; + struct InputReplyParameters { + int64 reply_to_message_id = 0; + bool allow_sending_without_reply = false; + }; + + struct CheckedReplyParameters { + int64 reply_to_message_id = 0; + }; + struct UserInfo; struct ChatInfo; struct BotCommandScope; @@ -349,8 +358,8 @@ class Client final : public WebhookActor::Callback { td::Slice message_type, PromisedQueryPtr query, OnSuccess on_success); template - void check_reply_parameters(td::Slice chat_id_str, int64 reply_to_message_id, bool allow_sending_without_reply, - int64 message_thread_id, PromisedQueryPtr query, OnSuccess on_success); + 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); @@ -386,7 +395,12 @@ 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( + const 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); From 19a7da41b23c9275efcf8c6f94e85a510a8ab5ee Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 14 Nov 2023 19:22:17 +0300 Subject: [PATCH 20/43] Improve error message. --- telegram-bot-api/Client.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index ff797a1..f9a0ca6 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -13173,7 +13173,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; } } From 94bfd307f85b58952cfd0fe07b450e4dc1a10fab Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 3 Feb 2024 11:43:50 +0100 Subject: [PATCH 21/43] Rebase: Support quote in ReplyParameters. --- telegram-bot-api/Client.cpp | 78 +++++++++++++++++++++---------------- telegram-bot-api/Client.h | 5 ++- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index f9a0ca6..d081a12 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5842,35 +5842,39 @@ void Client::check_message(td::Slice chat_id_str, int64 message_id, bool allow_e template void Client::check_reply_parameters(td::Slice chat_id_str, InputReplyParameters &&reply_parameters, int64 message_thread_id, PromisedQueryPtr query, OnSuccess on_success) { - check_message(chat_id_str, reply_parameters.reply_to_message_id, - reply_parameters.reply_to_message_id <= 0 || reply_parameters.allow_sending_without_reply, - AccessRights::Write, "message to reply", std::move(query), - [this, message_thread_id, on_success = std::move(on_success)](int64 chat_id, int64 reply_to_message_id, - PromisedQueryPtr query) mutable { - CheckedReplyParameters reply_parameters; - reply_parameters.reply_to_message_id = reply_to_message_id; + check_message( + chat_id_str, reply_parameters.reply_to_message_id, + reply_parameters.reply_to_message_id <= 0 || reply_parameters.allow_sending_without_reply, AccessRights::Write, + "message to reply", std::move(query), + [this, message_thread_id, quote = std::move(reply_parameters.quote), on_success = std::move(on_success)]( + int64 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.quote = std::move(quote); + } - if (message_thread_id <= 0) { - return on_success(chat_id, 0, std::move(reply_parameters), std::move(query)); - } + if (message_thread_id <= 0) { + return on_success(chat_id, 0, std::move(reply_parameters), std::move(query)); + } - 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, std::move(reply_parameters), std::move(query)); - } + 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, std::move(reply_parameters), std::move(query)); + } - 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))); - }); + 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))); + }); } template @@ -6687,9 +6691,10 @@ bool Client::to_bool(td::MutableSlice value) { } td_api::object_ptr Client::get_input_message_reply_to( - const CheckedReplyParameters &reply_parameters) { + CheckedReplyParameters &&reply_parameters) { if (reply_parameters.reply_to_message_id > 0) { - return make_object(0, reply_parameters.reply_to_message_id, nullptr); + return make_object(0, reply_parameters.reply_to_message_id, + std::move(reply_parameters.quote)); } return nullptr; } @@ -6724,10 +6729,15 @@ td::Result Client::get_reply_parameters(td::JsonVa auto &object = value.get_object(); 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"))); InputReplyParameters result; 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 = std::move(quote); return std::move(result); } @@ -9756,7 +9766,7 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { send_request( make_object( - chat_id, message_thread_id, get_input_message_reply_to(reply_parameters), + 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))); }; @@ -12010,11 +12020,11 @@ void Client::do_send_message(object_ptr input_messa } count++; - send_request( - make_object(chat_id, message_thread_id, get_input_message_reply_to(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))); + 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_reply_parameters(chat_id, std::move(reply_parameters), message_thread_id, std::move(query), std::move(on_success)); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 7542cce..e906481 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -287,10 +287,12 @@ class Client final : public WebhookActor::Callback { struct InputReplyParameters { int64 reply_to_message_id = 0; bool allow_sending_without_reply = false; + object_ptr quote; }; struct CheckedReplyParameters { int64 reply_to_message_id = 0; + object_ptr quote; }; struct UserInfo; @@ -395,8 +397,7 @@ class Client final : public WebhookActor::Callback { static bool to_bool(td::MutableSlice value); - static object_ptr get_input_message_reply_to( - const CheckedReplyParameters &reply_parameters); + static object_ptr get_input_message_reply_to(CheckedReplyParameters &&reply_parameters); static td::Result get_reply_parameters(const Query *query); From de0d0ad75dba5016e917216e8b9ac6c2085972e9 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 15 Nov 2023 16:18:00 +0300 Subject: [PATCH 22/43] Support chat in ReplyParameters. --- telegram-bot-api/Client.cpp | 98 ++++++++++++++++++++++++------------- telegram-bot-api/Client.h | 2 + 2 files changed, 67 insertions(+), 33 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index d081a12..5c989de 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5675,7 +5675,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)); @@ -5683,8 +5682,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 { @@ -5842,38 +5842,68 @@ void Client::check_message(td::Slice chat_id_str, int64 message_id, bool allow_e template void Client::check_reply_parameters(td::Slice chat_id_str, InputReplyParameters &&reply_parameters, int64 message_thread_id, PromisedQueryPtr query, OnSuccess on_success) { - check_message( - chat_id_str, reply_parameters.reply_to_message_id, - reply_parameters.reply_to_message_id <= 0 || reply_parameters.allow_sending_without_reply, AccessRights::Write, - "message to reply", std::move(query), - [this, message_thread_id, quote = std::move(reply_parameters.quote), on_success = std::move(on_success)]( - int64 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.quote = std::move(quote); - } - - if (message_thread_id <= 0) { - return on_success(chat_id, 0, std::move(reply_parameters), std::move(query)); - } - - 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 (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 (reply_to_message_id == message_thread_id) { - return on_success(chat_id, message_thread_id, std::move(reply_parameters), std::move(query)); + + 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)); } - 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))); + 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)); }); } @@ -6693,8 +6723,8 @@ bool Client::to_bool(td::MutableSlice value) { td_api::object_ptr Client::get_input_message_reply_to( CheckedReplyParameters &&reply_parameters) { if (reply_parameters.reply_to_message_id > 0) { - return make_object(0, reply_parameters.reply_to_message_id, - std::move(reply_parameters.quote)); + return make_object( + reply_parameters.reply_in_chat_id, reply_parameters.reply_to_message_id, std::move(reply_parameters.quote)); } return nullptr; } @@ -6727,6 +6757,7 @@ td::Result Client::get_reply_parameters(td::JsonVa 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")); @@ -6735,6 +6766,7 @@ td::Result Client::get_reply_parameters(td::JsonVa get_formatted_text(std::move(input_quote), std::move(parse_mode), object.extract_field("quote_entities"))); 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 = std::move(quote); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index e906481..b77ad42 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -285,12 +285,14 @@ class Client final : public WebhookActor::Callback { }; 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; }; From aa0f3691324a5fdb64311f47f27bb56f3fd8b4f0 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 4 Dec 2023 13:42:00 +0300 Subject: [PATCH 23/43] Update TDLib to 1.8.22. --- td | 2 +- telegram-bot-api/Client.cpp | 19 +++++++++++++------ telegram-bot-api/Client.h | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/td b/td index 9184b3e..4dbcfce 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 9184b3e62de59663a59d3500528aee7e5f0d83fa +Subproject commit 4dbcfce77d6f2ef95b3f1d2ef8ef6b0c66a4fd4d diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 5c989de..28de281 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2390,9 +2390,9 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("external_reply", JsonExternalReplyInfo(message_->reply_to_message.get(), client_)); } if (message_->reply_to_message != nullptr && message_->reply_to_message->quote_ != nullptr) { - object("quote", message_->reply_to_message->quote_->text_); - if (!message_->reply_to_message->quote_->entities_.empty()) { - object("quote_entities", JsonVectorEntities(message_->reply_to_message->quote_->entities_, client_)); + object("quote", message_->reply_to_message->quote_->text_->text_); + if (!message_->reply_to_message->quote_->text_->entities_.empty()) { + object("quote_entities", JsonVectorEntities(message_->reply_to_message->quote_->text_->entities_, client_)); } } if (message_->media_album_id != 0) { @@ -2723,6 +2723,9 @@ 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: @@ -2735,8 +2738,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("giveaway", JsonGiveaway(content, client_)); break; } - case td_api::messageStory::ID: - object("story", JsonEmptyObject()); + case td_api::messagePremiumGiveawayCompleted::ID: break; default: UNREACHABLE(); @@ -6769,7 +6771,7 @@ td::Result Client::get_reply_parameters(td::JsonVa 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 = std::move(quote); + result.quote = td_api::make_object(std::move(quote), 0); return std::move(result); } @@ -13136,6 +13138,8 @@ 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); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index b77ad42..3294c79 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -288,13 +288,13 @@ class Client final : public WebhookActor::Callback { td::string reply_in_chat_id; int64 reply_to_message_id = 0; bool allow_sending_without_reply = false; - object_ptr quote; + object_ptr quote; }; struct CheckedReplyParameters { int64 reply_in_chat_id = 0; int64 reply_to_message_id = 0; - object_ptr quote; + object_ptr quote; }; struct UserInfo; From 5dce30bc209554084e8cdf704f08a066527ee363 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 4 Dec 2023 14:18:57 +0300 Subject: [PATCH 24/43] Add class TextQuote. --- telegram-bot-api/Client.cpp | 26 ++++++++++++++++++++++---- telegram-bot-api/Client.h | 1 + 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 28de281..02febd5 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2294,6 +2294,27 @@ class Client::JsonExternalReplyInfo final : public td::Jsonable { } }; +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(); @@ -2390,10 +2411,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("external_reply", JsonExternalReplyInfo(message_->reply_to_message.get(), client_)); } if (message_->reply_to_message != nullptr && message_->reply_to_message->quote_ != nullptr) { - object("quote", message_->reply_to_message->quote_->text_->text_); - if (!message_->reply_to_message->quote_->text_->entities_.empty()) { - object("quote_entities", JsonVectorEntities(message_->reply_to_message->quote_->text_->entities_, client_)); - } + 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)); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 3294c79..4eb8f06 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -112,6 +112,7 @@ class Client final : public WebhookActor::Callback { class JsonMessageSender; class JsonMessageOrigin; class JsonExternalReplyInfo; + class JsonTextQuote; class JsonLinkPreviewOptions; class JsonAnimation; class JsonAudio; From 253d3acddca8bdcd4facd6e1ff44b4fc4c12d8bc Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 4 Dec 2023 14:31:45 +0300 Subject: [PATCH 25/43] Support quote position in reply parameters. --- telegram-bot-api/Client.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 02febd5..04b981b 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -6784,12 +6784,13 @@ td::Result Client::get_reply_parameters(td::JsonVa 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), 0); + result.quote = td_api::make_object(std::move(quote), quote_position); return std::move(result); } From e6e6cbc72fafe6ada3614691d03ee05b83ee0815 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 4 Dec 2023 15:23:59 +0300 Subject: [PATCH 26/43] Add Message.giveaway_completed. --- telegram-bot-api/Client.cpp | 69 ++++++++++++++++++++++++++----------- telegram-bot-api/Client.h | 1 + 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 04b981b..37dd5fd 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -998,6 +998,24 @@ class Client::JsonChat final : public td::Jsonable { int32 distance_; }; +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_, 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) @@ -2039,6 +2057,31 @@ class Client::JsonGiveaway final : public td::Jsonable { 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) { @@ -2756,8 +2799,11 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("giveaway", JsonGiveaway(content, client_)); break; } - case td_api::messagePremiumGiveawayCompleted::ID: + case td_api::messagePremiumGiveawayCompleted::ID: { + auto content = static_cast(message_->content.get()); + object("giveaway_completed", JsonGiveawayCompleted(content, message_->chat_id, client_)); break; + } default: UNREACHABLE(); } @@ -2775,24 +2821,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_, 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) { @@ -13047,6 +13075,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr Date: Mon, 4 Dec 2023 15:32:17 +0300 Subject: [PATCH 27/43] Store identifier of inaccessible pinned message. --- telegram-bot-api/Client.cpp | 12 ++++-------- telegram-bot-api/Client.h | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 37dd5fd..a8e50ee 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -998,9 +998,9 @@ class Client::JsonChat final : public td::Jsonable { int32 distance_; }; -class Client::JsonDeletedMessage final : public td::Jsonable { +class Client::JsonInaccessibleMessage final : public td::Jsonable { public: - JsonDeletedMessage(int64 chat_id, int64 message_id, const Client *client) + 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 { @@ -2675,6 +2675,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; @@ -2956,7 +2957,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_); @@ -13144,11 +13145,6 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr Date: Mon, 11 Dec 2023 20:55:10 +0300 Subject: [PATCH 28/43] Add deleteMessages method. --- telegram-bot-api/Client.cpp | 124 ++++++++++++++++++++++++++++++++++++ telegram-bot-api/Client.h | 10 +++ 2 files changed, 134 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index a8e50ee..182d24c 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -261,6 +261,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); @@ -4319,6 +4320,57 @@ 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: @@ -5888,6 +5940,24 @@ void Client::check_message(td::Slice chat_id_str, int64 message_id, bool allow_e }); } +template +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"); + } + + 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))); + }); +} + template void Client::check_reply_parameters(td::Slice chat_id_str, InputReplyParameters &&reply_parameters, int64 message_thread_id, PromisedQueryPtr query, OnSuccess on_success) { @@ -9057,6 +9127,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()) { @@ -10073,6 +10183,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)), diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index cc2e45b..54bc373 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -323,6 +323,8 @@ class Client final : public WebhookActor::Callback { template class TdOnCheckMessageCallback; template + class TdOnCheckMessagesCallback; + template class TdOnCheckMessageThreadCallback; template class TdOnCheckRemoteFileIdCallback; @@ -363,6 +365,10 @@ class Client final : public WebhookActor::Callback { void check_message(td::Slice chat_id_str, int64 message_id, bool allow_empty, AccessRights access_rights, td::Slice message_type, PromisedQueryPtr query, OnSuccess on_success); + template + 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); @@ -575,6 +581,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")); @@ -655,6 +664,7 @@ class Client final : public WebhookActor::Callback { 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); From 9c1ecb749bf419d891560cd0251aea85e4004c0a Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 12 Dec 2023 14:37:51 +0300 Subject: [PATCH 29/43] Add forwardMessages method. --- telegram-bot-api/Client.cpp | 87 ++++++++++++++++++++++++++++++++++++- telegram-bot-api/Client.h | 4 +- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 182d24c..3ebca6e 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -252,6 +252,7 @@ bool Client::init_methods() { methods_.emplace("stoppoll", &Client::process_stop_poll_query); methods_.emplace("copymessage", &Client::process_copy_message_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("editmessagetext", &Client::process_edit_message_text_query); @@ -3963,6 +3964,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) @@ -9346,7 +9385,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.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) { @@ -9926,6 +9969,48 @@ 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")); + + auto on_success = [this, from_chat_id = from_chat_id.str(), message_ids = std::move(message_ids), + 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]( + int64 from_chat_id, td::vector message_ids, PromisedQueryPtr query) { + 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), 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"); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 54bc373..09cc421 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; @@ -213,6 +213,7 @@ class Client final : public WebhookActor::Callback { class TdOnGetUserProfilePhotosCallback; class TdOnSendMessageCallback; class TdOnSendMessageAlbumCallback; + class TdOnForwardMessagesCallback; class TdOnDeleteFailedToSendMessageCallback; class TdOnEditMessageCallback; class TdOnEditInlineMessageCallback; @@ -656,6 +657,7 @@ class Client final : public WebhookActor::Callback { td::Status process_stop_poll_query(PromisedQueryPtr &query); td::Status process_copy_message_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_edit_message_text_query(PromisedQueryPtr &query); From 81b2c0e5500881f7cebe287a1ea97bbaeb3ffdd8 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 12 Dec 2023 14:48:35 +0300 Subject: [PATCH 30/43] Add copyMessages method. --- telegram-bot-api/Client.cpp | 47 ++++++++++++++++++++++++++++++++++++- telegram-bot-api/Client.h | 1 + 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 3ebca6e..4182597 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -251,6 +251,7 @@ 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); @@ -9385,7 +9386,7 @@ void Client::on_message_send_succeeded(object_ptr &&message, in auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id); auto &query = *pending_send_message_queries_[query_id]; if (query.is_multisend) { - if (query.query->method() == "forwardmessages") { + 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))); @@ -9957,6 +9958,50 @@ 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")); + + auto on_success = [this, from_chat_id = from_chat_id.str(), message_ids = std::move(message_ids), + disable_notification, protect_content, + 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]( + int64 from_chat_id, td::vector message_ids, PromisedQueryPtr query) { + 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), 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()); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 09cc421..8c927c7 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -656,6 +656,7 @@ 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); From 92b7a6a55655df60acd63f8cc332929a39b899e4 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Dec 2023 15:14:10 +0300 Subject: [PATCH 31/43] Add Chat.available_reactions. --- telegram-bot-api/Client.cpp | 46 +++++++++++++++++++++++++++++++++++++ telegram-bot-api/Client.h | 2 ++ 2 files changed, 48 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 4182597..9741455 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -654,6 +654,32 @@ 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::JsonChatPermissions final : public td::Jsonable { public: explicit JsonChatPermissions(const td_api::chatPermissions *chat_permissions) : chat_permissions_(chat_permissions) { @@ -975,6 +1001,11 @@ 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->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) { @@ -6519,6 +6550,9 @@ 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_; + 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->has_protected_content = chat->has_protected_content_; @@ -6552,6 +6586,18 @@ void Client::on_update(object_ptr result) { chat_info->message_auto_delete_time = update->message_auto_delete_time_; 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::updateChatAccentColor::ID: { auto update = move_object_as(result); auto chat_info = add_chat(update->chat_id_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 8c927c7..25bdcb2 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -104,6 +104,7 @@ class Client final : public WebhookActor::Callback { class JsonDatedFiles; class JsonUser; class JsonUsers; + class JsonReactionType; class JsonChatPermissions; class JsonChatPhotoInfo; class JsonChatLocation; @@ -941,6 +942,7 @@ class Client final : public WebhookActor::Callback { int32 accent_color_id = -1; int64 background_custom_emoji_id = 0; bool has_protected_content = false; + object_ptr available_reactions; object_ptr photo_info; object_ptr permissions; union { From 7d84c0a0d807a662b702af4b40a04c84d9ea8c18 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Dec 2023 16:05:37 +0300 Subject: [PATCH 32/43] Update TDLib to 1.8.23. --- td | 2 +- telegram-bot-api/Client.cpp | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/td b/td index 4dbcfce..27c3eae 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 4dbcfce77d6f2ef95b3f1d2ef8ef6b0c66a4fd4d +Subproject commit 27c3eaeb4964bd5f18d8488e354abde1a4383e49 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 9741455..d0f0566 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2033,16 +2033,16 @@ 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::JsonChatShared final : public td::Jsonable { @@ -2809,8 +2809,8 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } break; } - case td_api::messageUserShared::ID: { - auto content = static_cast(message_->content.get()); + case td_api::messageUsersShared::ID: { + auto content = static_cast(message_->content.get()); object("user_shared", JsonUserShared(content)); break; } @@ -2834,6 +2834,8 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("giveaway", JsonGiveaway(content, client_)); break; } + case td_api::messagePremiumGiveawayWinners::ID: + break; case td_api::messagePremiumGiveawayCompleted::ID: { auto content = static_cast(message_->content.get()); object("giveaway_completed", JsonGiveawayCompleted(content, message_->chat_id, client_)); @@ -6598,17 +6600,11 @@ void Client::on_update(object_ptr result) { } break; } - case td_api::updateChatAccentColor::ID: { - auto update = move_object_as(result); + 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_; - break; - } - case td_api::updateChatBackgroundCustomEmoji::ID: { - auto update = move_object_as(result); - auto chat_info = add_chat(update->chat_id_); - CHECK(chat_info->type != ChatInfo::Type::Unknown); chat_info->background_custom_emoji_id = update->background_custom_emoji_id_; break; } @@ -7029,8 +7025,8 @@ td::Result> Client::get_keyboard_butt 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")); 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, 1)); } if (object.has_field("request_chat")) { @@ -13482,6 +13478,8 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr Date: Wed, 20 Dec 2023 16:09:15 +0300 Subject: [PATCH 33/43] Add fields chat.profile_accent_color_id and chat.profile_background_custom_emoji_id. --- telegram-bot-api/Client.cpp | 10 ++++++++++ telegram-bot-api/Client.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index d0f0566..fb386b8 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1011,6 +1011,12 @@ class Client::JsonChat final : public td::Jsonable { 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()); } @@ -6557,6 +6563,8 @@ void Client::on_update(object_ptr result) { } 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; } @@ -6606,6 +6614,8 @@ void Client::on_update(object_ptr result) { 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: { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 25bdcb2..2ae577d 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -940,7 +940,9 @@ class Client final : public WebhookActor::Callback { td::string title; int32 message_auto_delete_time = 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; From 9aedc15f76b2868c62569e61de6107d00e927caa Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Dec 2023 16:19:06 +0300 Subject: [PATCH 34/43] Add "has_public_winners" and "prize_description" giveaway fields. --- telegram-bot-api/Client.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index fb386b8..a85f901 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2083,10 +2083,16 @@ class Client::JsonGiveaway final : public td::Jsonable { 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_); } From fa489a49795d01c18a24ed18aa683364041205f7 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Dec 2023 16:44:01 +0300 Subject: [PATCH 35/43] Add Message.giveaway_winners. --- telegram-bot-api/Client.cpp | 50 ++++++++++++++++++++++++++++++++++--- telegram-bot-api/Client.h | 1 + 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index a85f901..2a2b679 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2103,6 +2103,43 @@ class Client::JsonGiveaway final : public td::Jsonable { 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, @@ -2363,6 +2400,11 @@ class Client::JsonExternalReplyInfo final : public td::Jsonable { 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; @@ -2846,8 +2888,11 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { object("giveaway", JsonGiveaway(content, client_)); break; } - case td_api::messagePremiumGiveawayWinners::ID: + 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_)); @@ -13388,6 +13433,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr Date: Wed, 20 Dec 2023 17:35:18 +0300 Subject: [PATCH 36/43] Add "message_reaction" updates. --- telegram-bot-api/Client.cpp | 53 +++++++++++++++++++++++++++++++++++++ telegram-bot-api/Client.h | 11 +++++--- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 2a2b679..1a3a82e 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -3571,6 +3571,41 @@ 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::JsonUpdateTypes final : public td::Jsonable { public: explicit JsonUpdateTypes(td::uint32 update_types) : update_types_(update_types) { @@ -6837,6 +6872,9 @@ void Client::on_update(object_ptr result) { 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::updateConnectionState::ID: { auto update = move_object_as(result); if (update->state_->get_id() == td_api::connectionStateReady::ID) { @@ -13074,6 +13112,8 @@ td::Slice Client::get_update_type_name(UpdateType update_type) { return td::Slice("chat_boost"); case UpdateType::ChatBoostRemoved: return td::Slice("removed_chat_boost"); + case UpdateType::MessageReaction: + return td::Slice("message_reaction"); default: UNREACHABLE(); return td::Slice(); @@ -13398,6 +13438,19 @@ void Client::add_update_chat_boost(object_ptr &&update) } } +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(); + } +} + 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_) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 01b602a..ab1f33e 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -173,6 +173,7 @@ class Client final : public WebhookActor::Callback { class JsonForumTopicEdited; class JsonForumTopicInfo; class JsonGameHighScore; + class JsonMessageReactionUpdated; class JsonAddress; class JsonOrderInfo; class JsonSuccessfulPaymentBot; @@ -1126,6 +1127,8 @@ class Client final : public WebhookActor::Callback { void add_update_chat_boost(object_ptr &&update); + void add_update_message_reaction(object_ptr &&update); + // append only before Size enum class UpdateType : int32 { Message, @@ -1146,6 +1149,7 @@ class Client final : public WebhookActor::Callback { ChatJoinRequest, ChatBoostUpdated, ChatBoostRemoved, + MessageReaction, Size }; @@ -1175,9 +1179,10 @@ class Client final : public WebhookActor::Callback { bool have_message_access(int64 chat_id) const; - // by default ChatMember updates are disabled - static constexpr td::uint32 DEFAULT_ALLOWED_UPDATE_TYPES = - (1 << static_cast(UpdateType::Size)) - 1 - (1 << static_cast(UpdateType::ChatMember)); + // by default ChatMember and MessageReaction 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::MessageReaction)); object_ptr authorization_state_; bool was_authorized_ = false; From 50bb07bc465c9016662ac5d84d992958bba3fb4c Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Dec 2023 17:45:26 +0300 Subject: [PATCH 37/43] Add "message_reaction_count" update. --- telegram-bot-api/Client.cpp | 51 +++++++++++++++++++++++++++++++++++++ telegram-bot-api/Client.h | 14 +++++++--- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 1a3a82e..e3c49e6 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -680,6 +680,20 @@ class Client::JsonReactionType final : public td::Jsonable { 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) { @@ -3606,6 +3620,25 @@ class Client::JsonMessageReactionUpdated final : public td::Jsonable { 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) { @@ -6875,6 +6908,9 @@ void Client::on_update(object_ptr result) { 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) { @@ -13114,6 +13150,8 @@ td::Slice Client::get_update_type_name(UpdateType update_type) { 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(); @@ -13451,6 +13489,19 @@ 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(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_) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index ab1f33e..6bcfa76 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -105,6 +105,7 @@ class Client final : public WebhookActor::Callback { class JsonUser; class JsonUsers; class JsonReactionType; + class JsonReactionCount; class JsonChatPermissions; class JsonChatPhotoInfo; class JsonChatLocation; @@ -174,6 +175,7 @@ class Client final : public WebhookActor::Callback { class JsonForumTopicInfo; class JsonGameHighScore; class JsonMessageReactionUpdated; + class JsonMessageReactionCountUpdated; class JsonAddress; class JsonOrderInfo; class JsonSuccessfulPaymentBot; @@ -1129,6 +1131,8 @@ class Client final : public WebhookActor::Callback { 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, @@ -1150,6 +1154,7 @@ class Client final : public WebhookActor::Callback { ChatBoostUpdated, ChatBoostRemoved, MessageReaction, + MessageReactionCount, Size }; @@ -1179,10 +1184,11 @@ class Client final : public WebhookActor::Callback { bool have_message_access(int64 chat_id) const; - // by default ChatMember and MessageReaction 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::MessageReaction)); + // 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::MessageReaction)) - + (1 << static_cast(UpdateType::MessageReactionCount)); object_ptr authorization_state_; bool was_authorized_ = false; From f19e58645e43af7669fb8a66aa46130421d2aed7 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Dec 2023 17:57:39 +0300 Subject: [PATCH 38/43] Add setMessageReaction method. --- telegram-bot-api/Client.cpp | 64 +++++++++++++++++++++++++++++++++++++ telegram-bot-api/Client.h | 5 +++ 2 files changed, 69 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index e3c49e6..8c27f53 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -256,6 +256,7 @@ bool Client::init_methods() { 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); @@ -9315,6 +9316,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); @@ -10291,6 +10339,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"); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 6bcfa76..a863d72 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -579,6 +579,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()); @@ -666,6 +670,7 @@ class Client final : public WebhookActor::Callback { 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); From d3300e9ba3a8142419ee312a83cf9f35b79ca254 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 3 Feb 2024 11:45:24 +0100 Subject: [PATCH 39/43] Rebase: Support channel emoji status. --- td | 2 +- telegram-bot-api/Client.cpp | 28 ++++++++++++++++++++-------- telegram-bot-api/Client.h | 4 ++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/td b/td index 27c3eae..9184b3e 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 27c3eaeb4964bd5f18d8488e354abde1a4383e49 +Subproject commit 9184b3e62de59663a59d3500528aee7e5f0d83fa diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 8c27f53..e493b6c 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -844,12 +844,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); } @@ -1016,6 +1010,12 @@ 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_, @@ -6678,6 +6678,10 @@ 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_); } @@ -6716,6 +6720,16 @@ 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_); @@ -12852,8 +12866,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_; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index a863d72..f2975d7 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -871,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; @@ -948,6 +946,8 @@ 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; From baa0546c3120755e9d5f1c1a4984550f593596f8 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Dec 2023 23:26:43 +0300 Subject: [PATCH 40/43] Support request_users.max_quantity. --- telegram-bot-api/Client.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index e493b6c..102d319 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -7166,17 +7166,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, 1)); + 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")) { From a38d9260af9fcce35e77ad76d3cbf474cd67fd71 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Dec 2023 23:32:52 +0300 Subject: [PATCH 41/43] Add Message.users_shared field. --- telegram-bot-api/Client.cpp | 19 ++++++++++++++++++- telegram-bot-api/Client.h | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 102d319..8897a2a 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2066,6 +2066,20 @@ class Client::JsonUserShared final : public td::Jsonable { 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 { public: explicit JsonChatShared(const td_api::messageChatShared *chat_shared) : chat_shared_(chat_shared) { @@ -2880,7 +2894,10 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } case td_api::messageUsersShared::ID: { auto content = static_cast(message_->content.get()); - object("user_shared", JsonUserShared(content)); + if (content->user_ids_.size() == 1) { + object("user_shared", JsonUserShared(content)); + } + object("users_shared", JsonUsersShared(content)); break; } case td_api::messageChatShared::ID: { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index f2975d7..225f38b 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -190,6 +190,7 @@ class Client final : public WebhookActor::Callback { class JsonChatSetMessageAutoDeleteTime; class JsonWriteAccessAllowed; class JsonUserShared; + class JsonUsersShared; class JsonChatShared; class JsonGiveaway; class JsonGiveawayWinners; From 1bf69f7abbb5a532c11fb1523e16c093da873641 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 28 Dec 2023 00:42:15 +0300 Subject: [PATCH 42/43] Update version to 7.0. --- CMakeLists.txt | 2 +- telegram-bot-api/telegram-bot-api.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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(); From d2415a919145cc001f39dc0a2ac69998c280fb6c Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 3 Feb 2024 12:34:51 +0100 Subject: [PATCH 43/43] Update and rebase to version 7.0 Fix a bunch of small issues here and there related to new API calls. Custom DeleteMessages (bulk) has been deleted in favour of the upstream implementation that allows deleting a list of messages (instead of a range). Documentation might need a redo :) --- td | 2 +- tdlight-api-openapi.yaml | 96 ------------------------------------- telegram-bot-api/Client.cpp | 64 +++++-------------------- telegram-bot-api/Client.h | 1 - 4 files changed, 13 insertions(+), 150 deletions(-) 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 8897a2a..da3fb92 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -345,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); @@ -10231,9 +10230,10 @@ td::Status Client::process_copy_messages_query(PromisedQueryPtr &query) { 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, + 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); @@ -10243,8 +10243,8 @@ td::Status Client::process_copy_messages_query(PromisedQueryPtr &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]( - int64 from_chat_id, td::vector message_ids, PromisedQueryPtr 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)); @@ -10255,7 +10255,7 @@ td::Status Client::process_copy_messages_query(PromisedQueryPtr &query) { send_request(make_object( chat_id, message_thread_id, from_chat_id, std::move(message_ids), - get_message_send_options(disable_notification, protect_content), true, remove_caption), + 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))); }); }; @@ -10286,8 +10286,10 @@ td::Status Client::process_forward_messages_query(PromisedQueryPtr &query) { } 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); @@ -10297,8 +10299,8 @@ td::Status Client::process_forward_messages_query(PromisedQueryPtr &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]( - int64 from_chat_id, td::vector message_ids, PromisedQueryPtr 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)); @@ -10309,7 +10311,7 @@ td::Status Client::process_forward_messages_query(PromisedQueryPtr &query) { send_request(make_object( chat_id, message_thread_id, from_chat_id, std::move(message_ids), - get_message_send_options(disable_notification, protect_content), false, false), + 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))); }); }; @@ -10351,7 +10353,7 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { send_request( make_object( chat_id, message_thread_id, get_input_message_reply_to(std::move(reply_parameters)), - get_message_send_options(disable_notification, protect_content), std::move(send_at), std::move(input_message_contents)), + 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_reply_parameters(chat_id, std::move(reply_parameters), message_thread_id, std::move(query), @@ -11893,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(); @@ -12636,7 +12596,7 @@ void Client::do_send_message(object_ptr input_messa 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), + 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))); }; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 225f38b..ef7ec91 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -754,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);