From 9a575cdb5658b648881a654f252ce09d3fbf6182 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Jan 2021 15:14:16 +0300 Subject: [PATCH 001/232] Forcely ignore updateChat received in getDifference. --- td/telegram/UpdatesManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 106380ad1..47ca2e19b 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1195,6 +1195,10 @@ void UpdatesManager::process_get_difference_updates( CHECK(!running_get_difference_); } + if (constructor_id == telegram_api::updateChat::ID) { + update = nullptr; + } + if (constructor_id == telegram_api::updateChannel::ID) { update = nullptr; } From 31265c93f1b4355c361a356173b53811c8f64080 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 21 Jan 2021 16:49:41 +0300 Subject: [PATCH 002/232] Add ability to check whether a user is a mutual contact. --- td/telegram/ContactsManager.cpp | 12 ++++++------ td/telegram/ContactsManager.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 28dc00e35..34f169dcd 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -8764,7 +8764,7 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo } if (u->is_is_contact_changed) { td_->messages_manager_->on_dialog_user_is_contact_updated(DialogId(user_id), u->is_contact); - if (is_user_contact(u, user_id)) { + if (is_user_contact(u, user_id, false)) { auto user_full = get_user_full(user_id); if (user_full != nullptr && user_full->need_phone_number_privacy_exception) { on_update_user_full_need_phone_number_privacy_exception(user_full, user_id, false); @@ -10619,12 +10619,12 @@ bool ContactsManager::on_get_channel_error(ChannelId channel_id, const Status &s return false; } -bool ContactsManager::is_user_contact(UserId user_id) const { - return is_user_contact(get_user(user_id), user_id); +bool ContactsManager::is_user_contact(UserId user_id, bool is_mutual) const { + return is_user_contact(get_user(user_id), user_id, is_mutual); } -bool ContactsManager::is_user_contact(const User *u, UserId user_id) const { - return u != nullptr && u->is_contact && user_id != get_my_id(); +bool ContactsManager::is_user_contact(const User *u, UserId user_id, bool is_mutual) const { + return u != nullptr && (is_mutual ? u->is_mutual_contact : u->is_contact) && user_id != get_my_id(); } void ContactsManager::on_get_channel_participants_success( @@ -12299,7 +12299,7 @@ void ContactsManager::on_update_channel_participant(ChannelId channel_id, UserId } void ContactsManager::update_contacts_hints(const User *u, UserId user_id, bool from_database) { - bool is_contact = is_user_contact(u, user_id); + bool is_contact = is_user_contact(u, user_id, false); if (td_->auth_manager_->is_bot()) { LOG_IF(ERROR, is_contact) << "Bot has " << user_id << " in the contacts list"; return; diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 00e5667c2..3aa111f24 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -422,7 +422,7 @@ class ContactsManager : public Actor { vector get_inactive_channels(Promise &&promise); - bool is_user_contact(UserId user_id) const; + bool is_user_contact(UserId user_id, bool is_mutual = false) const; bool is_user_deleted(UserId user_id) const; @@ -1317,7 +1317,7 @@ class ContactsManager : public Actor { bool is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id); - bool is_user_contact(const User *u, UserId user_id) const; + bool is_user_contact(const User *u, UserId user_id, bool is_mutual) const; int32 get_user_was_online(const User *u, UserId user_id) const; From 6729581d038180220d59ab696921ef1ce3d64043 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 21 Jan 2021 23:19:40 +0300 Subject: [PATCH 003/232] Clamp wrong dialog action progress instead of ignoring. --- td/telegram/DialogAction.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/td/telegram/DialogAction.cpp b/td/telegram/DialogAction.cpp index 0ad50a6e6..d4b9f8a64 100644 --- a/td/telegram/DialogAction.cpp +++ b/td/telegram/DialogAction.cpp @@ -6,6 +6,8 @@ // #include "td/telegram/DialogAction.h" +#include "td/utils/misc.h" + namespace td { void DialogAction::init(Type type) { @@ -14,11 +16,8 @@ void DialogAction::init(Type type) { } void DialogAction::init(Type type, int32 progress) { - if (progress < 0 || progress > 100) { - progress = 0; - } type_ = type; - progress_ = progress; + progress_ = clamp(progress, 0, 100); } DialogAction::DialogAction(Type type, int32 progress) { From 9c961b2eb87cd1f5275de7716ff637705afe480a Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 21 Jan 2021 23:37:00 +0300 Subject: [PATCH 004/232] Optimize database_message_id updating in on_get_history. --- td/telegram/MessagesManager.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index f64f20861..170accb66 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8903,7 +8903,6 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ } if (need_update_database_message_ids) { - bool is_dialog_updated = false; if (!d->last_database_message_id.is_valid()) { CHECK(d->last_message_id.is_valid()); MessagesConstIterator it(d, d->last_message_id); @@ -8918,7 +8917,6 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ } --it; } - is_dialog_updated = true; } else { LOG_CHECK(d->last_new_message_id.is_valid()) << dialog_id << " " << from_the_end << " " << d->first_database_message_id << " " @@ -8933,28 +8931,34 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ { MessagesConstIterator it(d, d->first_database_message_id); if (*it != nullptr && ((*it)->message_id == d->first_database_message_id || (*it)->have_next)) { + MessageId new_first_database_message_id = d->first_database_message_id; while (*it != nullptr) { auto message_id = (*it)->message_id; - if ((message_id.is_server() || message_id.is_local()) && message_id < d->first_database_message_id) { - set_dialog_first_database_message_id(d, message_id, "on_get_history 2"); + if ((message_id.is_server() || message_id.is_local()) && message_id < new_first_database_message_id) { + new_first_database_message_id = message_id; try_restore_dialog_reply_markup(d, *it); - is_dialog_updated = true; } --it; } + if (new_first_database_message_id != d->first_database_message_id) { + set_dialog_first_database_message_id(d, new_first_database_message_id, "on_get_history 2"); + } } } { MessagesConstIterator it(d, d->last_database_message_id); if (*it != nullptr && ((*it)->message_id == d->last_database_message_id || (*it)->have_next)) { + MessageId new_last_database_message_id = d->last_database_message_id; while (*it != nullptr) { auto message_id = (*it)->message_id; - if ((message_id.is_server() || message_id.is_local()) && message_id > d->last_database_message_id) { - set_dialog_last_database_message_id(d, message_id, "on_get_history 2"); - is_dialog_updated = true; + if ((message_id.is_server() || message_id.is_local()) && message_id > new_last_database_message_id) { + new_last_database_message_id = message_id; } ++it; } + if (new_last_database_message_id != d->last_database_message_id) { + set_dialog_last_database_message_id(d, new_last_database_message_id, "on_get_history 2"); + } } } } @@ -8974,10 +8978,6 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ first_message_id = first_added_message_id; } } - - if (is_dialog_updated) { - on_dialog_updated(dialog_id, "on_get_history"); - } } } From ae59f017e7b12af0946ccdd7143f363d9329e804 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 23 Jan 2021 21:39:41 +0300 Subject: [PATCH 005/232] Fix expires_in == 0 in schedule_get_promo_data. --- td/telegram/Td.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 9d582a3d2..20e8483b1 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3260,7 +3260,7 @@ void Td::on_get_promo_data(Resultis_authorized() && !auth_manager_->is_bot()) { LOG(INFO) << "Schedule getPromoData in " << expires_in; alarm_timeout_.set_timeout_in(PROMO_DATA_ALARM_ID, expires_in); From 6e20aaa4281e61173a191db5b5affdc76ed9497b Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 23 Jan 2021 22:04:35 +0300 Subject: [PATCH 006/232] Add source to get_dimensions. --- td/telegram/DocumentsManager.cpp | 4 ++-- td/telegram/MessageContent.cpp | 27 +++++++++++++++------------ td/telegram/Photo.cpp | 24 ++++++++++++------------ td/telegram/Photo.h | 2 +- td/telegram/StickersManager.cpp | 5 +++-- td/telegram/WebPageBlock.cpp | 6 +++--- td/telegram/WebPagesManager.cpp | 2 +- 7 files changed, 37 insertions(+), 33 deletions(-) diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index 2d6570841..75106114f 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -78,7 +78,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo switch (attribute->get_id()) { case telegram_api::documentAttributeImageSize::ID: { auto image_size = move_tl_object_as(attribute); - dimensions = get_dimensions(image_size->w_, image_size->h_); + dimensions = get_dimensions(image_size->w_, image_size->h_, "documentAttributeImageSize"); break; } case telegram_api::documentAttributeAnimated::ID: @@ -111,7 +111,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo if (video != nullptr) { video_duration = video->duration_; if (dimensions.width == 0) { - dimensions = get_dimensions(video->w_, video->h_); + dimensions = get_dimensions(video->w_, video->h_, "documentAttributeVideo"); } if (animated != nullptr) { diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 530dfdec3..d10679aeb 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -1621,7 +1621,7 @@ static Result create_input_message_content( td->animations_manager_->create_animation( file_id, string(), thumbnail, AnimationSize(), has_stickers, std::move(sticker_file_ids), std::move(file_name), std::move(mime_type), input_animation->duration_, - get_dimensions(input_animation->width_, input_animation->height_), false); + get_dimensions(input_animation->width_, input_animation->height_, "inputMessageAnimation"), false); content = make_unique(file_id, std::move(caption)); break; @@ -1690,7 +1690,7 @@ static Result create_input_message_content( PhotoSize s; s.type = type; - s.dimensions = get_dimensions(input_photo->width_, input_photo->height_); + s.dimensions = get_dimensions(input_photo->width_, input_photo->height_, "inputMessagePhoto"); s.size = static_cast(file_view.size()); s.file_id = file_id; @@ -1713,9 +1713,10 @@ static Result create_input_message_content( emoji = std::move(input_sticker->emoji_); - td->stickers_manager_->create_sticker(file_id, string(), thumbnail, - get_dimensions(input_sticker->width_, input_sticker->height_), nullptr, - false, nullptr); + td->stickers_manager_->create_sticker( + file_id, string(), thumbnail, + get_dimensions(input_sticker->width_, input_sticker->height_, "inputMessageSticker"), nullptr, false, + nullptr); content = make_unique(file_id); break; @@ -1726,10 +1727,11 @@ static Result create_input_message_content( ttl = input_video->ttl_; bool has_stickers = !sticker_file_ids.empty(); - td->videos_manager_->create_video( - file_id, string(), thumbnail, AnimationSize(), has_stickers, std::move(sticker_file_ids), - std::move(file_name), std::move(mime_type), input_video->duration_, - get_dimensions(input_video->width_, input_video->height_), input_video->supports_streaming_, false); + td->videos_manager_->create_video(file_id, string(), thumbnail, AnimationSize(), has_stickers, + std::move(sticker_file_ids), std::move(file_name), std::move(mime_type), + input_video->duration_, + get_dimensions(input_video->width_, input_video->height_, "inputMessageVideo"), + input_video->supports_streaming_, false); content = make_unique(file_id, std::move(caption)); break; @@ -1743,7 +1745,7 @@ static Result create_input_message_content( } td->video_notes_manager_->create_video_note(file_id, string(), thumbnail, input_video_note->duration_, - get_dimensions(length, length), false); + get_dimensions(length, length, "inputMessageVideoNote"), false); content = make_unique(file_id, false); break; @@ -1834,7 +1836,8 @@ static Result create_input_message_content( PhotoSize s; s.type = 'n'; - s.dimensions = get_dimensions(input_invoice->photo_width_, input_invoice->photo_height_); + s.dimensions = + get_dimensions(input_invoice->photo_width_, input_invoice->photo_height_, "inputMessageInvoice"); s.size = input_invoice->photo_size_; // TODO use invoice_file_id size s.file_id = invoice_file_id; @@ -2075,7 +2078,7 @@ Result get_input_message_content( LOG(WARNING) << "Ignore thumbnail file: " << r_thumbnail_file_id.error().message(); } else { thumbnail.type = 't'; - thumbnail.dimensions = get_dimensions(input_thumbnail->width_, input_thumbnail->height_); + thumbnail.dimensions = get_dimensions(input_thumbnail->width_, input_thumbnail->height_, "inputThumbnail"); thumbnail.file_id = r_thumbnail_file_id.ok(); CHECK(thumbnail.file_id.is_valid()); diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index 282a4d474..3d0950f0d 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -29,18 +29,18 @@ namespace td { -static uint16 get_dimension(int32 size) { +static uint16 get_dimension(int32 size, const char *source) { if (size < 0 || size > 65535) { - LOG(ERROR) << "Wrong image dimension = " << size; + LOG(ERROR) << "Wrong image dimension = " << size << " from " << source; return 0; } return narrow_cast(size); } -Dimensions get_dimensions(int32 width, int32 height) { +Dimensions get_dimensions(int32 width, int32 height, const char *source) { Dimensions result; - result.width = get_dimension(width); - result.height = get_dimension(height); + result.width = get_dimension(width, source); + result.height = get_dimension(height, source); if (result.width == 0 || result.height == 0) { result.width = 0; result.height = 0; @@ -338,7 +338,7 @@ PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice } PhotoSize res; res.type = 't'; - res.dimensions = get_dimensions(width, height); + res.dimensions = get_dimensions(width, height, "get_secret_thumbnail_photo_size"); res.size = narrow_cast(bytes.size()); // generate some random remote location to save @@ -374,7 +374,7 @@ Variant get_photo_size(FileManager *file_manager, PhotoSizeSo type = std::move(size->type_); location = std::move(size->location_); - res.dimensions = get_dimensions(size->w_, size->h_); + res.dimensions = get_dimensions(size->w_, size->h_, "photoSize"); res.size = size->size_; break; @@ -385,7 +385,7 @@ Variant get_photo_size(FileManager *file_manager, PhotoSizeSo type = std::move(size->type_); location = std::move(size->location_); CHECK(size->bytes_.size() <= static_cast(std::numeric_limits::max())); - res.dimensions = get_dimensions(size->w_, size->h_); + res.dimensions = get_dimensions(size->w_, size->h_, "photoCachedSize"); res.size = static_cast(size->bytes_.size()); content = std::move(size->bytes_); @@ -411,7 +411,7 @@ Variant get_photo_size(FileManager *file_manager, PhotoSizeSo type = std::move(size->type_); location = std::move(size->location_); - res.dimensions = get_dimensions(size->w_, size->h_); + res.dimensions = get_dimensions(size->w_, size->h_, "photoSizeProgressive"); res.size = size->sizes_.back(); size->sizes_.pop_back(); res.progressive_sizes = std::move(size->sizes_); @@ -460,7 +460,7 @@ AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource sour LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size); } res.type = static_cast(size->type_[0]); - res.dimensions = get_dimensions(size->w_, size->h_); + res.dimensions = get_dimensions(size->w_, size->h_, "get_animation_size"); res.size = size->size_; if ((size->flags_ & telegram_api::videoSize::VIDEO_START_TS_MASK) != 0) { res.main_frame_timestamp = size->video_start_ts_; @@ -534,7 +534,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t switch (attribute->get_id()) { case telegram_api::documentAttributeImageSize::ID: { auto image_size = move_tl_object_as(attribute); - dimensions = get_dimensions(image_size->w_, image_size->h_); + dimensions = get_dimensions(image_size->w_, image_size->h_, "web documentAttributeImageSize"); break; } case telegram_api::documentAttributeAnimated::ID: @@ -684,7 +684,7 @@ Photo get_encrypted_file_photo(FileManager *file_manager, tl_object_ptrw_, photo->h_); + s.dimensions = get_dimensions(photo->w_, photo->h_, "get_encrypted_file_photo"); s.size = photo->size_; s.file_id = file_id; res.photos.push_back(s); diff --git a/td/telegram/Photo.h b/td/telegram/Photo.h index 2662e5367..933ff30b1 100644 --- a/td/telegram/Photo.h +++ b/td/telegram/Photo.h @@ -71,7 +71,7 @@ struct Photo { } }; -Dimensions get_dimensions(int32 width, int32 height); +Dimensions get_dimensions(int32 width, int32 height, const char *source); bool operator==(const Dimensions &lhs, const Dimensions &rhs); bool operator!=(const Dimensions &lhs, const Dimensions &rhs); diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index b8c3f5780..4e16dde2a 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1855,7 +1855,7 @@ std::pair StickersManager::on_get_sticker_document( switch (attribute->get_id()) { case telegram_api::documentAttributeImageSize::ID: { auto image_size = move_tl_object_as(attribute); - dimensions = get_dimensions(image_size->w_, image_size->h_); + dimensions = get_dimensions(image_size->w_, image_size->h_, "sticker documentAttributeImageSize"); break; } case telegram_api::documentAttributeSticker::ID: @@ -4573,7 +4573,8 @@ Result> StickersManager::prepare_input_file if (is_animated) { int32 width = for_thumbnail ? 100 : 512; - create_sticker(file_id, string(), PhotoSize(), get_dimensions(width, width), nullptr, true, nullptr); + create_sticker(file_id, string(), PhotoSize(), get_dimensions(width, width, "prepare_input_file"), nullptr, true, + nullptr); } else { td_->documents_manager_->create_document(file_id, string(), PhotoSize(), "sticker.png", "image/png", false); } diff --git a/td/telegram/WebPageBlock.cpp b/td/telegram/WebPageBlock.cpp index 2f94325fa..7ab72c171 100644 --- a/td/telegram/WebPageBlock.cpp +++ b/td/telegram/WebPageBlock.cpp @@ -1851,7 +1851,7 @@ RichText get_rich_text(tl_object_ptr &&rich_text_ptr, if (it != documents.end()) { result.type = RichText::Type::Icon; result.document_file_id = it->second; - Dimensions dimensions = get_dimensions(rich_text->w_, rich_text->h_); + Dimensions dimensions = get_dimensions(rich_text->w_, rich_text->h_, "textImage"); result.content = PSTRING() << (dimensions.width * static_cast(65536) + dimensions.height); } else { LOG(ERROR) << "Can't find document " << rich_text->document_id_; @@ -2070,7 +2070,7 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptrw_, page_block->h_); + dimensions = get_dimensions(page_block->w_, page_block->h_, "pageBlockEmbed"); } return td::make_unique( std::move(page_block->url_), std::move(page_block->html_), std::move(poster_photo), dimensions, @@ -2221,7 +2221,7 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptr(page_block_ptr); Location location(std::move(page_block->geo_)); auto zoom = page_block->zoom_; - Dimensions dimensions = get_dimensions(page_block->w_, page_block->h_); + Dimensions dimensions = get_dimensions(page_block->w_, page_block->h_, "pageBlockMap"); if (location.empty()) { LOG(ERROR) << "Receive invalid map location"; break; diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index c97c123c1..9c442eb15 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -485,7 +485,7 @@ WebPageId WebPagesManager::on_get_web_page(tl_object_ptr page->embed_type = std::move(web_page->embed_type_); } if (web_page->flags_ & WEBPAGE_FLAG_HAS_EMBEDDED_PREVIEW_SIZE) { - page->embed_dimensions = get_dimensions(web_page->embed_width_, web_page->embed_height_); + page->embed_dimensions = get_dimensions(web_page->embed_width_, web_page->embed_height_, "webPage"); } if (web_page->flags_ & WEBPAGE_FLAG_HAS_DURATION) { page->duration = web_page->duration_; From 960aae0ae636fe1b4baa73b4b6d0cb9fc8532e7e Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 23 Jan 2021 23:19:37 +0300 Subject: [PATCH 007/232] Improve handling of via_bot_name. --- td/telegram/MessagesManager.cpp | 36 +++++++++++++++++++++++---------- td/telegram/MessagesManager.h | 3 +++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 170accb66..b985c616e 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -12302,17 +12302,14 @@ void MessagesManager::on_get_secret_message(SecretChatId secret_chat_id, UserId LOG(WARNING) << "Receive invalid bot username " << message->via_bot_name_; message->via_bot_name_.clear(); } - if ((message->flags_ & secret_api::decryptedMessage::VIA_BOT_NAME_MASK) != 0 && !message->via_bot_name_.empty()) { - pending_secret_message->load_data_multipromise.add_promise( - PromiseCreator::lambda([this, via_bot_name = message->via_bot_name_, &flags = message_info.flags, - &via_bot_user_id = message_info.via_bot_user_id](Unit) mutable { - auto dialog_id = resolve_dialog_username(via_bot_name); - if (dialog_id.is_valid() && dialog_id.get_type() == DialogType::User) { - flags |= MESSAGE_FLAG_IS_SENT_VIA_BOT; - via_bot_user_id = dialog_id.get_user_id(); - } - })); - search_public_dialog(message->via_bot_name_, false, pending_secret_message->load_data_multipromise.get_promise()); + if (!message->via_bot_name_.empty()) { + auto request_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), via_bot_username = message->via_bot_name_, message_info_ptr = &message_info, + promise = pending_secret_message->load_data_multipromise.get_promise()](Unit) mutable { + send_closure(actor_id, &MessagesManager::on_resolve_secret_chat_message_via_bot_username, via_bot_username, + message_info_ptr, std::move(promise)); + }); + search_public_dialog(message->via_bot_name_, false, std::move(request_promise)); } if ((message->flags_ & secret_api::decryptedMessage::GROUPED_ID_MASK) != 0 && message->grouped_id_ != 0) { message_info.media_album_id = message->grouped_id_; @@ -12327,6 +12324,23 @@ void MessagesManager::on_get_secret_message(SecretChatId secret_chat_id, UserId add_secret_message(std::move(pending_secret_message), std::move(lock_promise)); } +void MessagesManager::on_resolve_secret_chat_message_via_bot_username(const string &via_bot_username, + MessageInfo *message_info_ptr, + Promise &&promise) { + if (!G()->close_flag()) { + auto dialog_id = resolve_dialog_username(via_bot_username); + if (dialog_id.is_valid() && dialog_id.get_type() == DialogType::User) { + auto user_id = dialog_id.get_user_id(); + auto r_bot_data = td_->contacts_manager_->get_bot_data(user_id); + if (r_bot_data.is_ok() && r_bot_data.ok().is_inline) { + message_info_ptr->flags |= MESSAGE_FLAG_IS_SENT_VIA_BOT; + message_info_ptr->via_bot_user_id = user_id; + } + } + } + promise.set_value(Unit()); +} + void MessagesManager::on_secret_chat_screenshot_taken(SecretChatId secret_chat_id, UserId user_id, MessageId message_id, int32 date, int64 random_id, Promise<> promise) { LOG(DEBUG) << "On screenshot taken in " << secret_chat_id; diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 5ea771f11..cc37f8b37 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -1703,6 +1703,9 @@ class MessagesManager : public Actor { DialogId get_my_dialog_id() const; + void on_resolve_secret_chat_message_via_bot_username(const string &via_bot_username, MessageInfo *message_info_ptr, + Promise &&promise); + void add_secret_message(unique_ptr pending_secret_message, Promise lock_promise = Auto()); void finish_add_secret_message(unique_ptr pending_secret_message); From c3e45608112168684aed11eaca16f9970e347d91 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 02:14:51 +0300 Subject: [PATCH 008/232] Add MessagesManager::delete_update_message_id method. --- td/telegram/MessagesManager.cpp | 29 ++++++++++++++++++----------- td/telegram/MessagesManager.h | 2 ++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index b985c616e..fe4091b75 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -12810,6 +12810,23 @@ MessageId MessagesManager::find_old_message_id(DialogId dialog_id, MessageId mes return MessageId(); } +void MessagesManager::delete_update_message_id(DialogId dialog_id, MessageId message_id) { + if (message_id.is_scheduled()) { + CHECK(message_id.is_scheduled_server()); + auto dialog_it = update_scheduled_message_ids_.find(dialog_id); + CHECK(dialog_it != update_scheduled_message_ids_.end()); + auto erased_count = dialog_it->second.erase(message_id.get_scheduled_server_message_id()); + CHECK(erased_count > 0); + if (dialog_it->second.empty()) { + update_scheduled_message_ids_.erase(dialog_it); + } + } else { + CHECK(message_id.is_server()); + auto erased_count = update_message_ids_.erase(FullMessageId(dialog_id, message_id)); + CHECK(erased_count > 0); + } +} + FullMessageId MessagesManager::on_get_message(tl_object_ptr message_ptr, bool from_update, bool is_channel_message, bool is_scheduled, bool have_previous, bool have_next, const char *source) { @@ -12865,17 +12882,7 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f } } - if (message_id.is_scheduled()) { - CHECK(message_id.is_scheduled_server()); - auto dialog_it = update_scheduled_message_ids_.find(dialog_id); - CHECK(dialog_it != update_scheduled_message_ids_.end()); - dialog_it->second.erase(message_id.get_scheduled_server_message_id()); - if (dialog_it->second.empty()) { - update_scheduled_message_ids_.erase(dialog_it); - } - } else { - update_message_ids_.erase(FullMessageId(dialog_id, message_id)); - } + delete_update_message_id(dialog_id, message_id); if (!new_message->is_outgoing && dialog_id != get_my_dialog_id()) { // sent message is not from me diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index cc37f8b37..dff6ce496 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -1721,6 +1721,8 @@ class MessagesManager : public Actor { MessageId find_old_message_id(DialogId dialog_id, MessageId message_id) const; + void delete_update_message_id(DialogId dialog_id, MessageId message_id); + FullMessageId on_get_message(MessageInfo &&message_info, bool from_update, bool is_channel_message, bool have_previous, bool have_next, const char *source); From 3e905848593ec8092b3e8cdc0c60460c6926fb05 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 02:49:01 +0300 Subject: [PATCH 009/232] Delete updateMessageId whenever target message is found to be deleted. --- td/telegram/MessagesManager.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index fe4091b75..ef7d2ce3d 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -9611,7 +9611,7 @@ bool MessagesManager::can_delete_message(DialogId dialog_id, const Message *m) c if (m == nullptr) { return true; } - if (m->message_id.is_local()) { + if (m->message_id.is_local() || m->message_id.is_yet_unsent()) { return true; } switch (dialog_id.get_type()) { @@ -9688,6 +9688,9 @@ bool MessagesManager::can_revoke_message(DialogId dialog_id, const Message *m) c void MessagesManager::delete_messages(DialogId dialog_id, const vector &input_message_ids, bool revoke, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { return promise.set_error(Status::Error(6, "Chat is not found")); @@ -32143,6 +32146,16 @@ void MessagesManager::delete_message_from_database(Dialog *d, MessageId message_ d->deleted_message_ids.insert(message_id); } } + + if (message_id.is_any_server()) { + auto old_message_id = find_old_message_id(d->dialog_id, message_id); + if (old_message_id.is_valid()) { + LOG(WARNING) << "Sent " << FullMessageId{d->dialog_id, message_id} << " was deleted before it was received"; + send_closure_later(actor_id(this), &MessagesManager::delete_messages, d->dialog_id, + vector{old_message_id}, false, Promise()); + delete_update_message_id(d->dialog_id, message_id); + } + } } if (m != nullptr && m->random_id != 0 && (m->is_outgoing || d->dialog_id == get_my_dialog_id())) { @@ -34427,6 +34440,7 @@ void MessagesManager::set_channel_pts(Dialog *d, int32 new_pts, const char *sour bool MessagesManager::need_channel_difference_to_add_message(DialogId dialog_id, const tl_object_ptr &message_ptr) { + // keep consistent with add_message_to_dialog if (dialog_id.get_type() != DialogType::Channel || !have_input_peer(dialog_id, AccessRights::Read) || dialog_id == debug_channel_difference_dialog_) { return false; From 77a404d3139ec05bb26197b2eb55809fc74190b5 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 03:18:51 +0300 Subject: [PATCH 010/232] Delete updateMessageId if target message is in inaccessible chat part. --- td/telegram/MessagesManager.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index ef7d2ce3d..0bbd61076 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8547,6 +8547,7 @@ void MessagesManager::after_get_difference() { auto dialog_id = full_message_id.get_dialog_id(); auto message_id = full_message_id.get_message_id(); CHECK(message_id.is_valid()); + CHECK(message_id.is_server()); switch (dialog_id.get_type()) { case DialogType::Channel: // get channel difference may prevent updates from being applied @@ -8559,11 +8560,14 @@ void MessagesManager::after_get_difference() { if (!have_message_force({dialog_id, it.second}, "after get difference")) { // The sent message has already been deleted by the user or sent to inaccessible channel. // The sent message may never be received, but we will need updateMessageId in case the message is received - // to delete it from the server and to not add to the chat. - // But if the chat is inaccessible, then likely we will be unable to delete the message from server and - // will delete it from the chat just after it is added. So we remove updateMessageId for such messages in + // to delete it from the server and not add to the chat. + // But if the chat is inaccessible or the message is in an inaccessible chat part, then we will not be able to + // add the message or delete it from the server. In this case we forget updateMessageId for such messages in // order to not check them over and over. - if (!have_input_peer(dialog_id, AccessRights::Read)) { + const Dialog *d = get_dialog(dialog_id); + if (!have_input_peer(dialog_id, AccessRights::Read) || + (d != nullptr && + message_id <= td::max(d->last_clear_history_message_id, d->max_unavailable_message_id))) { update_message_ids_to_delete.push_back(it.first); } break; @@ -8571,16 +8575,14 @@ void MessagesManager::after_get_difference() { const Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); - if (dialog_id.get_type() == DialogType::Channel || message_id.is_scheduled() || - message_id <= d->last_new_message_id) { + if (dialog_id.get_type() == DialogType::Channel || message_id <= d->last_new_message_id) { LOG(ERROR) << "Receive updateMessageId from " << it.second << " to " << full_message_id << " but not receive corresponding message, last_new_message_id = " << d->last_new_message_id; } - if (dialog_id.get_type() != DialogType::Channel && - (message_id.is_scheduled() || message_id <= d->last_new_message_id)) { + if (dialog_id.get_type() != DialogType::Channel && message_id <= d->last_new_message_id) { dump_debug_message_op(get_dialog(dialog_id)); } - if (message_id.is_scheduled() || message_id <= d->last_new_message_id) { + if (message_id <= d->last_new_message_id) { get_message_from_server(it.first, PromiseCreator::lambda([this, full_message_id](Result result) { if (result.is_error()) { LOG(WARNING) @@ -25948,7 +25950,7 @@ Result MessagesManager::add_local_message( } bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_message_id, const string &source) { - if (!new_message_id.is_valid()) { + if (!new_message_id.is_valid() || !new_message_id.is_server()) { LOG(ERROR) << "Receive " << new_message_id << " in updateMessageId with random_id " << random_id << " from " << source; return false; @@ -25956,7 +25958,7 @@ bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_messag auto it = being_sent_messages_.find(random_id); if (it == being_sent_messages_.end()) { - // update about new message sent from other device or service message + // update about a new message sent from other device or a service message LOG(INFO) << "Receive not send outgoing " << new_message_id << " with random_id = " << random_id; return true; } From 527c61caeb4b1c05665151a7ada752849ffc4a3e Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 03:29:54 +0300 Subject: [PATCH 011/232] Skip send_update_chat_has_scheduled_messages call if there was no scheduled message changes. --- td/telegram/MessagesManager.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 0bbd61076..d71e9195e 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -9752,8 +9752,10 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector deleted_message_ids; for (auto message_id : message_ids) { + need_update_chat_has_scheduled_messages |= message_id.is_scheduled(); auto m = delete_message(d, message_id, true, &need_update_dialog_pos, DELETE_MESSAGE_USER_REQUEST_SOURCE); if (m == nullptr) { LOG(INFO) << "Can't delete " << message_id << " because it is not found"; @@ -9767,7 +9769,9 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vectormessage_id.is_scheduled()) { + send_update_chat_has_scheduled_messages(d, false); + } if (need_update_dialog_pos) { send_update_chat_last_message(d, "on_get_message"); @@ -22188,7 +22194,9 @@ MessagesManager::Message *MessagesManager::get_message_to_send( CHECK(have_input_peer(dialog_id, AccessRights::Read)); auto result = add_message_to_dialog(d, std::move(m), true, &need_update, need_update_dialog_pos, "send message"); LOG_CHECK(result != nullptr) << message_id << " " << debug_add_message_to_dialog_fail_reason_; - send_update_chat_has_scheduled_messages(d, false); + if (result->message_id.is_scheduled()) { + send_update_chat_has_scheduled_messages(d, false); + } return result; } @@ -35252,7 +35260,9 @@ MessagesManager::Message *MessagesManager::continue_send_message(DialogId dialog add_message_to_dialog(d, std::move(m), true, &need_update, &need_update_dialog_pos, "continue_send_message"); CHECK(result_message != nullptr); - send_update_chat_has_scheduled_messages(d, false); + if (result_message->message_id.is_scheduled()) { + send_update_chat_has_scheduled_messages(d, false); + } send_update_new_message(d, result_message); if (need_update_dialog_pos) { From fc2941763b6ee2394842ea51ebe4d430686bca6f Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 21:59:03 +0300 Subject: [PATCH 012/232] Fix LibreSSL 3+ support. --- td/mtproto/RSA.cpp | 10 +++++----- tdutils/td/utils/BigNum.cpp | 2 +- tdutils/td/utils/crypto.cpp | 26 +++++++++++++------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/td/mtproto/RSA.cpp b/td/mtproto/RSA.cpp index 5c33f3bd5..48740130a 100644 --- a/td/mtproto/RSA.cpp +++ b/td/mtproto/RSA.cpp @@ -22,7 +22,7 @@ #include #include #include -#if OPENSSL_VERSION_NUMBER < 0x30000000L +#if OPENSSL_VERSION_NUMBER < 0x30000000L || defined(LIBRESSL_VERSION_NUMBER) #include #endif @@ -47,7 +47,7 @@ Result RSA::from_pem_public_key(Slice pem) { BIO_free(bio); }; -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) EVP_PKEY *rsa = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr); #else auto rsa = PEM_read_bio_RSAPublicKey(bio, nullptr, nullptr, nullptr); @@ -56,14 +56,14 @@ Result RSA::from_pem_public_key(Slice pem) { return Status::Error("Error while reading RSA public key"); } SCOPE_EXIT { -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) EVP_PKEY_free(rsa); #else RSA_free(rsa); #endif }; -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) if (!EVP_PKEY_is_a(rsa, "RSA")) { return Status::Error("Key is not an RSA key"); } @@ -76,7 +76,7 @@ Result RSA::from_pem_public_key(Slice pem) { } #endif -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) BIGNUM *n_num = nullptr; BIGNUM *e_num = nullptr; diff --git a/tdutils/td/utils/BigNum.cpp b/tdutils/td/utils/BigNum.cpp index 7578cc241..d29ef7502 100644 --- a/tdutils/td/utils/BigNum.cpp +++ b/tdutils/td/utils/BigNum.cpp @@ -147,7 +147,7 @@ bool BigNum::is_bit_set(int num) const { } bool BigNum::is_prime(BigNumContext &context) const { -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) int result = BN_check_prime(impl_->big_num, context.impl_->big_num_context, nullptr); #else int result = diff --git a/tdutils/td/utils/crypto.cpp b/tdutils/td/utils/crypto.cpp index 043a0b037..7b033ad0b 100644 --- a/tdutils/td/utils/crypto.cpp +++ b/tdutils/td/utils/crypto.cpp @@ -34,7 +34,7 @@ #include #endif -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) #include #include #endif @@ -680,7 +680,7 @@ void AesCtrState::decrypt(Slice from, MutableSlice to) { encrypt(from, to); } -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) static void make_digest(Slice data, MutableSlice output, const EVP_MD *evp_md) { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); LOG_IF(FATAL, ctx == nullptr); @@ -695,7 +695,7 @@ static void make_digest(Slice data, MutableSlice output, const EVP_MD *evp_md) { #endif void sha1(Slice data, unsigned char output[20]) { -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) make_digest(data, MutableSlice(output, 20), EVP_sha1()); #else auto result = SHA1(data.ubegin(), data.size(), output); @@ -705,7 +705,7 @@ void sha1(Slice data, unsigned char output[20]) { void sha256(Slice data, MutableSlice output) { CHECK(output.size() >= 32); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) make_digest(data, output, EVP_sha256()); #else auto result = SHA256(data.ubegin(), data.size(), output.ubegin()); @@ -715,7 +715,7 @@ void sha256(Slice data, MutableSlice output) { void sha512(Slice data, MutableSlice output) { CHECK(output.size() >= 64); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) make_digest(data, output, EVP_sha512()); #else auto result = SHA512(data.ubegin(), data.size(), output.ubegin()); @@ -737,7 +737,7 @@ string sha512(Slice data) { class Sha256State::Impl { public: -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) EVP_MD_CTX *ctx_; Impl() { @@ -789,7 +789,7 @@ void Sha256State::init() { impl_ = make_unique(); } CHECK(!is_inited_); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) int err = EVP_DigestInit_ex(impl_->ctx_, EVP_sha256(), nullptr); #else int err = SHA256_Init(&impl_->ctx_); @@ -801,7 +801,7 @@ void Sha256State::init() { void Sha256State::feed(Slice data) { CHECK(impl_); CHECK(is_inited_); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) int err = EVP_DigestUpdate(impl_->ctx_, data.ubegin(), data.size()); #else int err = SHA256_Update(&impl_->ctx_, data.ubegin(), data.size()); @@ -813,7 +813,7 @@ void Sha256State::extract(MutableSlice output, bool destroy) { CHECK(output.size() >= 32); CHECK(impl_); CHECK(is_inited_); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) int err = EVP_DigestFinal_ex(impl_->ctx_, output.ubegin(), nullptr); #else int err = SHA256_Final(output.ubegin(), &impl_->ctx_); @@ -827,7 +827,7 @@ void Sha256State::extract(MutableSlice output, bool destroy) { void md5(Slice input, MutableSlice output) { CHECK(output.size() >= 16); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) make_digest(input, output, EVP_md5()); #else auto result = MD5(input.ubegin(), input.size(), output.ubegin()); @@ -880,7 +880,7 @@ void pbkdf2_sha512(Slice password, Slice salt, int iteration_count, MutableSlice pbkdf2_impl(password, salt, iteration_count, dest, EVP_sha512()); } -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) static void hmac_impl(const char *digest, Slice key, Slice message, MutableSlice dest) { EVP_MAC *hmac = EVP_MAC_fetch(nullptr, "HMAC", nullptr); LOG_IF(FATAL, hmac == nullptr); @@ -918,7 +918,7 @@ static void hmac_impl(const EVP_MD *evp_md, Slice key, Slice message, MutableSli void hmac_sha256(Slice key, Slice message, MutableSlice dest) { CHECK(dest.size() == 256 / 8); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) hmac_impl("SHA256", key, message, dest); #else hmac_impl(EVP_sha256(), key, message, dest); @@ -927,7 +927,7 @@ void hmac_sha256(Slice key, Slice message, MutableSlice dest) { void hmac_sha512(Slice key, Slice message, MutableSlice dest) { CHECK(dest.size() == 512 / 8); -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) hmac_impl("SHA512", key, message, dest); #else hmac_impl(EVP_sha512(), key, message, dest); From d36ee86027e1739fe10494ea5625da498bab7cef Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 22:45:25 +0300 Subject: [PATCH 013/232] Stricter conditions for setting have_full_history. --- td/telegram/MessagesManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index d71e9195e..2f8326b65 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8721,8 +8721,9 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ CHECK(offset < 0 || from_the_end); CHECK(!from_message_id.is_scheduled()); - // it is likely that there are no more history messages on the server - bool have_full_history = from_the_end && narrow_cast(messages.size()) < limit; + // the server can return less messages than requested if some of messages are deleted during request + // but if it happens, it is likely that there are no more messages on the server + bool have_full_history = from_the_end && narrow_cast(messages.size()) < limit && messages.size() <= 1; Dialog *d = get_dialog(dialog_id); if (messages.empty()) { @@ -8772,7 +8773,7 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ MessageId last_added_message_id; bool have_next = false; - if (narrow_cast(messages.size()) < limit + offset && d != nullptr) { + if (narrow_cast(messages.size()) < limit + offset && messages.size() <= 1 && d != nullptr) { MessageId first_received_message_id = get_message_id(messages.back(), false); if (first_received_message_id >= from_message_id && d->first_database_message_id.is_valid() && first_received_message_id >= d->first_database_message_id) { From 08b3ce0aacec1f3d1b79304d1807006bc9209c6a Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 23:07:55 +0300 Subject: [PATCH 014/232] Fix updating last database message ID after channelDifferenceTooLong. --- td/telegram/MessagesManager.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 2f8326b65..a24dce04c 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -14311,11 +14311,16 @@ unique_ptr MessagesManager::do_delete_message(Dialog * if (*it != nullptr) { if (!(*it)->message_id.is_yet_unsent() && (*it)->message_id != d->last_database_message_id) { - set_dialog_last_database_message_id(d, (*it)->message_id, "do_delete_message"); - if (d->last_database_message_id < d->first_database_message_id) { - LOG(ERROR) << "Last database " << d->last_database_message_id << " became less than first database " - << d->first_database_message_id << " after deletion of " << full_message_id; - set_dialog_first_database_message_id(d, d->last_database_message_id, "do_delete_message 2"); + if ((*it)->message_id < d->first_database_message_id && d->dialog_id.get_type() == DialogType::Channel) { + // possible if messages was deleted from database, but not from memory after updateChannelTooLong + set_dialog_last_database_message_id(d, MessageId(), "do_delete_message"); + } else { + set_dialog_last_database_message_id(d, (*it)->message_id, "do_delete_message"); + if (d->last_database_message_id < d->first_database_message_id) { + LOG(ERROR) << "Last database " << d->last_database_message_id << " became less than first database " + << d->first_database_message_id << " after deletion of " << full_message_id; + set_dialog_first_database_message_id(d, d->last_database_message_id, "do_delete_message 2"); + } } } else { need_get_history = true; @@ -34693,7 +34698,7 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m // as results of getChatHistory and (if implemented continuous ranges support for searching shared media) // searchChatMessages. The messages should still be lazily checked using getHistory, but they are still available // offline. It is the best way for gaps support, but it is pretty hard to implement correctly. - // It should be also noted that some messages like live location messages shouldn't be deleted. + // It should be also noted that some messages like outgoing live location messages shouldn't be deleted. if (last_message_id > d->last_new_message_id) { // TODO properly support last_message_id <= d->last_new_message_id From e63776dbd3c9e52e9fa5b56953de580a976115dc Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 23:27:53 +0300 Subject: [PATCH 015/232] Improve updating first/last database message IDs in on_get_history. --- td/telegram/MessagesManager.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index a24dce04c..9edd575cb 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8890,9 +8890,10 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ // LOG_IF(ERROR, d->first_message_id.is_valid() && d->first_message_id > first_received_message_id) // << "Receive " << first_received_message_id << ", but first chat message is " << d->first_message_id; + bool intersect_last_database_message_ids = + last_added_message_id >= d->first_database_message_id && d->last_database_message_id >= first_added_message_id; bool need_update_database_message_ids = - last_added_message_id.is_valid() && (from_the_end || (last_added_message_id >= d->first_database_message_id && - d->last_database_message_id >= first_added_message_id)); + last_added_message_id.is_valid() && (from_the_end || intersect_last_database_message_ids); if (from_the_end) { if (!d->last_new_message_id.is_valid()) { set_dialog_last_new_message_id( @@ -8906,20 +8907,28 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ } if (need_update_database_message_ids) { + if (from_the_end && !intersect_last_database_message_ids) { + set_dialog_first_database_message_id(d, MessageId(), "on_get_history 1"); + set_dialog_last_database_message_id(d, MessageId(), "on_get_history 1"); + } if (!d->last_database_message_id.is_valid()) { CHECK(d->last_message_id.is_valid()); MessagesConstIterator it(d, d->last_message_id); + MessageId new_first_database_message_id; while (*it != nullptr) { auto message_id = (*it)->message_id; if (message_id.is_server() || message_id.is_local()) { if (!d->last_database_message_id.is_valid()) { set_dialog_last_database_message_id(d, message_id, "on_get_history"); } - set_dialog_first_database_message_id(d, message_id, "on_get_history"); + new_first_database_message_id = message_id; try_restore_dialog_reply_markup(d, *it); } --it; } + if (new_first_database_message_id.is_valid()) { + set_dialog_first_database_message_id(d, new_first_database_message_id, "on_get_history"); + } } else { LOG_CHECK(d->last_new_message_id.is_valid()) << dialog_id << " " << from_the_end << " " << d->first_database_message_id << " " From c169067bb57c170dc04220acda0defebdcf40855 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 24 Jan 2021 23:45:03 +0300 Subject: [PATCH 016/232] Improve logging. --- td/telegram/MessagesManager.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 9edd575cb..8828a9153 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -12871,7 +12871,9 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f MessageId old_message_id = find_old_message_id(dialog_id, message_id); bool is_sent_message = false; - LOG(INFO) << "Found temporarily " << old_message_id << " for " << FullMessageId{dialog_id, message_id}; + if (old_message_id.is_valid()) { + LOG(INFO) << "Found temporary " << old_message_id << " for " << FullMessageId{dialog_id, message_id}; + } if (old_message_id.is_valid() || old_message_id.is_valid_scheduled()) { Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); @@ -31514,7 +31516,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } - LOG(INFO) << "Attach " << message_id << " to the previous " << previous_message_id; + LOG(INFO) << "Attach " << message_id << " to the previous " << previous_message_id << " in " << dialog_id; message->have_previous = true; message->have_next = previous_message->have_next; previous_message->have_next = true; @@ -31535,7 +31537,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } if (next_message != nullptr) { CHECK(!next_message->have_previous); - LOG(INFO) << "Attach " << message_id << " to the next " << next_message->message_id; + LOG(INFO) << "Attach " << message_id << " to the next " << next_message->message_id << " in " << dialog_id; if (from_update && !next_message->message_id.is_yet_unsent()) { LOG(ERROR) << "Attach " << message_id << " from " << source << " to the next " << next_message->message_id << " in " << dialog_id; @@ -31547,7 +31549,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } if (!is_attached) { - LOG(INFO) << "Can't auto-attach " << message_id; + LOG(INFO) << "Can't auto-attach " << message_id << " in " << dialog_id; message->have_previous = false; message->have_next = false; } @@ -32274,7 +32276,7 @@ void MessagesManager::attach_message_to_previous(Dialog *d, MessageId message_id LOG_CHECK(m->have_previous) << d->dialog_id << " " << message_id << " " << source; --it; LOG_CHECK(*it != nullptr) << d->dialog_id << " " << message_id << " " << source; - LOG(INFO) << "Attach " << message_id << " to the previous " << (*it)->message_id; + LOG(INFO) << "Attach " << message_id << " to the previous " << (*it)->message_id << " in " << d->dialog_id; if ((*it)->have_next) { m->have_next = true; } else { @@ -32292,7 +32294,7 @@ void MessagesManager::attach_message_to_next(Dialog *d, MessageId message_id, co LOG_CHECK(m->have_next) << d->dialog_id << " " << message_id << " " << source; ++it; LOG_CHECK(*it != nullptr) << d->dialog_id << " " << message_id << " " << source; - LOG(INFO) << "Attach " << message_id << " to the next " << (*it)->message_id; + LOG(INFO) << "Attach " << message_id << " to the next " << (*it)->message_id << " in " << d->dialog_id; if ((*it)->have_previous) { m->have_previous = true; } else { From feba24f1f44fa7fd76493aff00dd6426500f037e Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 25 Jan 2021 01:19:41 +0300 Subject: [PATCH 017/232] Improve updating first/last database message IDs in on_get_history. --- td/telegram/MessagesManager.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 8828a9153..7f9fff7e9 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8907,9 +8907,23 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ } if (need_update_database_message_ids) { - if (from_the_end && !intersect_last_database_message_ids) { - set_dialog_first_database_message_id(d, MessageId(), "on_get_history 1"); - set_dialog_last_database_message_id(d, MessageId(), "on_get_history 1"); + if (from_the_end && !intersect_last_database_message_ids && d->last_database_message_id.is_valid()) { + if (d->last_database_message_id < first_added_message_id || last_added_message_id == d->last_message_id) { + set_dialog_first_database_message_id(d, MessageId(), "on_get_history 1"); + set_dialog_last_database_message_id(d, MessageId(), "on_get_history 1"); + } else { + auto min_message_id = td::min(d->first_database_message_id, d->last_message_id); + CHECK(last_added_message_id < min_message_id); + if (min_message_id <= last_added_message_id.get_next_message_id(MessageType::Server)) { + // connect local messages with last received server message + set_dialog_first_database_message_id(d, last_added_message_id, "on_get_history 2"); + } else { + LOG(WARNING) << "Have last " << d->last_message_id << " and first database " << d->first_database_message_id + << " in " << dialog_id << ", but received history from the end only up to " + << last_added_message_id; + // can't connect messages, because there can be unknown server messages after last_added_message_id + } + } } if (!d->last_database_message_id.is_valid()) { CHECK(d->last_message_id.is_valid()); From ef160aa2c04171b19d13ecfa3d424a70c3c5162f Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 25 Jan 2021 02:00:57 +0300 Subject: [PATCH 018/232] Add struct DialogParticipants. --- td/telegram/ContactsManager.cpp | 22 ++++++++++------------ td/telegram/ContactsManager.h | 16 ++++++++-------- td/telegram/DialogParticipant.h | 10 ++++++++++ td/telegram/GroupCallManager.cpp | 2 +- td/telegram/MessagesManager.cpp | 12 +++++++----- td/telegram/MessagesManager.h | 12 +++++------- td/telegram/Td.cpp | 16 ++++++++-------- 7 files changed, 49 insertions(+), 41 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 34f169dcd..6e07545ef 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -13401,11 +13401,9 @@ DialogParticipant ContactsManager::get_chat_participant(ChatId chat_id, UserId u return *result; } -std::pair> ContactsManager::search_chat_participants(ChatId chat_id, - const string &query, int32 limit, - DialogParticipantsFilter filter, - bool force, - Promise &&promise) { +DialogParticipants ContactsManager::search_chat_participants(ChatId chat_id, const string &query, int32 limit, + DialogParticipantsFilter filter, bool force, + Promise &&promise) { if (limit < 0) { promise.set_error(Status::Error(3, "Parameter limit must be non-negative")); return {}; @@ -13518,7 +13516,7 @@ DialogParticipant ContactsManager::get_channel_participant(ChannelId channel_id, return DialogParticipant(); } -std::pair> ContactsManager::get_channel_participants( +DialogParticipants ContactsManager::get_channel_participants( ChannelId channel_id, const tl_object_ptr &filter, const string &additional_query, int32 offset, int32 limit, int32 additional_limit, int64 &random_id, bool without_bot_info, bool force, Promise &&promise) { @@ -13534,24 +13532,24 @@ std::pair> ContactsManager::get_channel_partici return result; } - auto user_ids = transform(result.second, [](const auto &participant) { return participant.user_id; }); + auto user_ids = transform(result.participants_, [](const auto &participant) { return participant.user_id; }); std::pair> result_user_ids = search_among_users(user_ids, additional_query, additional_limit); - result.first = result_user_ids.first; + result.total_count_ = result_user_ids.first; std::unordered_set result_user_ids_set(result_user_ids.second.begin(), result_user_ids.second.end()); - auto all_participants = std::move(result.second); - result.second.clear(); + auto all_participants = std::move(result.participants_); + result.participants_.clear(); for (auto &participant : all_participants) { if (result_user_ids_set.count(participant.user_id)) { - result.second.push_back(std::move(participant)); + result.participants_.push_back(std::move(participant)); result_user_ids_set.erase(participant.user_id); } } return result; } - std::pair> result; + DialogParticipants result; if (limit <= 0) { promise.set_error(Status::Error(3, "Parameter limit must be positive")); return result; diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 3aa111f24..f396feb7c 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -501,17 +501,17 @@ class ContactsManager : public Actor { DialogParticipant get_chat_participant(ChatId chat_id, UserId user_id, bool force, Promise &&promise); - std::pair> search_chat_participants(ChatId chat_id, const string &query, int32 limit, - DialogParticipantsFilter filter, bool force, - Promise &&promise); + DialogParticipants search_chat_participants(ChatId chat_id, const string &query, int32 limit, + DialogParticipantsFilter filter, bool force, Promise &&promise); DialogParticipant get_channel_participant(ChannelId channel_id, UserId user_id, int64 &random_id, bool force, Promise &&promise); - std::pair> get_channel_participants( - ChannelId channel_id, const tl_object_ptr &filter, - const string &additional_query, int32 offset, int32 limit, int32 additional_limit, int64 &random_id, - bool without_bot_info, bool force, Promise &&promise); + DialogParticipants get_channel_participants(ChannelId channel_id, + const tl_object_ptr &filter, + const string &additional_query, int32 offset, int32 limit, + int32 additional_limit, int64 &random_id, bool without_bot_info, + bool force, Promise &&promise); void send_get_channel_participants_query(ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, int32 limit, int64 random_id, Promise &&promise); @@ -1567,7 +1567,7 @@ class ContactsManager : public Actor { std::unordered_map, vector>> imported_contacts_; std::unordered_map received_channel_participant_; - std::unordered_map>> received_channel_participants_; + std::unordered_map received_channel_participants_; std::unordered_map, ChannelIdHash> cached_channel_participants_; diff --git a/td/telegram/DialogParticipant.h b/td/telegram/DialogParticipant.h index ac0d96116..30960d340 100644 --- a/td/telegram/DialogParticipant.h +++ b/td/telegram/DialogParticipant.h @@ -409,6 +409,16 @@ struct DialogParticipant { StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant &dialog_participant); +struct DialogParticipants { + int32 total_count_ = 0; + vector participants_; + + DialogParticipants() = default; + DialogParticipants(int32 total_count, vector &&participants) + : total_count_(total_count), participants_(std::move(participants)) { + } +}; + class ChannelParticipantsFilter { enum class Type : int32 { Recent, Contacts, Administrators, Search, Mention, Restricted, Banned, Bots } type; string query; diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index f393e4475..ffbd727e9 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1520,7 +1520,7 @@ void GroupCallManager::finish_load_group_call_administrators(InputGroupCallId in auto participants = td_->messages_manager_->search_dialog_participants( group_call->dialog_id, string(), 100, DialogParticipantsFilter(DialogParticipantsFilter::Type::Administrators), random_id, true, true, std::move(promise)); - for (auto &administrator : participants.second) { + for (auto &administrator : participants.participants_) { if (administrator.status.can_manage_calls() && administrator.user_id != td_->contacts_manager_->get_my_id()) { administrator_user_ids.push_back(administrator.user_id); } diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 7f9fff7e9..002f19ede 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -30265,8 +30265,9 @@ DialogParticipant MessagesManager::get_dialog_participant(DialogId dialog_id, Us return DialogParticipant(); } -std::pair> MessagesManager::search_private_chat_participants( - UserId my_user_id, UserId peer_user_id, const string &query, int32 limit, DialogParticipantsFilter filter) const { +DialogParticipants MessagesManager::search_private_chat_participants(UserId my_user_id, UserId peer_user_id, + const string &query, int32 limit, + DialogParticipantsFilter filter) const { vector user_ids; switch (filter.type) { case DialogParticipantsFilter::Type::Contacts: @@ -30307,9 +30308,10 @@ std::pair> MessagesManager::search_private_chat })}; } -std::pair> MessagesManager::search_dialog_participants( - DialogId dialog_id, const string &query, int32 limit, DialogParticipantsFilter filter, int64 &random_id, - bool without_bot_info, bool force, Promise &&promise) { +DialogParticipants MessagesManager::search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, + DialogParticipantsFilter filter, int64 &random_id, + bool without_bot_info, bool force, + Promise &&promise) { LOG(INFO) << "Receive searchChatMembers request to search for \"" << query << "\" in " << dialog_id << " with filter " << filter; if (!have_dialog_force(dialog_id)) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index dff6ce496..653082c5f 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -477,10 +477,9 @@ class MessagesManager : public Actor { DialogParticipant get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, Promise &&promise); - std::pair> search_dialog_participants(DialogId dialog_id, const string &query, - int32 limit, DialogParticipantsFilter filter, - int64 &random_id, bool without_bot_info, - bool force, Promise &&promise); + DialogParticipants search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, + DialogParticipantsFilter filter, int64 &random_id, + bool without_bot_info, bool force, Promise &&promise); vector get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise); @@ -2515,9 +2514,8 @@ class MessagesManager : public Actor { DialogFolder *get_dialog_folder(FolderId folder_id); const DialogFolder *get_dialog_folder(FolderId folder_id) const; - std::pair> search_private_chat_participants(UserId my_user_id, UserId peer_user_id, - const string &query, int32 limit, - DialogParticipantsFilter filter) const; + DialogParticipants search_private_chat_participants(UserId my_user_id, UserId peer_user_id, const string &query, + int32 limit, DialogParticipantsFilter filter) const; static unique_ptr *treap_find_message(unique_ptr *v, MessageId message_id); static const unique_ptr *treap_find_message(const unique_ptr *v, MessageId message_id); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 20e8483b1..15c6e8dc5 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1968,7 +1968,7 @@ class SearchChatMembersRequest : public RequestActor<> { DialogParticipantsFilter filter_; int64 random_id_ = 0; - std::pair> participants_; + DialogParticipants participants_; void do_run(Promise &&promise) override { participants_ = td->messages_manager_->search_dialog_participants(dialog_id_, query_, limit_, filter_, random_id_, @@ -1978,12 +1978,12 @@ class SearchChatMembersRequest : public RequestActor<> { void do_send_result() override { // TODO create function get_chat_members_object vector> result; - result.reserve(participants_.second.size()); - for (auto participant : participants_.second) { + result.reserve(participants_.participants_.size()); + for (auto participant : participants_.participants_) { result.push_back(td->contacts_manager_->get_chat_member_object(participant)); } - send_result(make_tl_object(participants_.first, std::move(result))); + send_result(make_tl_object(participants_.total_count_, std::move(result))); } public: @@ -2285,7 +2285,7 @@ class GetSupergroupMembersRequest : public RequestActor<> { int32 limit_; int64 random_id_ = 0; - std::pair> participants_; + DialogParticipants participants_; void do_run(Promise &&promise) override { participants_ = td->contacts_manager_->get_channel_participants( @@ -2295,12 +2295,12 @@ class GetSupergroupMembersRequest : public RequestActor<> { void do_send_result() override { // TODO create function get_chat_members_object vector> result; - result.reserve(participants_.second.size()); - for (auto participant : participants_.second) { + result.reserve(participants_.participants_.size()); + for (auto participant : participants_.participants_) { result.push_back(td->contacts_manager_->get_chat_member_object(participant)); } - send_result(make_tl_object(participants_.first, std::move(result))); + send_result(make_tl_object(participants_.total_count_, std::move(result))); } public: From 60673fea785b67528c04cb2a3a7fc3d6fb47c208 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 25 Jan 2021 02:07:38 +0300 Subject: [PATCH 019/232] Add get_chat_members_object method. --- td/telegram/DialogParticipant.cpp | 12 ++++++++++++ td/telegram/DialogParticipant.h | 4 ++++ td/telegram/Td.cpp | 18 ++---------------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/td/telegram/DialogParticipant.cpp b/td/telegram/DialogParticipant.cpp index 5c7abce7c..d4700b0d2 100644 --- a/td/telegram/DialogParticipant.cpp +++ b/td/telegram/DialogParticipant.cpp @@ -6,8 +6,10 @@ // #include "td/telegram/DialogParticipant.h" +#include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" #include "td/telegram/misc.h" +#include "td/telegram/Td.h" #include "td/utils/common.h" #include "td/utils/logging.h" @@ -705,6 +707,16 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant << ']'; } +td_api::object_ptr DialogParticipants::get_chat_members_object(Td *td) const { + vector> chat_members; + chat_members.reserve(participants_.size()); + for (auto &participant : participants_) { + chat_members.push_back(td->contacts_manager_->get_chat_member_object(participant)); + } + + return td_api::make_object(total_count_, std::move(chat_members)); +} + tl_object_ptr ChannelParticipantsFilter::get_input_channel_participants_filter() const { switch (type) { diff --git a/td/telegram/DialogParticipant.h b/td/telegram/DialogParticipant.h index 30960d340..5f376c241 100644 --- a/td/telegram/DialogParticipant.h +++ b/td/telegram/DialogParticipant.h @@ -17,6 +17,8 @@ namespace td { +class Td; + class RestrictedRights { static constexpr uint32 CAN_SEND_MESSAGES = 1 << 16; static constexpr uint32 CAN_SEND_MEDIA = 1 << 17; @@ -417,6 +419,8 @@ struct DialogParticipants { DialogParticipants(int32 total_count, vector &&participants) : total_count_(total_count), participants_(std::move(participants)) { } + + td_api::object_ptr get_chat_members_object(Td *td) const; }; class ChannelParticipantsFilter { diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 15c6e8dc5..a571cca68 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1976,14 +1976,7 @@ class SearchChatMembersRequest : public RequestActor<> { } void do_send_result() override { - // TODO create function get_chat_members_object - vector> result; - result.reserve(participants_.participants_.size()); - for (auto participant : participants_.participants_) { - result.push_back(td->contacts_manager_->get_chat_member_object(participant)); - } - - send_result(make_tl_object(participants_.total_count_, std::move(result))); + send_result(participants_.get_chat_members_object(td)); } public: @@ -2293,14 +2286,7 @@ class GetSupergroupMembersRequest : public RequestActor<> { } void do_send_result() override { - // TODO create function get_chat_members_object - vector> result; - result.reserve(participants_.participants_.size()); - for (auto participant : participants_.participants_) { - result.push_back(td->contacts_manager_->get_chat_member_object(participant)); - } - - send_result(make_tl_object(participants_.total_count_, std::move(result))); + send_result(participants_.get_chat_members_object(td)); } public: From d25edad06d819dc45f2546ec31f8cff830cbb91a Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 25 Jan 2021 17:26:04 +0300 Subject: [PATCH 020/232] Rewrite searchChatMembers and getSupergroupMembers implementation. --- td/telegram/ContactsManager.cpp | 243 +++++++++++++++---------------- td/telegram/ContactsManager.h | 35 +++-- td/telegram/GroupCallManager.cpp | 56 +++---- td/telegram/GroupCallManager.h | 4 +- td/telegram/MessagesManager.cpp | 46 +++--- td/telegram/MessagesManager.h | 5 +- td/telegram/Td.cpp | 87 +++-------- 7 files changed, 203 insertions(+), 273 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 6e07545ef..7fc14f8f5 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -2429,30 +2429,23 @@ class GetChannelParticipantQuery : public Td::ResultHandler { }; class GetChannelParticipantsQuery : public Td::ResultHandler { - Promise promise_; + Promise> promise_; ChannelId channel_id_; - ChannelParticipantsFilter filter_{nullptr}; - int32 offset_; - int32 limit_; - int64 random_id_; public: - explicit GetChannelParticipantsQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit GetChannelParticipantsQuery(Promise> &&promise) + : promise_(std::move(promise)) { } - void send(ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, int32 limit, int64 random_id) { + void send(ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, int32 limit) { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); if (input_channel == nullptr) { return promise_.set_error(Status::Error(3, "Supergroup not found")); } channel_id_ = channel_id; - filter_ = std::move(filter); - offset_ = offset; - limit_ = limit; - random_id_ = random_id; send_query(G()->net_query_creator().create(telegram_api::channels_getParticipants( - std::move(input_channel), filter_.get_input_channel_participants_filter(), offset, limit, 0))); + std::move(input_channel), filter.get_input_channel_participants_filter(), offset, limit, 0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2462,31 +2455,22 @@ class GetChannelParticipantsQuery : public Td::ResultHandler { } auto participants_ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for GetChannelParticipantsQuery with filter " - << to_string(filter_.get_input_channel_participants_filter()) << ": " << to_string(participants_ptr); + LOG(INFO) << "Receive result for GetChannelParticipantsQuery: " << to_string(participants_ptr); switch (participants_ptr->get_id()) { case telegram_api::channels_channelParticipants::ID: { - auto participants = telegram_api::move_object_as(participants_ptr); - td->contacts_manager_->on_get_users(std::move(participants->users_), "GetChannelParticipantsQuery"); - td->contacts_manager_->on_get_channel_participants_success(channel_id_, std::move(filter_), offset_, limit_, - random_id_, participants->count_, - std::move(participants->participants_)); + promise_.set_value(telegram_api::move_object_as(participants_ptr)); break; } case telegram_api::channels_channelParticipantsNotModified::ID: LOG(ERROR) << "Receive channelParticipantsNotModified"; - break; + return on_error(id, Status::Error(500, "Receive channelParticipantsNotModified")); default: UNREACHABLE(); } - - promise_.set_value(Unit()); } void on_error(uint64 id, Status status) override { td->contacts_manager_->on_get_channel_error(channel_id_, status, "GetChannelParticipantsQuery"); - td->contacts_manager_->on_get_channel_participants_fail(channel_id_, std::move(filter_), offset_, limit_, - random_id_); promise_.set_error(std::move(status)); } }; @@ -10627,9 +10611,17 @@ bool ContactsManager::is_user_contact(const User *u, UserId user_id, bool is_mut return u != nullptr && (is_mutual ? u->is_mutual_contact : u->is_contact) && user_id != get_my_id(); } -void ContactsManager::on_get_channel_participants_success( - ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, int32 limit, int64 random_id, - int32 total_count, vector> &&participants) { +void ContactsManager::on_get_channel_participants( + ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, int32 limit, string additional_query, + int32 additional_limit, tl_object_ptr &&channel_participants, + Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + on_get_users(std::move(channel_participants->users_), "on_get_channel_participants"); + int32 total_count = channel_participants->count_; + auto participants = std::move(channel_participants->participants_); LOG(INFO) << "Receive " << participants.size() << " members in " << channel_id; bool is_full = offset == 0 && static_cast(participants.size()) < limit && total_count < limit; @@ -10738,17 +10730,24 @@ void ContactsManager::on_get_channel_participants_success( } } - if (random_id != 0) { - received_channel_participants_[random_id] = {total_count, std::move(result)}; - } -} + if (!additional_query.empty()) { + auto user_ids = transform(result, [](const auto &participant) { return participant.user_id; }); + std::pair> result_user_ids = search_among_users(user_ids, additional_query, additional_limit); -void ContactsManager::on_get_channel_participants_fail(ChannelId channel_id, ChannelParticipantsFilter filter, - int32 offset, int32 limit, int64 random_id) { - if (random_id != 0) { - // clean up - received_channel_participants_.erase(random_id); + total_count = result_user_ids.first; + std::unordered_set result_user_ids_set(result_user_ids.second.begin(), + result_user_ids.second.end()); + auto all_participants = std::move(result); + result.clear(); + for (auto &participant : all_participants) { + if (result_user_ids_set.count(participant.user_id)) { + result.push_back(std::move(participant)); + result_user_ids_set.erase(participant.user_id); + } + } } + + promise.set_value(DialogParticipants{total_count, std::move(result)}); } bool ContactsManager::speculative_add_count(int32 &count, int32 delta_count, int32 min_count) { @@ -11247,13 +11246,16 @@ void ContactsManager::on_update_channel_full_linked_channel_id(ChannelFull *chan td_->messages_manager_->on_dialog_linked_channel_updated(DialogId(channel_id), old_linked_channel_id, linked_channel_id); } - auto new_linked_linked_channel_id = get_linked_channel_id(linked_channel_id); - LOG(INFO) << "Uplate linked channel in " << linked_channel_id << " from " << old_linked_linked_channel_id << " to " - << new_linked_linked_channel_id; - if (old_linked_linked_channel_id != new_linked_linked_channel_id) { - // must be called after the linked channel is changed - td_->messages_manager_->on_dialog_linked_channel_updated(DialogId(linked_channel_id), old_linked_linked_channel_id, - new_linked_linked_channel_id); + + if (linked_channel_id.is_valid()) { + auto new_linked_linked_channel_id = get_linked_channel_id(linked_channel_id); + LOG(INFO) << "Uplate linked channel in " << linked_channel_id << " from " << old_linked_linked_channel_id << " to " + << new_linked_linked_channel_id; + if (old_linked_linked_channel_id != new_linked_linked_channel_id) { + // must be called after the linked channel is changed + td_->messages_manager_->on_dialog_linked_channel_updated( + DialogId(linked_channel_id), old_linked_linked_channel_id, new_linked_linked_channel_id); + } } } @@ -13401,24 +13403,34 @@ DialogParticipant ContactsManager::get_chat_participant(ChatId chat_id, UserId u return *result; } -DialogParticipants ContactsManager::search_chat_participants(ChatId chat_id, const string &query, int32 limit, - DialogParticipantsFilter filter, bool force, - Promise &&promise) { +void ContactsManager::search_chat_participants(ChatId chat_id, const string &query, int32 limit, + DialogParticipantsFilter filter, Promise &&promise) { if (limit < 0) { - promise.set_error(Status::Error(3, "Parameter limit must be non-negative")); - return {}; + return promise.set_error(Status::Error(3, "Parameter limit must be non-negative")); } - if (force) { - promise.set_value(Unit()); - } else if (!load_chat_full(chat_id, force, std::move(promise), "search_chat_participants")) { - return {}; + auto load_chat_full_promise = PromiseCreator::lambda([actor_id = actor_id(this), chat_id, query, limit, filter, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &ContactsManager::do_search_chat_participants, chat_id, query, limit, filter, + std::move(promise)); + } + }); + load_chat_full(chat_id, false, std::move(load_chat_full_promise), "search_chat_participants"); +} + +void ContactsManager::do_search_chat_participants(ChatId chat_id, const string &query, int32 limit, + DialogParticipantsFilter filter, + Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); } - // promise is already set auto chat_full = get_chat_full(chat_id); if (chat_full == nullptr) { - return {}; + return promise.set_error(Status::Error(500, "Can't find basic group full info")); } auto is_dialog_participant_suitable = [this, filter](const DialogParticipant &participant) { @@ -13452,7 +13464,9 @@ DialogParticipants ContactsManager::search_chat_participants(ChatId chat_id, con int32 total_count; std::tie(total_count, user_ids) = search_among_users(user_ids, query, limit); - return {total_count, transform(user_ids, [&](UserId user_id) { return *get_chat_participant(chat_full, user_id); })}; + promise.set_value(DialogParticipants{total_count, transform(user_ids, [this, chat_full](UserId user_id) { + return *get_chat_participant(chat_full, user_id); + })}); } DialogParticipant ContactsManager::get_channel_participant(ChannelId channel_id, UserId user_id, int64 &random_id, @@ -13516,88 +13530,69 @@ DialogParticipant ContactsManager::get_channel_participant(ChannelId channel_id, return DialogParticipant(); } -DialogParticipants ContactsManager::get_channel_participants( - ChannelId channel_id, const tl_object_ptr &filter, const string &additional_query, - int32 offset, int32 limit, int32 additional_limit, int64 &random_id, bool without_bot_info, bool force, - Promise &&promise) { - if (random_id != 0) { - // request has already been sent before - auto it = received_channel_participants_.find(random_id); - CHECK(it != received_channel_participants_.end()); - auto result = std::move(it->second); - received_channel_participants_.erase(it); - promise.set_value(Unit()); - - if (additional_query.empty()) { - return result; - } - - auto user_ids = transform(result.participants_, [](const auto &participant) { return participant.user_id; }); - std::pair> result_user_ids = search_among_users(user_ids, additional_query, additional_limit); - - result.total_count_ = result_user_ids.first; - std::unordered_set result_user_ids_set(result_user_ids.second.begin(), - result_user_ids.second.end()); - auto all_participants = std::move(result.participants_); - result.participants_.clear(); - for (auto &participant : all_participants) { - if (result_user_ids_set.count(participant.user_id)) { - result.participants_.push_back(std::move(participant)); - result_user_ids_set.erase(participant.user_id); - } - } - return result; - } - - DialogParticipants result; +void ContactsManager::get_channel_participants(ChannelId channel_id, + tl_object_ptr &&filter, + string additional_query, int32 offset, int32 limit, + int32 additional_limit, bool without_bot_info, + Promise &&promise) { if (limit <= 0) { - promise.set_error(Status::Error(3, "Parameter limit must be positive")); - return result; + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); } if (limit > MAX_GET_CHANNEL_PARTICIPANTS) { limit = MAX_GET_CHANNEL_PARTICIPANTS; } if (offset < 0) { - promise.set_error(Status::Error(3, "Parameter offset must be non-negative")); - return result; + return promise.set_error(Status::Error(400, "Parameter offset must be non-negative")); } - auto channel_full = get_channel_full_force(channel_id, "get_channel_participants"); - if (td_->auth_manager_->is_bot()) { - without_bot_info = true; - } - if (!without_bot_info && (channel_full == nullptr || (!force && channel_full->is_expired()))) { - if (force) { - LOG(ERROR) << "Can't find cached ChannelFull"; - } else { - send_get_channel_full_query(channel_full, channel_id, std::move(promise), "get_channel_participants"); - return result; + auto load_channel_full_promise = + PromiseCreator::lambda([actor_id = actor_id(this), channel_id, filter = ChannelParticipantsFilter(filter), + additional_query = std::move(additional_query), offset, limit, additional_limit, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &ContactsManager::do_get_channel_participants, channel_id, std::move(filter), + std::move(additional_query), offset, limit, additional_limit, std::move(promise)); + } + }); + if (!without_bot_info && !td_->auth_manager_->is_bot()) { + auto channel_full = get_channel_full_force(channel_id, "get_channel_participants"); + if (channel_full == nullptr || channel_full->is_expired()) { + send_get_channel_full_query(channel_full, channel_id, std::move(load_channel_full_promise), + "get_channel_participants"); + return; } } - - if (channel_full != nullptr && !channel_full->is_expired() && !channel_full->can_get_participants) { - promise.set_error(Status::Error(3, "Member list is inaccessible")); - return result; - } - - do { - random_id = Random::secure_int64(); - } while (random_id == 0 || received_channel_participants_.find(random_id) != received_channel_participants_.end()); - received_channel_participants_[random_id]; // reserve place for result - - send_get_channel_participants_query(channel_id, ChannelParticipantsFilter(filter), offset, limit, random_id, - std::move(promise)); - return result; + load_channel_full_promise.set_value(Unit()); } -void ContactsManager::send_get_channel_participants_query(ChannelId channel_id, ChannelParticipantsFilter filter, - int32 offset, int32 limit, int64 random_id, - Promise &&promise) { - LOG(DEBUG) << "Get members of the " << channel_id << " with filter " << filter << ", offset = " << offset - << " and limit = " << limit; - td_->create_handler(std::move(promise)) - ->send(channel_id, std::move(filter), offset, limit, random_id); +void ContactsManager::do_get_channel_participants(ChannelId channel_id, ChannelParticipantsFilter &&filter, + string additional_query, int32 offset, int32 limit, + int32 additional_limit, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + auto channel_full = get_channel_full_force(channel_id, "do_get_channel_participants"); + if (channel_full != nullptr && !channel_full->is_expired() && !channel_full->can_get_participants) { + return promise.set_error(Status::Error(400, "Member list is inaccessible")); + } + + auto get_channel_participants_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), channel_id, filter, additional_query = std::move(additional_query), offset, limit, + additional_limit, promise = std::move(promise)]( + Result> &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &ContactsManager::on_get_channel_participants, channel_id, std::move(filter), offset, + limit, std::move(additional_query), additional_limit, result.move_as_ok(), std::move(promise)); + } + }); + td_->create_handler(std::move(get_channel_participants_promise)) + ->send(channel_id, std::move(filter), offset, limit); } vector ContactsManager::get_dialog_administrators(DialogId dialog_id, int left_tries, diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index f396feb7c..334608768 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -220,13 +220,6 @@ class ContactsManager : public Actor { bool on_get_channel_error(ChannelId channel_id, const Status &status, const string &source); - void on_get_channel_participants_success(ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, - int32 limit, int64 random_id, int32 total_count, - vector> &&participants); - - void on_get_channel_participants_fail(ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, - int32 limit, int64 random_id); - static Slice get_dialog_invite_link_hash(const string &invite_link); void on_get_chat_invite_link(ChatId chat_id, tl_object_ptr &&invite_link_ptr); @@ -501,20 +494,15 @@ class ContactsManager : public Actor { DialogParticipant get_chat_participant(ChatId chat_id, UserId user_id, bool force, Promise &&promise); - DialogParticipants search_chat_participants(ChatId chat_id, const string &query, int32 limit, - DialogParticipantsFilter filter, bool force, Promise &&promise); + void search_chat_participants(ChatId chat_id, const string &query, int32 limit, DialogParticipantsFilter filter, + Promise &&promise); DialogParticipant get_channel_participant(ChannelId channel_id, UserId user_id, int64 &random_id, bool force, Promise &&promise); - DialogParticipants get_channel_participants(ChannelId channel_id, - const tl_object_ptr &filter, - const string &additional_query, int32 offset, int32 limit, - int32 additional_limit, int64 &random_id, bool without_bot_info, - bool force, Promise &&promise); - - void send_get_channel_participants_query(ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, - int32 limit, int64 random_id, Promise &&promise); + void get_channel_participants(ChannelId channel_id, tl_object_ptr &&filter, + string additional_query, int32 offset, int32 limit, int32 additional_limit, + bool without_bot_info, Promise &&promise); DialogParticipant get_dialog_participant(ChannelId channel_id, tl_object_ptr &&participant_ptr) const; @@ -1425,6 +1413,18 @@ class ContactsManager : public Actor { void delete_chat_participant(ChatId chat_id, UserId user_id, Promise &&promise); + void do_search_chat_participants(ChatId chat_id, const string &query, int32 limit, DialogParticipantsFilter filter, + Promise &&promise); + + void do_get_channel_participants(ChannelId channel_id, ChannelParticipantsFilter &&filter, string additional_query, + int32 offset, int32 limit, int32 additional_limit, + Promise &&promise); + + void on_get_channel_participants(ChannelId channel_id, ChannelParticipantsFilter filter, int32 offset, int32 limit, + string additional_query, int32 additional_limit, + tl_object_ptr &&channel_participants, + Promise &&promise); + void change_channel_participant_status_impl(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, DialogParticipantStatus old_status, Promise &&promise); @@ -1567,7 +1567,6 @@ class ContactsManager : public Actor { std::unordered_map, vector>> imported_contacts_; std::unordered_map received_channel_participant_; - std::unordered_map received_channel_participants_; std::unordered_map, ChannelIdHash> cached_channel_participants_; diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index ffbd727e9..bfe77a9b8 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -9,7 +9,6 @@ #include "td/telegram/AccessRights.h" #include "td/telegram/AuthManager.h" #include "td/telegram/ContactsManager.h" -#include "td/telegram/DialogParticipant.h" #include "td/telegram/Global.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessagesManager.h" @@ -1483,23 +1482,29 @@ void GroupCallManager::try_load_group_call_administrators(InputGroupCallId input return; } - unique_ptr random_id_ptr = td::make_unique(); - auto random_id_raw = random_id_ptr.get(); - auto promise = PromiseCreator::lambda( - [actor_id = actor_id(this), input_group_call_id, random_id = std::move(random_id_ptr)](Result &&result) { + auto promise = + PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id](Result &&result) { send_closure(actor_id, &GroupCallManager::finish_load_group_call_administrators, input_group_call_id, - *random_id, std::move(result)); + std::move(result)); }); td_->messages_manager_->search_dialog_participants( - dialog_id, string(), 100, DialogParticipantsFilter(DialogParticipantsFilter::Type::Administrators), - *random_id_raw, true, true, std::move(promise)); + dialog_id, string(), 100, DialogParticipantsFilter(DialogParticipantsFilter::Type::Administrators), true, + std::move(promise)); } -void GroupCallManager::finish_load_group_call_administrators(InputGroupCallId input_group_call_id, int64 random_id, - Result &&result) { +void GroupCallManager::finish_load_group_call_administrators(InputGroupCallId input_group_call_id, + Result &&result) { if (G()->close_flag()) { return; } + if (result.is_error()) { + LOG(WARNING) << "Failed to get administrators of " << input_group_call_id << ": " << result.error(); + return; + } + + if (!need_group_call_participants(input_group_call_id)) { + return; + } auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr); @@ -1508,34 +1513,11 @@ void GroupCallManager::finish_load_group_call_administrators(InputGroupCallId in } vector administrator_user_ids; - if (result.is_ok()) { - result = Status::Error(500, "Failed to receive result"); - unique_ptr ignore_result = make_unique(); - auto ignore_result_ptr = ignore_result.get(); - auto promise = PromiseCreator::lambda([&result, ignore_result = std::move(ignore_result)](Result new_result) { - if (!*ignore_result) { - result = std::move(new_result); - } - }); - auto participants = td_->messages_manager_->search_dialog_participants( - group_call->dialog_id, string(), 100, DialogParticipantsFilter(DialogParticipantsFilter::Type::Administrators), - random_id, true, true, std::move(promise)); - for (auto &administrator : participants.participants_) { - if (administrator.status.can_manage_calls() && administrator.user_id != td_->contacts_manager_->get_my_id()) { - administrator_user_ids.push_back(administrator.user_id); - } + auto participants = result.move_as_ok(); + for (auto &administrator : participants.participants_) { + if (administrator.status.can_manage_calls() && administrator.user_id != td_->contacts_manager_->get_my_id()) { + administrator_user_ids.push_back(administrator.user_id); } - - *ignore_result_ptr = true; - } - - if (result.is_error()) { - LOG(WARNING) << "Failed to get administrators of " << input_group_call_id << ": " << result.error(); - return; - } - - if (!need_group_call_participants(input_group_call_id)) { - return; } auto *group_call_participants = add_group_call_participants(input_group_call_id); diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index c55fe6d22..8a0bb3015 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -7,6 +7,7 @@ #pragma once #include "td/telegram/DialogId.h" +#include "td/telegram/DialogParticipant.h" #include "td/telegram/GroupCallId.h" #include "td/telegram/GroupCallParticipant.h" #include "td/telegram/InputGroupCallId.h" @@ -151,8 +152,7 @@ class GroupCallManager : public Actor { void try_load_group_call_administrators(InputGroupCallId input_group_call_id, DialogId dialog_id); - void finish_load_group_call_administrators(InputGroupCallId input_group_call_id, int64 random_id, - Result &&result); + void finish_load_group_call_administrators(InputGroupCallId input_group_call_id, Result &&result); bool on_join_group_call_response(InputGroupCallId input_group_call_id, string json_response); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 002f19ede..419014f2d 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -11275,9 +11275,9 @@ void MessagesManager::on_update_dialog_online_member_count_timeout(DialogId dial if (participant_count == 0 || participant_count >= 195) { td_->create_handler()->send(dialog_id); } else { - td_->contacts_manager_->send_get_channel_participants_query( - dialog_id.get_channel_id(), - ChannelParticipantsFilter(td_api::make_object()), 0, 200, 0, Auto()); + td_->contacts_manager_->get_channel_participants(dialog_id.get_channel_id(), + td_api::make_object(), + string(), 0, 200, 200, true, Auto()); } return; } @@ -18927,10 +18927,9 @@ void MessagesManager::open_dialog(Dialog *d) { if (!is_broadcast_channel(dialog_id)) { auto participant_count = td_->contacts_manager_->get_channel_participant_count(dialog_id.get_channel_id()); if (participant_count < 195) { // include unknown participant_count - td_->contacts_manager_->send_get_channel_participants_query( - dialog_id.get_channel_id(), - ChannelParticipantsFilter(td_api::make_object()), 0, 200, 0, - Auto()); + td_->contacts_manager_->get_channel_participants(dialog_id.get_channel_id(), + td_api::make_object(), + string(), 0, 200, 200, true, Auto()); } } get_channel_difference(dialog_id, d->pts, true, "open_dialog"); @@ -30308,28 +30307,25 @@ DialogParticipants MessagesManager::search_private_chat_participants(UserId my_u })}; } -DialogParticipants MessagesManager::search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, - DialogParticipantsFilter filter, int64 &random_id, - bool without_bot_info, bool force, - Promise &&promise) { +void MessagesManager::search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, + DialogParticipantsFilter filter, bool without_bot_info, + Promise &&promise) { LOG(INFO) << "Receive searchChatMembers request to search for \"" << query << "\" in " << dialog_id << " with filter " << filter; if (!have_dialog_force(dialog_id)) { - promise.set_error(Status::Error(3, "Chat not found")); - return {}; + return promise.set_error(Status::Error(3, "Chat not found")); } if (limit < 0) { - promise.set_error(Status::Error(3, "Parameter limit must be non-negative")); - return {}; + return promise.set_error(Status::Error(3, "Parameter limit must be non-negative")); } switch (dialog_id.get_type()) { case DialogType::User: - promise.set_value(Unit()); - return search_private_chat_participants(td_->contacts_manager_->get_my_id(), dialog_id.get_user_id(), query, - limit, filter); + promise.set_value(search_private_chat_participants(td_->contacts_manager_->get_my_id(), dialog_id.get_user_id(), + query, limit, filter)); + return; case DialogType::Chat: - return td_->contacts_manager_->search_chat_participants(dialog_id.get_chat_id(), query, limit, filter, force, + return td_->contacts_manager_->search_chat_participants(dialog_id.get_chat_id(), query, limit, filter, std::move(promise)); case DialogType::Channel: { tl_object_ptr request_filter; @@ -30379,21 +30375,21 @@ DialogParticipants MessagesManager::search_dialog_participants(DialogId dialog_i UNREACHABLE(); } - return td_->contacts_manager_->get_channel_participants(dialog_id.get_channel_id(), request_filter, - additional_query, 0, limit, additional_limit, random_id, - without_bot_info, force, std::move(promise)); + return td_->contacts_manager_->get_channel_participants(dialog_id.get_channel_id(), std::move(request_filter), + std::move(additional_query), 0, limit, additional_limit, + without_bot_info, std::move(promise)); } case DialogType::SecretChat: { - promise.set_value(Unit()); auto peer_user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); - return search_private_chat_participants(td_->contacts_manager_->get_my_id(), peer_user_id, query, limit, filter); + promise.set_value( + search_private_chat_participants(td_->contacts_manager_->get_my_id(), peer_user_id, query, limit, filter)); + return; } case DialogType::None: default: UNREACHABLE(); promise.set_error(Status::Error(500, "Wrong chat type")); } - return {}; } vector MessagesManager::get_dialog_administrators(DialogId dialog_id, int left_tries, diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 653082c5f..06ee3e3b5 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -477,9 +477,8 @@ class MessagesManager : public Actor { DialogParticipant get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, Promise &&promise); - DialogParticipants search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, - DialogParticipantsFilter filter, int64 &random_id, - bool without_bot_info, bool force, Promise &&promise); + void search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, DialogParticipantsFilter filter, + bool without_bot_info, Promise &&promise); vector get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index a571cca68..cd3fcab69 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1961,36 +1961,6 @@ class GetChatMemberRequest : public RequestActor<> { } }; -class SearchChatMembersRequest : public RequestActor<> { - DialogId dialog_id_; - string query_; - int32 limit_; - DialogParticipantsFilter filter_; - int64 random_id_ = 0; - - DialogParticipants participants_; - - void do_run(Promise &&promise) override { - participants_ = td->messages_manager_->search_dialog_participants(dialog_id_, query_, limit_, filter_, random_id_, - false, get_tries() < 3, std::move(promise)); - } - - void do_send_result() override { - send_result(participants_.get_chat_members_object(td)); - } - - public: - SearchChatMembersRequest(ActorShared td, uint64 request_id, int64 dialog_id, string &&query, int32 limit, - DialogParticipantsFilter filter) - : RequestActor(std::move(td), request_id) - , dialog_id_(dialog_id) - , query_(std::move(query)) - , limit_(limit) - , filter_(filter) { - set_tries(3); - } -}; - class GetChatAdministratorsRequest : public RequestActor<> { DialogId dialog_id_; @@ -2271,36 +2241,6 @@ class GetRecentInlineBotsRequest : public RequestActor<> { } }; -class GetSupergroupMembersRequest : public RequestActor<> { - ChannelId channel_id_; - tl_object_ptr filter_; - int32 offset_; - int32 limit_; - int64 random_id_ = 0; - - DialogParticipants participants_; - - void do_run(Promise &&promise) override { - participants_ = td->contacts_manager_->get_channel_participants( - channel_id_, filter_, string(), offset_, limit_, -1, random_id_, false, get_tries() < 3, std::move(promise)); - } - - void do_send_result() override { - send_result(participants_.get_chat_members_object(td)); - } - - public: - GetSupergroupMembersRequest(ActorShared td, uint64 request_id, int32 channel_id, - tl_object_ptr &&filter, int32 offset, int32 limit) - : RequestActor(std::move(td), request_id) - , channel_id_(channel_id) - , filter_(std::move(filter)) - , offset_(offset) - , limit_(limit) { - set_tries(3); - } -}; - class GetUserProfilePhotosRequest : public RequestActor<> { UserId user_id_; int32 offset_; @@ -6327,8 +6267,18 @@ void Td::on_request(uint64 id, const td_api::getChatMember &request) { void Td::on_request(uint64 id, td_api::searchChatMembers &request) { CLEAN_INPUT_STRING(request.query_); - CREATE_REQUEST(SearchChatMembersRequest, request.chat_id_, std::move(request.query_), request.limit_, - get_dialog_participants_filter(request.filter_)); + CREATE_REQUEST_PROMISE(); + auto query_promise = + PromiseCreator::lambda([promise = std::move(promise), td = this](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(result.ok().get_chat_members_object(td)); + } + }); + messages_manager_->search_dialog_participants(DialogId(request.chat_id_), request.query_, request.limit_, + get_dialog_participants_filter(request.filter_), false, + std::move(query_promise)); } void Td::on_request(uint64 id, td_api::getChatAdministrators &request) { @@ -6691,8 +6641,17 @@ void Td::on_request(uint64 id, const td_api::reportSupergroupSpam &request) { } void Td::on_request(uint64 id, td_api::getSupergroupMembers &request) { - CREATE_REQUEST(GetSupergroupMembersRequest, request.supergroup_id_, std::move(request.filter_), request.offset_, - request.limit_); + CREATE_REQUEST_PROMISE(); + auto query_promise = + PromiseCreator::lambda([promise = std::move(promise), td = this](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(result.ok().get_chat_members_object(td)); + } + }); + contacts_manager_->get_channel_participants(ChannelId(request.supergroup_id_), std::move(request.filter_), string(), + request.offset_, request.limit_, -1, false, std::move(query_promise)); } void Td::on_request(uint64 id, const td_api::deleteSupergroup &request) { From cec5f11d17d2d95a1afd829b64b38d8d9a65c353 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 25 Jan 2021 17:41:49 +0300 Subject: [PATCH 021/232] Don't save group administrators after basic group full is loaded from database. --- td/telegram/ContactsManager.cpp | 18 ++++++++++-------- td/telegram/ContactsManager.h | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 7fc14f8f5..2c66e5d8a 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -2524,7 +2524,8 @@ class GetChannelAdministratorsQuery : public Td::ResultHandler { td->contacts_manager_->on_update_channel_administrator_count(channel_id_, narrow_cast(administrators.size())); - td->contacts_manager_->on_update_dialog_administrators(DialogId(channel_id_), std::move(administrators), true); + td->contacts_manager_->on_update_dialog_administrators(DialogId(channel_id_), std::move(administrators), true, + false); break; } @@ -9082,7 +9083,8 @@ void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id, bool bot_user_ids.push_back(user_id); } } - on_update_dialog_administrators(DialogId(chat_id), std::move(administrators), chat_full->version != -1); + on_update_dialog_administrators(DialogId(chat_id), std::move(administrators), chat_full->version != -1, + from_database); td_->messages_manager_->on_dialog_bots_updated(DialogId(chat_id), std::move(bot_user_ids)); { @@ -10691,7 +10693,7 @@ void ContactsManager::on_get_channel_participants( } } if (filter.is_administrators() || filter.is_recent()) { - on_update_dialog_administrators(DialogId(channel_id), std::move(administrators), true); + on_update_dialog_administrators(DialogId(channel_id), std::move(administrators), true, false); } if (filter.is_bots() || filter.is_recent()) { on_update_channel_bot_user_ids(channel_id, std::move(bot_user_ids)); @@ -10894,14 +10896,14 @@ void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId if (administrator.get_rank() != new_status.get_rank() || administrator.is_creator() != new_status.is_creator()) { administrator = DialogAdministrator(user_id, new_status.get_rank(), new_status.is_creator()); - on_update_dialog_administrators(dialog_id, std::move(administrators), true); + on_update_dialog_administrators(dialog_id, std::move(administrators), true, false); } break; } } if (!is_found) { administrators.emplace_back(user_id, new_status.get_rank(), new_status.is_creator()); - on_update_dialog_administrators(dialog_id, std::move(administrators), true); + on_update_dialog_administrators(dialog_id, std::move(administrators), true, false); } } else { size_t i = 0; @@ -10910,7 +10912,7 @@ void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId } if (i != administrators.size()) { administrators.erase(administrators.begin() + i); - on_update_dialog_administrators(dialog_id, std::move(administrators), true); + on_update_dialog_administrators(dialog_id, std::move(administrators), true, false); } } } @@ -13702,7 +13704,7 @@ void ContactsManager::on_update_channel_administrator_count(ChannelId channel_id } void ContactsManager::on_update_dialog_administrators(DialogId dialog_id, vector &&administrators, - bool have_access) { + bool have_access, bool from_database) { LOG(INFO) << "Update administrators in " << dialog_id << " to " << format::as_array(administrators); if (have_access) { std::sort(administrators.begin(), administrators.end(), @@ -13720,7 +13722,7 @@ void ContactsManager::on_update_dialog_administrators(DialogId dialog_id, vector it = dialog_administrators_.emplace(dialog_id, std::move(administrators)).first; } - if (G()->parameters().use_chat_info_db) { + if (G()->parameters().use_chat_info_db && !from_database) { LOG(INFO) << "Save administrators of " << dialog_id << " to database"; G()->td_db()->get_sqlite_pmc()->set(get_dialog_administrators_database_key(dialog_id), log_event_store(it->second).as_slice().str(), Auto()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 334608768..2d9b0b67f 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -209,7 +209,7 @@ class ContactsManager : public Actor { int32 on_update_peer_located(vector> &&peers, bool from_update); void on_update_dialog_administrators(DialogId dialog_id, vector &&administrators, - bool have_access); + bool have_access, bool from_database); void speculative_add_channel_participants(ChannelId channel_id, const vector &added_user_ids, UserId inviter_user_id, int32 date, bool by_me); From 3f856ccb46f79936b162eeeb0f645cd3d457cac8 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 25 Jan 2021 17:58:19 +0300 Subject: [PATCH 022/232] Don't load dialog after ChatFull is loaded from database. --- td/telegram/ContactsManager.cpp | 8 +++++--- td/telegram/MessagesManager.cpp | 4 ++-- td/telegram/MessagesManager.h | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 2c66e5d8a..de3e766ee 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -8704,6 +8704,8 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s } } + td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), channel_full->bot_user_ids, true); + update_channel_full(channel_full, channel_id, true); if (channel_full->expires_at == 0.0) { @@ -9085,7 +9087,7 @@ void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id, bool } on_update_dialog_administrators(DialogId(chat_id), std::move(administrators), chat_full->version != -1, from_database); - td_->messages_manager_->on_dialog_bots_updated(DialogId(chat_id), std::move(bot_user_ids)); + td_->messages_manager_->on_dialog_bots_updated(DialogId(chat_id), std::move(bot_user_ids), from_database); { Chat *c = get_chat(chat_id); @@ -12217,7 +12219,7 @@ void ContactsManager::on_update_channel_bot_user_ids(ChannelId channel_id, vecto auto channel_full = get_channel_full_force(channel_id, "on_update_channel_bot_user_ids"); if (channel_full == nullptr) { - td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), std::move(bot_user_ids)); + td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), std::move(bot_user_ids), false); return; } on_update_channel_full_bot_user_ids(channel_full, channel_id, std::move(bot_user_ids)); @@ -12228,7 +12230,7 @@ void ContactsManager::on_update_channel_full_bot_user_ids(ChannelFull *channel_f vector &&bot_user_ids) { CHECK(channel_full != nullptr); if (channel_full->bot_user_ids != bot_user_ids) { - td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), bot_user_ids); + td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), bot_user_ids, false); channel_full->bot_user_ids = std::move(bot_user_ids); channel_full->need_save_to_database = true; } diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 419014f2d..3338967f6 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -28953,12 +28953,12 @@ void MessagesManager::on_create_new_dialog_fail(int64 random_id, Status error, P td_->updates_manager_->get_difference("on_create_new_dialog_fail"); } -void MessagesManager::on_dialog_bots_updated(DialogId dialog_id, vector bot_user_ids) { +void MessagesManager::on_dialog_bots_updated(DialogId dialog_id, vector bot_user_ids, bool from_database) { if (td_->auth_manager_->is_bot()) { return; } - auto d = get_dialog_force(dialog_id); + auto d = from_database ? get_dialog(dialog_id) : get_dialog_force(dialog_id); if (d == nullptr || d->reply_markup_message_id == MessageId()) { return; } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 06ee3e3b5..4f1ffde27 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -748,7 +748,7 @@ class MessagesManager : public Actor { bool is_update_about_username_change_received(DialogId dialog_id) const; - void on_dialog_bots_updated(DialogId dialog_id, vector bot_user_ids); + void on_dialog_bots_updated(DialogId dialog_id, vector bot_user_ids, bool from_database); void on_dialog_photo_updated(DialogId dialog_id); void on_dialog_title_updated(DialogId dialog_id); From 797aaecf2ac620c41029c4fc62f93226f36149f1 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 28 Jan 2021 15:03:52 +0300 Subject: [PATCH 023/232] Prefer video dimensions over image size attribute. --- td/telegram/DocumentsManager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index 75106114f..5eb5003df 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -110,8 +110,12 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo int32 video_duration = 0; if (video != nullptr) { video_duration = video->duration_; - if (dimensions.width == 0) { - dimensions = get_dimensions(video->w_, video->h_, "documentAttributeVideo"); + auto video_dimensions = get_dimensions(video->w_, video->h_, "documentAttributeVideo"); + if (dimensions.width == 0 || (video_dimensions.width != 0 && video_dimensions != dimensions)) { + if (dimensions.width != 0) { + LOG(ERROR) << "Receive ambiguous video dimensions " << dimensions << " and " << video_dimensions; + } + dimensions = video_dimensions; } if (animated != nullptr) { From 4e710553ef6ca01c4266bd6e8a6aaafa35fc8eaa Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 28 Jan 2021 15:11:14 +0300 Subject: [PATCH 024/232] Don't try to merge GIF animation with generated MP4 animation. --- td/telegram/AnimationsManager.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/td/telegram/AnimationsManager.cpp b/td/telegram/AnimationsManager.cpp index 6c1c830ab..32401f987 100644 --- a/td/telegram/AnimationsManager.cpp +++ b/td/telegram/AnimationsManager.cpp @@ -295,6 +295,7 @@ bool AnimationsManager::merge_animations(FileId new_id, FileId old_id, bool can_ return old_->is_changed; } + bool need_merge = true; auto new_it = animations_.find(new_id); if (new_it == animations_.end()) { auto &old = animations_[old_id]; @@ -313,8 +314,13 @@ bool AnimationsManager::merge_animations(FileId new_id, FileId old_id, bool can_ if (old_->thumbnail != new_->thumbnail) { // LOG_STATUS(td_->file_manager_->merge(new_->thumbnail.file_id, old_->thumbnail.file_id)); } + if (old_->mime_type == "image/gif" && new_->mime_type == "video/mp4") { + need_merge = false; + } + } + if (need_merge) { + LOG_STATUS(td_->file_manager_->merge(new_id, old_id)); } - LOG_STATUS(td_->file_manager_->merge(new_id, old_id)); if (can_delete_old) { animations_.erase(old_id); } From 91e5b9e67754a641a1b83c8e059e1ada653ea7a8 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 29 Dec 2020 16:43:58 +0300 Subject: [PATCH 025/232] Update layer to 123. Rename Android Pay to Google Pay. --- td/generate/scheme/td_api.tl | 6 +++--- td/generate/scheme/td_api.tlo | Bin 191624 -> 191624 bytes td/generate/scheme/telegram_api.tl | 8 ++++---- td/generate/scheme/telegram_api.tlo | Bin 224380 -> 224580 bytes td/telegram/GroupCallManager.cpp | 2 +- td/telegram/Payments.cpp | 8 ++++---- td/telegram/Version.h | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 1fe948319..dabfd3a56 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1290,12 +1290,12 @@ inputCredentialsSaved saved_credentials_id:string = InputCredentials; //@description Applies if a user enters new credentials on a payment provider website @data Contains JSON-encoded data with a credential identifier from the payment provider @allow_save True, if the credential identifier can be saved on the server side inputCredentialsNew data:string allow_save:Bool = InputCredentials; -//@description Applies if a user enters new credentials using Android Pay @data JSON-encoded data with the credential identifier -inputCredentialsAndroidPay data:string = InputCredentials; - //@description Applies if a user enters new credentials using Apple Pay @data JSON-encoded data with the credential identifier inputCredentialsApplePay data:string = InputCredentials; +//@description Applies if a user enters new credentials using Google Pay @data JSON-encoded data with the credential identifier +inputCredentialsGooglePay data:string = InputCredentials; + //@description Stripe payment provider @publishable_key Stripe API publishable key @need_country True, if the user country must be provided @need_postal_code True, if the user ZIP/postal code must be provided @need_cardholder_name True, if the cardholder name must be provided paymentsProviderStripe publishable_key:string need_country:Bool need_postal_code:Bool need_cardholder_name:Bool = PaymentsProviderStripe; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index d11f1af51cd6e91cd29e9aed3f52736c1fc007fa..225e9d2a61f8a7ae32a3829e43e732f1ab9ae612 100644 GIT binary patch delta 150 zcmeCU$=z|2dqagN%jSC*&Tp;}{jvwlnp|?p0m9gENd>}qa47`Bm~&YN!Z>r;zyzd? z0Sd0lamA##hZsp_<`tBdI2Wa+q~?`mCgv2o=jW&Aqy{8bGBAMDPX=lPo6vM++ZD!w U*Q{Vermw7El;1vOKhqZ-0PC1bEC2ui delta 150 zcmeCU$=z|2dqagNi+XTn$L1Q*FMGhO$t9N@AdDTCR3MB8mqJWHiWs2asvK9$hf{w` zr84shN=uxJQd3g%N-`63iXHP(it;m40un13K versi chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto; -messageEmpty#83e5de54 id:int = Message; +messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector = Message; messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message; @@ -807,7 +807,7 @@ payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_inf inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials; inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials; inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials; -inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials; +inputPaymentCredentialsGooglePay#8ac32801 payment_token:DataJSON = InputPaymentCredentials; account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword; @@ -1182,7 +1182,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2 inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#56b087c9 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true user_id:int date:int active_date:flags.3?int source:int = GroupCallParticipant; +groupCallParticipant#b881f32b flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int muted_cnt:flags.8?int = GroupCallParticipant; phone.groupCall#66ab0bfc call:GroupCall participants:Vector participants_next_offset:string users:Vector = phone.GroupCall; @@ -1551,7 +1551,7 @@ phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool; phone.createGroupCall#bd3dabe0 peer:InputPeer random_id:int = Updates; phone.joinGroupCall#5f9c8e62 flags:# muted:flags.0?true call:InputGroupCall params:DataJSON = Updates; phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates; -phone.editGroupCallMember#63146ae4 flags:# muted:flags.0?true call:InputGroupCall user_id:InputUser = Updates; +phone.editGroupCallMember#a5e76cd8 flags:# muted:flags.0?true call:InputGroupCall user_id:InputUser volume:flags.1?int = Updates; phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector = Updates; phone.discardGroupCall#7a777135 call:InputGroupCall = Updates; phone.toggleGroupCallSettings#74bbb43d flags:# call:InputGroupCall join_muted:flags.0?Bool = Updates; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index f1cf1d522748fedb153c7b8abbe1df785ab04e5a..786729a6d49fbbb4cfe45fafa7c81f9f0a9cbf11 100644 GIT binary patch delta 867 zcmYjPQAkr!7~X&Gy-mk1UfRr=q8IYvh6^l&3Vf(QGMqSRmW8pIURL1R)GZnbT}x>c zgmHpnV6e!_2Oor)#|Mi<>mhs?l`tb>L6FRdMZT5P?mZXv!iE3;zW@9F|NQ6t&4^2% z#OXO3U;BG#E;VmU^L+f?^;l}OL=(hQ?ZK?IMI9LE3aT9LUAv8z7X{-k$wD8hrkQ4Lisv)E-=C6%m#e`W$K5zEyPs^H=BkO@ f##IvSHR6R``imoNHsm0A1)N0YkVJiLoKo>03s_GT delta 776 zcmYjOUuaTc6!-h?HJfN!X-bp410ReQxF~4ggHnN%qQ-4g*qApbW;Atbv7iTGvV4@j z*tbE!q97l95G21nv=}6V{yZ#7s37s7pmLCqkBjnt_tLy@x##yg=lp)>p40fO-TkPo zuB+KscdHY%=jv^iy_MZKQ!DB6O6{uK_FLtPWwSy&#-Mq|R6A|#jk6++wtXD(`gsXF z@pF6Zcfa>PsD%P6>>e^)(KFPEwtGUnWV;qhPwx{4FvQUd^^qYl{Iqq*tH$#@!=sIa z4tXD%QMFz~=?I6~{kKd!$ThxS>JO~*r|8&7|HF(ne>y3+*Rj(t?v8>nG~Kx*l~g(o zDQI`j8pVHA$WQZ_dg?$^`FaE$?3j%1k)g}HRDucV;6>#mw{x&aMzG1z+) zn1@7Ow6Neuf5Cg=8U}4fO53bJUaq6=z^*@_OgIkM0L>E52(Uj^irotAeg|gAhX%0e z2Z^aBxT6{*oL|72+}&;gr(P1`eu1_|zRfcSPHzD{Ill`EVVYi^ghk9xz%oVkNq7y& zlEOJ~nB+t7flAhiz_O%{xd<#0{wg#KVBCwsF0I6+CAdQJF{sf|+oz=*1WNJr3M>Sn ziBxXFfL8KbggJTQg#x@Uhx#R$mzDcrj}(jW?Mo%SC>Ao%N*26Kg=N29H$<^L$k-5H a7)6o#aqJ5-R=gQxEX!}8nOH6}_ThgBH!d*% diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index bfe77a9b8..f7f7704af 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -291,7 +291,7 @@ class EditGroupCallMemberQuery : public Td::ResultHandler { } send_query(G()->net_query_creator().create(telegram_api::phone_editGroupCallMember( - flags, false /*ignored*/, input_group_call_id.get_input_group_call(), std::move(input_user)))); + flags, false /*ignored*/, input_group_call_id.get_input_group_call(), std::move(input_user), 0))); } void on_result(uint64 id, BufferSlice packet) override { diff --git a/td/telegram/Payments.cpp b/td/telegram/Payments.cpp index aa497a0a1..5896d1c5b 100644 --- a/td/telegram/Payments.cpp +++ b/td/telegram/Payments.cpp @@ -859,10 +859,10 @@ void send_payment_form(ServerMessageId server_message_id, const string &order_in flags, false /*ignored*/, make_tl_object(credentials_new->data_)); break; } - case td_api::inputCredentialsAndroidPay::ID: { - auto credentials_android_pay = static_cast(credentials.get()); - input_credentials = make_tl_object( - make_tl_object(credentials_android_pay->data_), string()); + case td_api::inputCredentialsGooglePay::ID: { + auto credentials_google_pay = static_cast(credentials.get()); + input_credentials = make_tl_object( + make_tl_object(credentials_google_pay->data_)); break; } case td_api::inputCredentialsApplePay::ID: { diff --git a/td/telegram/Version.h b/td/telegram/Version.h index afaed1585..af435abeb 100644 --- a/td/telegram/Version.h +++ b/td/telegram/Version.h @@ -8,7 +8,7 @@ namespace td { -constexpr int32 MTPROTO_LAYER = 122; +constexpr int32 MTPROTO_LAYER = 123; enum class Version : int32 { Initial, // 0 From 7bf916f4f5973fb083bec725020c45b5dede9ed4 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 31 Dec 2020 02:48:45 +0300 Subject: [PATCH 026/232] Support muting group call participants for self. --- td/generate/scheme/td_api.tl | 8 +++-- td/generate/scheme/td_api.tlo | Bin 191624 -> 191760 bytes td/telegram/GroupCallManager.cpp | 8 ++--- td/telegram/GroupCallParticipant.cpp | 44 ++++++++++++++++++--------- td/telegram/GroupCallParticipant.h | 7 +++-- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index dabfd3a56..6cd9e9f9b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2102,12 +2102,14 @@ groupCallJoinResponse payload:groupCallPayload candidates:vectorPl-$~oWdls`J>SCRcv5>`t*xunS^;57#Kj@o5zHFxTZUP zVAPx*Hl7ipdwE zP!#|LbSC!{Doi$r=A5j$Q+e`{eJU&<+nV3(+x})BqlyKXH$CAdqr!HR?~EZ|0spT` A@c;k- delta 125 zcmbPmi@W0{_l6rnEMcontacts_manager_->get_my_id()) { - TRY_STATUS_PROMISE(promise, can_manage_group_calls(group_call->dialog_id)); - } else { - if (!is_muted && !group_call->can_self_unmute) { - return promise.set_error(Status::Error(400, "Can't unmute self")); - } + if (user_id == td_->contacts_manager_->get_my_id() && !is_muted && !group_call->can_self_unmute) { + return promise.set_error(Status::Error(400, "Can't unmute self")); } td_->create_handler(std::move(promise))->send(input_group_call_id, user_id, is_muted); diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index c125d1646..62e2419b4 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -18,6 +18,7 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptrsource_; is_muted = participant->muted_; can_self_unmute = participant->can_self_unmute_; + is_muted_only_for_self = participant->muted_by_you_; if (!participant->left_) { joined_date = participant->date_; if ((participant->flags_ & telegram_api::groupCallParticipant::ACTIVE_DATE_MASK) != 0) { @@ -37,26 +38,35 @@ bool GroupCallParticipant::is_versioned_update(const tl_object_ptr GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), source, is_speaking, - can_be_muted, can_be_unmuted, is_muted, can_self_unmute, order); + can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, + can_be_unmuted_only_for_self, is_muted, can_self_unmute, order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { - return lhs.user_id == rhs.user_id && lhs.source == rhs.source && lhs.can_be_muted == rhs.can_be_muted && - lhs.can_be_unmuted == rhs.can_be_unmuted && lhs.is_muted == rhs.is_muted && + return lhs.user_id == rhs.user_id && lhs.source == rhs.source && + lhs.can_be_muted_for_all_users == rhs.can_be_muted_for_all_users && + lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && + lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && + lhs.can_be_unmuted_only_for_self == rhs.can_be_unmuted_only_for_self && lhs.is_muted == rhs.is_muted && lhs.can_self_unmute == rhs.can_self_unmute && lhs.is_speaking == rhs.is_speaking && lhs.order == rhs.order; } diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 9e9a05fc5..4295f509e 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -24,9 +24,12 @@ struct GroupCallParticipant { int32 active_date = 0; bool is_muted = false; bool can_self_unmute = false; + bool is_muted_only_for_self = false; - bool can_be_muted = false; - bool can_be_unmuted = false; + bool can_be_muted_for_all_users = false; + bool can_be_unmuted_for_all_users = false; + bool can_be_muted_only_for_self = false; + bool can_be_unmuted_only_for_self = false; bool is_just_joined = false; bool is_speaking = false; From 607d198d3610d5c67751359cf551eba43b9cceb6 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 31 Dec 2020 02:54:17 +0300 Subject: [PATCH 027/232] Add groupCallParticipant.muted_only_for_self_count. --- td/generate/scheme/td_api.tl | 3 ++- td/generate/scheme/td_api.tlo | Bin 191760 -> 191812 bytes td/telegram/GroupCallParticipant.cpp | 5 +++-- td/telegram/GroupCallParticipant.h | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 6cd9e9f9b..ad4e5bbd2 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2108,8 +2108,9 @@ groupCallJoinResponse payload:groupCallPayload candidates:vectorJ6M#afK`&Gb3Hka(*Ub3H2 U#RANme&GhA!uCDi8AHAT0C*HP!2kdN delta 96 zcmX?di+jQ??hQADSnl;$p4xmzXwND(5KAO|@;xCjP7w3vF(DtW>5U&4H73{o=bygf tJEP#_*n{$uC+rj2Y_?B~adN|c6|nB+75lfZ*w3h90p@ML@SU;bD*(frE^+_> diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index 62e2419b4..a1a19db89 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -19,6 +19,7 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptrmuted_; can_self_unmute = participant->can_self_unmute_; is_muted_only_for_self = participant->muted_by_you_; + muted_count = participant->muted_cnt_; if (!participant->left_) { joined_date = participant->date_; if ((participant->flags_ & telegram_api::groupCallParticipant::ACTIVE_DATE_MASK) != 0) { @@ -81,11 +82,11 @@ td_api::object_ptr GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, - can_be_unmuted_only_for_self, is_muted, can_self_unmute, order); + can_be_unmuted_only_for_self, is_muted, can_self_unmute, muted_count, order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { - return lhs.user_id == rhs.user_id && lhs.source == rhs.source && + return lhs.user_id == rhs.user_id && lhs.source == rhs.source && lhs.muted_count == rhs.muted_count && lhs.can_be_muted_for_all_users == rhs.can_be_muted_for_all_users && lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 4295f509e..6296bd93f 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -22,6 +22,7 @@ struct GroupCallParticipant { int32 source = 0; int32 joined_date = 0; int32 active_date = 0; + int32 muted_count = 0; bool is_muted = false; bool can_self_unmute = false; bool is_muted_only_for_self = false; From 6b66cd87167ba13c3cecef0cbc30e6b4894ddb14 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 31 Dec 2020 03:10:41 +0300 Subject: [PATCH 028/232] Add groupCallParticipant.volume_level. --- td/generate/scheme/td_api.tl | 3 ++- td/generate/scheme/td_api.tlo | Bin 191812 -> 191852 bytes td/telegram/GroupCallParticipant.cpp | 20 +++++++++++++++++--- td/telegram/GroupCallParticipant.h | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index ad4e5bbd2..6dc21c4c0 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2109,8 +2109,9 @@ groupCallJoinResponse payload:groupCallPayload candidates:vector6U4lEOvs04dgBL1jmfqD`KPb= z&L}uJ_MrUa3HyXLo9**rKZ(p&WQN;qx L+kW9YW64(l_5d*5 delta 93 zcmaEJi~Gne?hQADSj?}k+_CwN(4JLnAeKn_muted_; can_self_unmute = participant->can_self_unmute_; is_muted_only_for_self = participant->muted_by_you_; - muted_count = participant->muted_cnt_; + if ((participant->flags_ & telegram_api::groupCallParticipant::MUTED_CNT_MASK) != 0) { + muted_count = participant->muted_cnt_; + if (muted_count < 0) { + LOG(ERROR) << "Receive " << to_string(participant); + muted_count = 0; + } + } + if ((participant->flags_ & telegram_api::groupCallParticipant::VOLUME_MASK) != 0) { + volume_level = participant->volume_; + if (volume_level <= 0 || volume_level > 20000) { + LOG(ERROR) << "Receive " << to_string(participant); + volume_level = 10000; + } + } if (!participant->left_) { joined_date = participant->date_; if ((participant->flags_ & telegram_api::groupCallParticipant::ACTIVE_DATE_MASK) != 0) { @@ -82,7 +95,7 @@ td_api::object_ptr GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, - can_be_unmuted_only_for_self, is_muted, can_self_unmute, muted_count, order); + can_be_unmuted_only_for_self, is_muted, can_self_unmute, muted_count, volume_level, order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { @@ -91,7 +104,8 @@ bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && lhs.can_be_unmuted_only_for_self == rhs.can_be_unmuted_only_for_self && lhs.is_muted == rhs.is_muted && - lhs.can_self_unmute == rhs.can_self_unmute && lhs.is_speaking == rhs.is_speaking && lhs.order == rhs.order; + lhs.can_self_unmute == rhs.can_self_unmute && lhs.is_speaking == rhs.is_speaking && + lhs.volume_level == rhs.volume_level && lhs.order == rhs.order; } bool operator!=(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 6296bd93f..5f568eb7c 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -23,6 +23,7 @@ struct GroupCallParticipant { int32 joined_date = 0; int32 active_date = 0; int32 muted_count = 0; + int32 volume_level = 10000; bool is_muted = false; bool can_self_unmute = false; bool is_muted_only_for_self = false; From 6bba5a568a77216df0c155e5c6a00dd2dcd25f2d Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 2 Jan 2021 22:59:48 +0300 Subject: [PATCH 029/232] Add setGroupCallParticipantVolumeLevel. --- td/generate/scheme/td_api.tl | 8 +++++-- td/generate/scheme/td_api.tlo | Bin 191852 -> 192040 bytes td/telegram/GroupCallManager.cpp | 33 +++++++++++++++++++++++---- td/telegram/GroupCallManager.h | 3 +++ td/telegram/GroupCallParticipant.cpp | 2 +- td/telegram/GroupCallParticipant.h | 3 +++ td/telegram/Td.cpp | 7 ++++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 7 ++++++ 9 files changed, 58 insertions(+), 7 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 6dc21c4c0..a5bec024a 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4385,7 +4385,7 @@ getGroupCall group_call_id:int32 = GroupCall; //@description Joins a group call @group_call_id Group call identifier @payload Group join payload, received from tgcalls. Use null to cancel previous joinGroupCall request @source Caller synchronization source identifier; received from tgcalls @is_muted True, if the user's microphone is muted joinGroupCall group_call_id:int32 payload:groupCallPayload source:int32 is_muted:Bool = GroupCallJoinResponse; -//@description Toggles whether new participants of a group call can be unmuted only by administrators of the group call. Requires can_manage_voice_chats rights in the corresponding chat and allowed_change_mute_mew_participants group call flag +//@description Toggles whether new participants of a group call can be unmuted only by administrators of the group call. Requires groupCall.can_be_managed and allowed_change_mute_mew_participants group call flag //@group_call_id Group call identifier @mute_new_participants New value of the mute_new_participants setting toggleGroupCallMuteNewParticipants group_call_id:int32 mute_new_participants:Bool = Ok; @@ -4401,6 +4401,10 @@ setGroupCallParticipantIsSpeaking group_call_id:int32 source:int32 is_speaking:B //@group_call_id Group call identifier @user_id User identifier @is_muted Pass true if the user must be muted and false otherwise toggleGroupCallParticipantIsMuted group_call_id:int32 user_id:int32 is_muted:Bool = Ok; +//@description Changes a group call participant's volume level. If the current user can manage the group call, then the participant's volume level will be changed for all users with default volume level +//@group_call_id Group call identifier @user_id User identifier @volume_level New participant's volume level; 1-20000 in hundreds of percents +setGroupCallParticipantVolumeLevel group_call_id:int32 user_id:int32 volume_level:int32 = Ok; + //@description Loads more group call participants. The loaded participants will be received through updates. Use the field groupCall.loaded_all_participants to check whether all participants has already been loaded //@group_call_id Group call identifier. The group call must be previously received through getGroupCall and must be joined or being joined //@limit Maximum number of participants to load @@ -4409,7 +4413,7 @@ loadGroupCallParticipants group_call_id:int32 limit:int32 = Ok; //@description Leaves a group call @group_call_id Group call identifier leaveGroupCall group_call_id:int32 = Ok; -//@description Discards a group call. Requires can_manage_voice_chats rights in the corresponding chat @group_call_id Group call identifier +//@description Discards a group call. Requires groupCall.can_be_managed @group_call_id Group call identifier discardGroupCall group_call_id:int32 = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 7fae3cf7fe1e67afd4d2aaee1ee0cd6d3601a8a7..dda0fc66aeac84a7f99b8f3407380c79fe7209cd 100644 GIT binary patch delta 78 zcmV-U0I~n<+6$=M3xI?Hv;q(%0g<;6B?42b1y-RuaUz$o&H@(|R&Q)|ZDmYlc4ce; kX};~$*~St~1DElW0x**eWgnA(za*D{GXfa5WX}Q!9 &&promise) : promise_(std::move(promise)) { } - void send(InputGroupCallId input_group_call_id, UserId user_id, bool is_muted) { + void send(InputGroupCallId input_group_call_id, UserId user_id, bool is_muted, int32 volume_level) { auto input_user = td->contacts_manager_->get_input_user(user_id); CHECK(input_user != nullptr); int32 flags = 0; - if (is_muted) { + if (volume_level) { + flags |= telegram_api::phone_editGroupCallMember::VOLUME_MASK; + } else if (is_muted) { flags |= telegram_api::phone_editGroupCallMember::MUTED_MASK; } send_query(G()->net_query_creator().create(telegram_api::phone_editGroupCallMember( - flags, false /*ignored*/, input_group_call_id.get_input_group_call(), std::move(input_user), 0))); + flags, false /*ignored*/, input_group_call_id.get_input_group_call(), std::move(input_user), volume_level))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1772,7 +1774,30 @@ void GroupCallManager::toggle_group_call_participant_is_muted(GroupCallId group_ return promise.set_error(Status::Error(400, "Can't unmute self")); } - td_->create_handler(std::move(promise))->send(input_group_call_id, user_id, is_muted); + td_->create_handler(std::move(promise))->send(input_group_call_id, user_id, is_muted, 0); +} + +void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group_call_id, UserId user_id, + int32 volume_level, Promise &&promise) { + TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); + if (volume_level < GroupCallParticipant::MIN_VOLUME_LEVEL || volume_level > GroupCallParticipant::MAX_VOLUME_LEVEL) { + return promise.set_error(Status::Error(400, "Wrong volume level specified")); + } + + auto *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + return promise.set_error(Status::Error(400, "GROUP_CALL_JOIN_MISSING")); + } + if (!td_->contacts_manager_->have_input_user(user_id)) { + return promise.set_error(Status::Error(400, "Have no access to the user")); + } + + if (user_id == td_->contacts_manager_->get_my_id()) { + return promise.set_error(Status::Error(400, "Can't change self volume level")); + } + + td_->create_handler(std::move(promise)) + ->send(input_group_call_id, user_id, false, volume_level); } void GroupCallManager::load_group_call_participants(GroupCallId group_call_id, int32 limit, Promise &&promise) { diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 8a0bb3015..7c74be9f3 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -62,6 +62,9 @@ class GroupCallManager : public Actor { void toggle_group_call_participant_is_muted(GroupCallId group_call_id, UserId user_id, bool is_muted, Promise &&promise); + void set_group_call_participant_volume_level(GroupCallId group_call_id, UserId user_id, int32 volume_level, + Promise &&promise); + void load_group_call_participants(GroupCallId group_call_id, int32 limit, Promise &&promise); void leave_group_call(GroupCallId group_call_id, Promise &&promise); diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index 58bd7f85c..0b1d92962 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -28,7 +28,7 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptrflags_ & telegram_api::groupCallParticipant::VOLUME_MASK) != 0) { volume_level = participant->volume_; - if (volume_level <= 0 || volume_level > 20000) { + if (volume_level < MIN_VOLUME_LEVEL || volume_level > MAX_VOLUME_LEVEL) { LOG(ERROR) << "Receive " << to_string(participant); volume_level = 10000; } diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 5f568eb7c..8c36de915 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -38,6 +38,9 @@ struct GroupCallParticipant { int32 local_active_date = 0; int64 order = 0; + static constexpr int32 MIN_VOLUME_LEVEL = 1; + static constexpr int32 MAX_VOLUME_LEVEL = 20000; + GroupCallParticipant() = default; explicit GroupCallParticipant(const tl_object_ptr &participant); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index cd3fcab69..a942407bb 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6001,6 +6001,13 @@ void Td::on_request(uint64 id, const td_api::toggleGroupCallParticipantIsMuted & GroupCallId(request.group_call_id_), UserId(request.user_id_), request.is_muted_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::setGroupCallParticipantVolumeLevel &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + group_call_manager_->set_group_call_participant_volume_level( + GroupCallId(request.group_call_id_), UserId(request.user_id_), request.volume_level_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::loadGroupCallParticipants &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index f0215dc4b..5acb98ef7 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -706,6 +706,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::toggleGroupCallParticipantIsMuted &request); + void on_request(uint64 id, const td_api::setGroupCallParticipantVolumeLevel &request); + void on_request(uint64 id, const td_api::loadGroupCallParticipants &request); void on_request(uint64 id, const td_api::leaveGroupCall &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index d4598f2d2..11f42bf9b 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2665,6 +2665,13 @@ class CliClient final : public Actor { get_args(args, group_call_id, user_id, is_muted); send_request(td_api::make_object(as_group_call_id(group_call_id), as_user_id(user_id), is_muted)); + } else if (op == "tgcpvl") { + string group_call_id; + string user_id; + int32 volume_level; + get_args(args, group_call_id, user_id, volume_level); + send_request(td_api::make_object(as_group_call_id(group_call_id), + as_user_id(user_id), volume_level)); } else if (op == "lgcp") { string group_call_id; string limit; From 567707099eddb15f90dcf19937027202944e7450 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 2 Jan 2021 23:32:02 +0300 Subject: [PATCH 030/232] Use messageEmpty.peer_id. --- td/telegram/MessagesManager.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 3338967f6..9f967fe21 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -6876,10 +6876,8 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p LOG(INFO) << "Receive from " << source << " pending " << to_string(update); CHECK(update != nullptr); if (dialog_id.get_type() != DialogType::Channel) { - if (dialog_id != DialogId() || !td_->auth_manager_->is_bot()) { - LOG(ERROR) << "Receive channel update in invalid " << dialog_id << " from " << source << ": " - << oneline(to_string(update)); - } + LOG(ERROR) << "Receive channel update in invalid " << dialog_id << " from " << source << ": " + << oneline(to_string(update)); promise.set_value(Unit()); return; } @@ -9301,10 +9299,8 @@ void MessagesManager::on_get_scheduled_server_messages(DialogId dialog_id, uint3 for (auto &message : messages) { auto message_dialog_id = get_message_dialog_id(message); if (message_dialog_id != dialog_id) { - if (dialog_id.is_valid()) { - LOG(ERROR) << "Receive " << get_message_id(message, true) << " in wrong " << message_dialog_id << " instead of " - << dialog_id << ": " << oneline(to_string(message)); - } + LOG(ERROR) << "Receive " << get_message_id(message, true) << " in wrong " << message_dialog_id << " instead of " + << dialog_id << ": " << oneline(to_string(message)); continue; } @@ -11312,8 +11308,10 @@ MessageId MessagesManager::get_message_id(const tl_object_ptr &message_ptr) { switch (message_ptr->get_id()) { - case telegram_api::messageEmpty::ID: - return DialogId(); + case telegram_api::messageEmpty::ID: { + auto message = static_cast(message_ptr.get()); + return message->peer_id_ == nullptr ? DialogId() : DialogId(message->peer_id_); + } case telegram_api::message::ID: { auto message = static_cast(message_ptr.get()); return DialogId(message->peer_id_); From 70b4838a219f42accca87b43bbf7b4456e1d7d18 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 11 Jan 2021 15:02:02 +0300 Subject: [PATCH 031/232] Rename source to audio_source. --- td/telegram/GroupCallManager.cpp | 96 +++++++++++++++------------- td/telegram/GroupCallManager.h | 14 ++-- td/telegram/GroupCallParticipant.cpp | 10 +-- td/telegram/GroupCallParticipant.h | 2 +- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 1b7b93c5a..9b658b8d4 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -121,11 +121,11 @@ class GetGroupCallParticipantQuery : public Td::ResultHandler { explicit GetGroupCallParticipantQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(InputGroupCallId input_group_call_id, vector user_ids, vector sources) { + void send(InputGroupCallId input_group_call_id, vector user_ids, vector audio_sources) { input_group_call_id_ = input_group_call_id; - auto limit = narrow_cast(max(user_ids.size(), sources.size())); + auto limit = narrow_cast(max(user_ids.size(), audio_sources.size())); send_query(G()->net_query_creator().create(telegram_api::phone_getGroupParticipants( - input_group_call_id.get_input_group_call(), std::move(user_ids), std::move(sources), string(), limit))); + input_group_call_id.get_input_group_call(), std::move(user_ids), std::move(audio_sources), string(), limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -319,9 +319,9 @@ class CheckGroupCallQuery : public Td::ResultHandler { explicit CheckGroupCallQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(InputGroupCallId input_group_call_id, int32 source) { + void send(InputGroupCallId input_group_call_id, int32 audio_source) { send_query(G()->net_query_creator().create( - telegram_api::phone_checkGroupCall(input_group_call_id.get_input_group_call(), source))); + telegram_api::phone_checkGroupCall(input_group_call_id.get_input_group_call(), audio_source))); } void on_result(uint64 id, BufferSlice packet) override { @@ -352,9 +352,9 @@ class LeaveGroupCallQuery : public Td::ResultHandler { explicit LeaveGroupCallQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(InputGroupCallId input_group_call_id, int32 source) { + void send(InputGroupCallId input_group_call_id, int32 audio_source) { send_query(G()->net_query_creator().create( - telegram_api::phone_leaveGroupCall(input_group_call_id.get_input_group_call(), source))); + telegram_api::phone_leaveGroupCall(input_group_call_id.get_input_group_call(), audio_source))); } void on_result(uint64 id, BufferSlice packet) override { @@ -419,7 +419,7 @@ struct GroupCallManager::GroupCall { int32 participant_count = 0; int32 version = -1; int32 duration = 0; - int32 source = 0; + int32 audio_source = 0; int32 joined_date = 0; }; @@ -444,7 +444,7 @@ struct GroupCallManager::GroupCallRecentSpeakers { struct GroupCallManager::PendingJoinRequest { NetQueryRef query_ref; uint64 generation = 0; - int32 source = 0; + int32 audio_source = 0; Promise> promise; }; @@ -494,17 +494,17 @@ void GroupCallManager::on_check_group_call_is_joined_timeout(GroupCallId group_c return; } - auto source = group_call->source; - auto promise = - PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, source](Result &&result) mutable { + auto audio_source = group_call->audio_source; + auto promise = PromiseCreator::lambda( + [actor_id = actor_id(this), input_group_call_id, audio_source](Result &&result) mutable { if (result.is_error() && result.error().message() == "GROUPCALL_JOIN_MISSING") { - send_closure(actor_id, &GroupCallManager::on_group_call_left, input_group_call_id, source, true); + send_closure(actor_id, &GroupCallManager::on_group_call_left, input_group_call_id, audio_source, true); result = Unit(); } - send_closure(actor_id, &GroupCallManager::finish_check_group_call_is_joined, input_group_call_id, source, + send_closure(actor_id, &GroupCallManager::finish_check_group_call_is_joined, input_group_call_id, audio_source, std::move(result)); }); - td_->create_handler(std::move(promise))->send(input_group_call_id, source); + td_->create_handler(std::move(promise))->send(input_group_call_id, audio_source); } void GroupCallManager::on_pending_send_speaking_action_timeout_callback(void *group_call_manager_ptr, @@ -830,14 +830,14 @@ void GroupCallManager::finish_get_group_call(InputGroupCallId input_group_call_i } } -void GroupCallManager::finish_check_group_call_is_joined(InputGroupCallId input_group_call_id, int32 source, +void GroupCallManager::finish_check_group_call_is_joined(InputGroupCallId input_group_call_id, int32 audio_source, Result &&result) { LOG(INFO) << "Finish check group call is_joined for " << input_group_call_id; auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); if (!group_call->is_joined || check_group_call_is_joined_timeout_.has_timeout(group_call->group_call_id.get()) || - group_call->source != source) { + group_call->audio_source != audio_source) { return; } @@ -974,7 +974,7 @@ void GroupCallManager::on_update_group_call_participants( GroupCallParticipant participant(group_call_participant); if (participant.user_id == td_->contacts_manager_->get_my_id() && group_call != nullptr && group_call->is_inited && group_call->is_joined && - (participant.joined_date == 0) == (participant.source == group_call->source)) { + (participant.joined_date == 0) == (participant.audio_source == group_call->audio_source)) { on_group_call_left_impl(group_call, participant.joined_date == 0); need_update = true; } @@ -1084,7 +1084,8 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup for (auto &participant : participants) { GroupCallParticipant group_call_participant(participant); if (group_call_participant.user_id == td_->contacts_manager_->get_my_id() && group_call->is_joined && - (group_call_participant.joined_date == 0) == (group_call_participant.source == group_call->source)) { + (group_call_participant.joined_date == 0) == + (group_call_participant.audio_source == group_call->audio_source)) { is_left = true; if (group_call_participant.joined_date != 0) { need_rejoin = false; @@ -1313,7 +1314,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou if (participant.user_id == td_->contacts_manager_->get_my_id()) { auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); - if (group_call->is_joined && group_call->is_active && participant.source == group_call->source && + if (group_call->is_joined && group_call->is_active && participant.audio_source == group_call->audio_source && participant.is_muted && group_call->can_self_unmute != participant.can_self_unmute) { group_call->can_self_unmute = participant.can_self_unmute; send_update_group_call(group_call, "process_group_call_participant"); @@ -1389,7 +1390,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou } void GroupCallManager::join_group_call(GroupCallId group_call_id, - td_api::object_ptr &&payload, int32 source, + td_api::object_ptr &&payload, int32 audio_source, bool is_muted, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); @@ -1444,7 +1445,7 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, } } - auto json_payload = json_encode(json_object([&payload, source](auto &o) { + auto json_payload = json_encode(json_object([&payload, audio_source](auto &o) { o("ufrag", payload->ufrag_); o("pwd", payload->pwd_); o("fingerprints", json_array(payload->fingerprints_, @@ -1455,14 +1456,14 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, o("fingerprint", fingerprint->fingerprint_); }); })); - o("ssrc", source); + o("ssrc", audio_source); })); auto generation = ++join_group_request_generation_; auto &request = pending_join_requests_[input_group_call_id]; request = make_unique(); request->generation = generation; - request->source = source; + request->audio_source = audio_source; request->promise = std::move(promise); auto query_promise = @@ -1634,7 +1635,7 @@ bool GroupCallManager::on_join_group_call_response(InputGroupCallId input_group_ group_call->need_rejoin = false; group_call->is_being_left = false; group_call->joined_date = G()->unix_time(); - group_call->source = it->second->source; + group_call->audio_source = it->second->audio_source; it->second->promise.set_value(result.move_as_ok()); check_group_call_is_joined_timeout_.set_timeout_in(group_call->group_call_id.get(), CHECK_GROUP_CALL_IS_JOINED_TIMEOUT); @@ -1691,8 +1692,8 @@ void GroupCallManager::invite_group_call_participants(GroupCallId group_call_id, td_->create_handler(std::move(promise))->send(input_group_call_id, std::move(input_users)); } -void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_call_id, int32 source, bool is_speaking, - Promise &&promise, int32 date) { +void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_call_id, int32 audio_source, + bool is_speaking, Promise &&promise, int32 date) { if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); } @@ -1703,8 +1704,8 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_ if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { return promise.set_value(Unit()); } - if (source == 0) { - source = group_call->source; + if (audio_source == 0) { + audio_source = group_call->audio_source; } bool recursive = false; @@ -1713,14 +1714,15 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_ } else { recursive = true; } - if (source != group_call->source && !recursive && is_speaking && + if (audio_source != group_call->audio_source && !recursive && is_speaking && check_group_call_is_joined_timeout_.has_timeout(group_call_id.get())) { check_group_call_is_joined_timeout_.set_timeout_in(group_call_id.get(), CHECK_GROUP_CALL_IS_JOINED_TIMEOUT); } - UserId user_id = set_group_call_participant_is_speaking_by_source(input_group_call_id, source, is_speaking, date); + UserId user_id = + set_group_call_participant_is_speaking_by_source(input_group_call_id, audio_source, is_speaking, date); if (!user_id.is_valid()) { if (!recursive) { - auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, source, is_speaking, + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, audio_source, is_speaking, promise = std::move(promise), date](Result &&result) mutable { if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); @@ -1728,14 +1730,14 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_ if (result.is_error()) { promise.set_value(Unit()); } else { - send_closure(actor_id, &GroupCallManager::set_group_call_participant_is_speaking, group_call_id, source, + send_closure(actor_id, &GroupCallManager::set_group_call_participant_is_speaking, group_call_id, audio_source, is_speaking, std::move(promise), date); } }); td_->create_handler(std::move(query_promise)) - ->send(input_group_call_id, {}, {source}); + ->send(input_group_call_id, {}, {audio_source}); } else { - LOG(INFO) << "Failed to find participant with source " << source << " in " << group_call_id << " from " + LOG(INFO) << "Failed to find participant with source " << audio_source << " in " << group_call_id << " from " << group_call->dialog_id; promise.set_value(Unit()); } @@ -1746,7 +1748,8 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_ on_user_speaking_in_group_call(group_call_id, user_id, date, recursive); } - if (group_call->source == source && group_call->dialog_id.is_valid() && group_call->is_speaking != is_speaking) { + if (group_call->audio_source == audio_source && group_call->dialog_id.is_valid() && + group_call->is_speaking != is_speaking) { group_call->is_speaking = is_speaking; if (is_speaking) { pending_send_speaking_action_timeout_.add_timeout_in(group_call_id.get(), 0.0); @@ -1833,24 +1836,24 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); } - auto source = group_call->source; + auto audio_source = group_call->audio_source; group_call->is_being_left = true; - auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, source, + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, audio_source, promise = std::move(promise)](Result &&result) mutable { if (result.is_ok()) { // just in case - send_closure(actor_id, &GroupCallManager::on_group_call_left, input_group_call_id, source, false); + send_closure(actor_id, &GroupCallManager::on_group_call_left, input_group_call_id, audio_source, false); } promise.set_result(std::move(result)); }); - td_->create_handler(std::move(query_promise))->send(input_group_call_id, source); + td_->create_handler(std::move(query_promise))->send(input_group_call_id, audio_source); } -void GroupCallManager::on_group_call_left(InputGroupCallId input_group_call_id, int32 source, bool need_rejoin) { +void GroupCallManager::on_group_call_left(InputGroupCallId input_group_call_id, int32 audio_source, bool need_rejoin) { auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); - if (group_call->is_joined && group_call->source == source) { + if (group_call->is_joined && group_call->audio_source == audio_source) { on_group_call_left_impl(group_call, need_rejoin); send_update_group_call(group_call, "on_group_call_left"); } @@ -1865,7 +1868,7 @@ void GroupCallManager::on_group_call_left_impl(GroupCall *group_call, bool need_ group_call->can_self_unmute = false; group_call->can_be_managed = false; group_call->joined_date = 0; - group_call->source = 0; + group_call->audio_source = 0; group_call->loaded_all_participants = false; group_call->version = -1; check_group_call_is_joined_timeout_.cancel_timeout(group_call->group_call_id.get()); @@ -1981,7 +1984,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrcan_self_unmute; call.syncing_participants = group_call->syncing_participants; call.loaded_all_participants = group_call->loaded_all_participants; - call.source = group_call->source; + call.audio_source = group_call->audio_source; *group_call = std::move(call); if (need_group_call_participants(input_group_call_id)) { @@ -2207,14 +2210,15 @@ void GroupCallManager::on_group_call_recent_speakers_updated(const GroupCall *gr } UserId GroupCallManager::set_group_call_participant_is_speaking_by_source(InputGroupCallId input_group_call_id, - int32 source, bool is_speaking, int32 date) { + int32 audio_source, bool is_speaking, + int32 date) { auto participants_it = group_call_participants_.find(input_group_call_id); if (participants_it == group_call_participants_.end()) { return UserId(); } for (auto &participant : participants_it->second->participants) { - if (participant.source == source) { + if (participant.audio_source == audio_source) { if (participant.is_speaking != is_speaking) { participant.is_speaking = is_speaking; if (is_speaking) { diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 7c74be9f3..aa7602cfa 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -48,15 +48,16 @@ class GroupCallManager : public Actor { void reload_group_call(InputGroupCallId input_group_call_id, Promise> &&promise); - void join_group_call(GroupCallId group_call_id, td_api::object_ptr &&payload, int32 source, - bool is_muted, Promise> &&promise); + void join_group_call(GroupCallId group_call_id, td_api::object_ptr &&payload, + int32 audio_source, bool is_muted, + Promise> &&promise); void toggle_group_call_mute_new_participants(GroupCallId group_call_id, bool mute_new_participants, Promise &&promise); void invite_group_call_participants(GroupCallId group_call_id, vector &&user_ids, Promise &&promise); - void set_group_call_participant_is_speaking(GroupCallId group_call_id, int32 source, bool is_speaking, + void set_group_call_participant_is_speaking(GroupCallId group_call_id, int32 audio_source, bool is_speaking, Promise &&promise, int32 date = 0); void toggle_group_call_participant_is_muted(GroupCallId group_call_id, UserId user_id, bool is_muted, @@ -131,7 +132,8 @@ class GroupCallManager : public Actor { void finish_get_group_call(InputGroupCallId input_group_call_id, Result> &&result); - void finish_check_group_call_is_joined(InputGroupCallId input_group_call_id, int32 source, Result &&result); + void finish_check_group_call_is_joined(InputGroupCallId input_group_call_id, int32 audio_source, + Result &&result); bool need_group_call_participants(InputGroupCallId input_group_call_id) const; @@ -163,7 +165,7 @@ class GroupCallManager : public Actor { GroupCallParticipants *add_group_call_participants(InputGroupCallId input_group_call_id); - void on_group_call_left(InputGroupCallId input_group_call_id, int32 source, bool need_rejoin); + void on_group_call_left(InputGroupCallId input_group_call_id, int32 audio_source, bool need_rejoin); void on_group_call_left_impl(GroupCall *group_call, bool need_rejoin); @@ -178,7 +180,7 @@ class GroupCallManager : public Actor { void on_group_call_recent_speakers_updated(const GroupCall *group_call, GroupCallRecentSpeakers *recent_speakers); - UserId set_group_call_participant_is_speaking_by_source(InputGroupCallId input_group_call_id, int32 source, + UserId set_group_call_participant_is_speaking_by_source(InputGroupCallId input_group_call_id, int32 audio_source, bool is_speaking, int32 date); static Result> get_group_call_join_response_object( diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index 0b1d92962..76848c3ab 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -15,7 +15,7 @@ namespace td { GroupCallParticipant::GroupCallParticipant(const tl_object_ptr &participant) { CHECK(participant != nullptr); user_id = UserId(participant->user_id_); - source = participant->source_; + audio_source = participant->source_; is_muted = participant->muted_; can_self_unmute = participant->can_self_unmute_; is_muted_only_for_self = participant->muted_by_you_; @@ -93,13 +93,13 @@ td_api::object_ptr GroupCallParticipant::get_group } return td_api::make_object( - contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), source, is_speaking, + contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), audio_source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, can_be_unmuted_only_for_self, is_muted, can_self_unmute, muted_count, volume_level, order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { - return lhs.user_id == rhs.user_id && lhs.source == rhs.source && lhs.muted_count == rhs.muted_count && + return lhs.user_id == rhs.user_id && lhs.audio_source == rhs.audio_source && lhs.muted_count == rhs.muted_count && lhs.can_be_muted_for_all_users == rhs.can_be_muted_for_all_users && lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && @@ -113,8 +113,8 @@ bool operator!=(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs } StringBuilder &operator<<(StringBuilder &string_builder, const GroupCallParticipant &group_call_participant) { - return string_builder << '[' << group_call_participant.user_id << " with source " << group_call_participant.source - << " and order " << group_call_participant.order << ']'; + return string_builder << '[' << group_call_participant.user_id << " with source " + << group_call_participant.audio_source << " and order " << group_call_participant.order << ']'; } } // namespace td diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 8c36de915..e3be59b19 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -19,7 +19,7 @@ class ContactsManager; struct GroupCallParticipant { UserId user_id; - int32 source = 0; + int32 audio_source = 0; int32 joined_date = 0; int32 active_date = 0; int32 muted_count = 0; From 0b7ce1c6471086da3baad037f26cdfc10f04ce11 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 12 Jan 2021 17:05:25 +0300 Subject: [PATCH 032/232] Update layer 123. Support new generateChatInviteLink. --- td/generate/scheme/td_api.tl | 14 +- td/generate/scheme/td_api.tlo | Bin 192040 -> 192372 bytes td/generate/scheme/telegram_api.tl | 20 ++- td/generate/scheme/telegram_api.tlo | Bin 224580 -> 226796 bytes td/telegram/ContactsManager.cpp | 242 +++++++++------------------- td/telegram/ContactsManager.h | 22 +-- td/telegram/MessagesManager.cpp | 37 ----- td/telegram/MessagesManager.h | 4 - td/telegram/Td.cpp | 21 +-- td/telegram/cli.cpp | 6 +- 10 files changed, 116 insertions(+), 250 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index a5bec024a..7a6228c45 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -884,8 +884,11 @@ chatNearby chat_id:int53 distance:int32 = ChatNearby; chatsNearby users_nearby:vector supergroups_nearby:vector = ChatsNearby; -//@description Contains a chat invite link @invite_link Chat invite link -chatInviteLink invite_link:string = ChatInviteLink; +//@description Contains a chat invite link @invite_link Chat invite link @administrator_user_id User identifier of an administrator created the link +//@date Point in time (Unix timestamp) when the link was created @expire_date Point in time (Unix timestamp) when the link will expire; 0 if never +//@usage_limit Maximum number of times the link can be used; 0 if not limited @usage_count Number of times the link has already been used +//@is_expired True, if the link is already expired @is_revoked True, if the link was revoked +chatInviteLink invite_link:string administrator_user_id:int32 date:int32 expire_date:int32 usage_limit:int32 usage_count:int32 is_expired:Bool is_revoked:Bool = ChatInviteLink; //@description Contains information about a chat invite link //@chat_id Chat identifier of the invite link; 0 if the user has no access to the chat before joining @@ -4346,8 +4349,11 @@ readFilePart file_id:int32 offset:int32 count:int32 = FilePart; deleteFile file_id:int32 = Ok; -//@description Generates a new invite link for a chat; the previously generated link is revoked. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right @chat_id Chat identifier -generateChatInviteLink chat_id:int53 = ChatInviteLink; +//@description Generates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right +//@chat_id Chat identifier +//@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never +//@usage_limit Maximum number of times the link can be used; pass 0 if not limited +generateChatInviteLink chat_id:int53 expire_date:int32 usage_limit:int32 = ChatInviteLink; //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; should begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index dda0fc66aeac84a7f99b8f3407380c79fe7209cd..2cbf5b843b255b0c0f19a60681ee42542d303036 100644 GIT binary patch delta 254 zcmZ2+hx^Mt?hQBCSzLVHUEO?#{ZGCuh{czjkyzrHSC(0l>XVt5%>WX+c}&QMWBNxk zCiTe^Ojv|P6H{_C^D>J|iV{omi{eX*Q;Vjrf5Rv@`E82)bb%H|7Us;N)am(~m}Ggl zON$fJQ{!_ob2CdOU+|J-1@ouh<7brPgobF)7sKd?;HV~WT2drekCT@?2 IWV$c~070o_1poj5 delta 94 zcmexzk9)-(?hQBCSwfcNoZftg{ZGCGh{czjkyzrHSC(0l>XVt5%>WXc{=uBd1gx%E mr*gYaCF7k3EFl%&il_5LF-d^MryJ-p8Gu>amqamLm;wOc!YHr+ diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 8fddc7b7c..ffe3710a7 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -114,8 +114,8 @@ chatForbidden#7328bdb id:int title:string = Chat; channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#dc8c181 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; -channelFull#ef3a6acd flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall = ChatFull; +chatFull#f3474af6 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; +channelFull#7a7de4f7 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -522,8 +522,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteEmpty#69df3769 = ExportedChatInvite; -chatInviteExported#fc2e05bc link:string = ExportedChatInvite; +chatInviteExported#a9a847ea flags:# revoked:flags.0?true expired:flags.4?true permanent:flags.5?true link:string admin_id:int date:int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; @@ -1194,6 +1193,14 @@ inlineQueryPeerTypeChat#d766c50a = InlineQueryPeerType; inlineQueryPeerTypeMegagroup#5ec4be43 = InlineQueryPeerType; inlineQueryPeerTypeBroadcast#6334ee9a = InlineQueryPeerType; +chatInviteImporter#1e3e6680 user_id:int date:int = ChatInviteImporter; + +messages.exportedChatInvites#bdc62dcc count:int invites:Vector users:Vector = messages.ExportedChatInvites; + +messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector = messages.ExportedChatInvite; + +messages.chatInviteImporters#81b6b00a count:int importers:Vector users:Vector = messages.ChatInviteImporters; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1353,7 +1360,7 @@ messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; +messages.exportChatInvite#14b9bcd7 flags:# legacy_revoke_permanent:flags.2?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1443,6 +1450,9 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; +messages.getExportedChatInvites#6d9cae03 flags:# peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; +messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index 786729a6d49fbbb4cfe45fafa7c81f9f0a9cbf11..f19c70fbd63414c34680a5d195436901bdf137c6 100644 GIT binary patch delta 1273 zcmZuvUr19?9NsQ?en{HftLrJT|AhOI_(C?6r zk8mnV4blN9lTT^Gjd|kc&K_XGk)J?~qWvng862!8|D}PVH)X}=x7P>yGFCuetd@IlSZ`Gp;yRLP>0LOK^&oJgFHwYb9mfdH;4Sgda;-cFr}gPA^V1!A%|Y;nTqvH zQPu~7A8fA%VLbQ>sLc7L=OkxJ4qfp4GSa7Z<3n|kTUb`1v`nu8X#+tD00hc={zxzTbkgj9#9k4Do53#>0bEdi>*Su?W1DR&UsP0=;#s*%L(MBAlX(&+wI9aw+_jUrF#QW<$B705NB!l#yjN+Iv->Iy(E WxwF1rWNFvqj@2Z+&`d-^FTlSY_Szc& delta 414 zcmaF!n)k>pUfxHu^{p77zQ9e2oHG(j+)8tDCVym?o;-s^WAg`=02cA9 zMjxjNxmFb97nP)@fRuRVm1UNsPR?hR++4tUMs0G1!I8}y3`^J<3pcA;-nJHLJa~ea zBN=KI&=!!{lN}}-Y<4NtVdMc>%VFDg5M=D+m}-gX8w8mIrZ@a!5}ACUY=#O*G1O%Y zSLL{3&SqJ?=LQ;n~W)`2|@4Qnh hm}S7aCo8-$05i5XFfu=3-5$che1~nji2(DCQ~+- promise_; - ChatId chat_id_; + Promise> promise_; + DialogId dialog_id_; public: - explicit ExportChatInviteLinkQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit ExportChatInviteLinkQuery(Promise> &&promise) + : promise_(std::move(promise)) { } - void send(ChatId chat_id) { - chat_id_ = chat_id; - auto input_peer = td->messages_manager_->get_input_peer(DialogId(chat_id), AccessRights::Read); + void send(DialogId dialog_id, int32 expire_date, int32 usage_limit) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite(std::move(input_peer)))); + + int32 flags = 0; + if (expire_date > 0) { + flags |= telegram_api::messages_exportChatInvite::EXPIRE_DATE_MASK; + } + if (usage_limit > 0) { + flags |= telegram_api::messages_exportChatInvite::USAGE_LIMIT_MASK; + } + send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( + flags, false /*ignored*/, std::move(input_peer), expire_date, usage_limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1561,8 +1571,21 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(ptr); - td->contacts_manager_->on_get_chat_invite_link(chat_id_, std::move(ptr)); - promise_.set_value(Unit()); + int32 expire_date = 0; + if ((ptr->flags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { + expire_date = ptr->expire_date_; + } + int32 usage_limit = 0; + if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_LIMIT_MASK) != 0) { + usage_limit = ptr->usage_limit_; + } + int32 usage_count = 0; + if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_MASK) != 0) { + usage_count = ptr->usage_; + } + auto invite_link = td_api::make_object( + ptr->link_, ptr->admin_id_, ptr->date_, expire_date, usage_limit, usage_count, ptr->expired_, ptr->revoked_); + promise_.set_value(std::move(invite_link)); } void on_error(uint64 id, Status status) override { @@ -1571,43 +1594,6 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { } }; -class ExportChannelInviteLinkQuery : public Td::ResultHandler { - Promise promise_; - ChannelId channel_id_; - - public: - explicit ExportChannelInviteLinkQuery(Promise &&promise) : promise_(std::move(promise)) { - } - - void send(ChannelId channel_id) { - channel_id_ = channel_id; - auto input_peer = td->messages_manager_->get_input_peer(DialogId(channel_id), AccessRights::Read); - if (input_peer == nullptr) { - return on_error(0, Status::Error(400, "Can't access the chat")); - } - send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite(std::move(input_peer)))); - } - - void on_result(uint64 id, BufferSlice packet) override { - auto result_ptr = fetch_result(packet); - if (result_ptr.is_error()) { - return on_error(id, result_ptr.move_as_error()); - } - - auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for ExportChannelInviteQuery: " << to_string(ptr); - - td->contacts_manager_->on_get_channel_invite_link(channel_id_, std::move(ptr)); - promise_.set_value(Unit()); - } - - void on_error(uint64 id, Status status) override { - td->contacts_manager_->on_get_channel_error(channel_id_, status, "ExportChannelInviteLinkQuery"); - promise_.set_error(std::move(status)); - td->updates_manager_->get_difference("ExportChannelInviteLinkQuery"); - } -}; - class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -6494,33 +6480,47 @@ void ContactsManager::transfer_channel_ownership( ->send(channel_id, user_id, std::move(input_check_password)); } -void ContactsManager::export_chat_invite_link(ChatId chat_id, Promise &&promise) { - const Chat *c = get_chat(chat_id); - if (c == nullptr) { - return promise.set_error(Status::Error(3, "Chat info not found")); - } - if (!c->is_active) { - return promise.set_error(Status::Error(3, "Chat is deactivated")); +void ContactsManager::export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, + Promise> &&promise) { + LOG(INFO) << "Receive ExportDialogInviteLink request for " << dialog_id; + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); } - if (!get_chat_status(c).is_administrator() || !get_chat_status(c).can_invite_users()) { - return promise.set_error(Status::Error(3, "Not enough rights to export chat invite link")); + switch (dialog_id.get_type()) { + case DialogType::User: + return promise.set_error(Status::Error(3, "Can't invite members to a private chat")); + case DialogType::Chat: { + const Chat *c = get_chat(dialog_id.get_chat_id()); + if (c == nullptr) { + return promise.set_error(Status::Error(3, "Chat info not found")); + } + if (!c->is_active) { + return promise.set_error(Status::Error(3, "Chat is deactivated")); + } + if (!get_chat_status(c).is_administrator() || !get_chat_status(c).can_invite_users()) { + return promise.set_error(Status::Error(3, "Not enough rights to export chat invite link")); + } + break; + } + case DialogType::Channel: { + const Channel *c = get_channel(dialog_id.get_channel_id()); + if (c == nullptr) { + return promise.set_error(Status::Error(3, "Chat info not found")); + } + if (!get_channel_status(c).is_administrator() || !get_channel_status(c).can_invite_users()) { + return promise.set_error(Status::Error(3, "Not enough rights to export chat invite link")); + } + break; + } + case DialogType::SecretChat: + return promise.set_error(Status::Error(3, "Can't invite members to a secret chat")); + case DialogType::None: + default: + UNREACHABLE(); } - td_->create_handler(std::move(promise))->send(chat_id); -} - -void ContactsManager::export_channel_invite_link(ChannelId channel_id, Promise &&promise) { - const Channel *c = get_channel(channel_id); - if (c == nullptr) { - return promise.set_error(Status::Error(3, "Chat info not found")); - } - - if (!get_channel_status(c).is_administrator() || !get_channel_status(c).can_invite_users()) { - return promise.set_error(Status::Error(3, "Not enough rights to export chat invite link")); - } - - td_->create_handler(std::move(promise))->send(channel_id); + td_->create_handler(std::move(promise))->send(dialog_id, expire_date, usage_limit); } void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { @@ -6543,25 +6543,6 @@ void ContactsManager::import_dialog_invite_link(const string &invite_link, Promi td_->create_handler(std::move(promise))->send(invite_link); } -string ContactsManager::get_chat_invite_link(ChatId chat_id) const { - auto chat_full = get_chat_full(chat_id); - if (chat_full == nullptr) { - auto it = dialog_invite_links_.find(DialogId(chat_id)); - return it == dialog_invite_links_.end() ? string() : it->second; - } - return chat_full->invite_link; -} - -string ContactsManager::get_channel_invite_link( - ChannelId channel_id) { // should be non-const to update ChannelFull cache - auto channel_full = get_channel_full(channel_id, "get_channel_invite_link"); - if (channel_full == nullptr) { - auto it = dialog_invite_links_.find(DialogId(channel_id)); - return it == dialog_invite_links_.end() ? string() : it->second; - } - return channel_full->invite_link; -} - void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, Promise &&promise) { const Chat *c = get_chat(chat_id); if (c == nullptr) { @@ -11017,11 +10998,6 @@ void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool need_dr } if (need_drop_invite_link) { remove_dialog_access_by_invite_link(DialogId(channel_id)); - - auto it = dialog_invite_links_.find(DialogId(channel_id)); - if (it != dialog_invite_links_.end()) { - invalidate_invite_link_info(it->second); - } } } @@ -11099,52 +11075,18 @@ void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, Ch } } -void ContactsManager::on_get_chat_invite_link(ChatId chat_id, - tl_object_ptr &&invite_link_ptr) { - CHECK(chat_id.is_valid()); - if (!have_chat_force(chat_id)) { - LOG(ERROR) << chat_id << " not found"; - return; - } - - auto chat_full = get_chat_full_force(chat_id, "on_get_chat_invite_link"); - if (chat_full == nullptr) { - update_invite_link(dialog_invite_links_[DialogId(chat_id)], std::move(invite_link_ptr)); - return; - } - on_update_chat_full_invite_link(chat_full, std::move(invite_link_ptr)); - update_chat_full(chat_full, chat_id); -} - -void ContactsManager::on_update_chat_full_invite_link( - ChatFull *chat_full, tl_object_ptr &&invite_link_ptr) { +void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full, + tl_object_ptr &&invite_link) { CHECK(chat_full != nullptr); - if (update_invite_link(chat_full->invite_link, std::move(invite_link_ptr))) { + if (update_invite_link(chat_full->invite_link, std::move(invite_link))) { chat_full->is_changed = true; } } -void ContactsManager::on_get_channel_invite_link(ChannelId channel_id, - tl_object_ptr &&invite_link_ptr) { - CHECK(channel_id.is_valid()); - if (!have_channel(channel_id)) { - LOG(ERROR) << channel_id << " not found"; - return; - } - - auto channel_full = get_channel_full_force(channel_id, "on_get_channel_invite_link"); - if (channel_full == nullptr) { - update_invite_link(dialog_invite_links_[DialogId(channel_id)], std::move(invite_link_ptr)); - return; - } - on_update_channel_full_invite_link(channel_full, std::move(invite_link_ptr)); - update_channel_full(channel_full, channel_id); -} - void ContactsManager::on_update_channel_full_invite_link( - ChannelFull *channel_full, tl_object_ptr &&invite_link_ptr) { + ChannelFull *channel_full, tl_object_ptr &&invite_link) { CHECK(channel_full != nullptr); - if (update_invite_link(channel_full->invite_link, std::move(invite_link_ptr))) { + if (update_invite_link(channel_full->invite_link, std::move(invite_link))) { channel_full->is_changed = true; } } @@ -11387,13 +11329,6 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, invite_link_info_expire_timeout_.set_timeout_in(dialog_id.get(), expires_in); } } - - if (chat_id.is_valid()) { - on_get_chat_invite_link(chat_id, make_tl_object(invite_link)); - } - if (channel_id.is_valid()) { - on_get_channel_invite_link(channel_id, make_tl_object(invite_link)); - } break; } case telegram_api::chatInvite::ID: { @@ -11486,21 +11421,10 @@ Slice ContactsManager::get_dialog_invite_link_hash(const string &invite_link) { } bool ContactsManager::update_invite_link(string &invite_link, - tl_object_ptr &&invite_link_ptr) { + tl_object_ptr &&exported_chat_invite) { string new_invite_link; - if (invite_link_ptr != nullptr) { - switch (invite_link_ptr->get_id()) { - case telegram_api::chatInviteEmpty::ID: - // link is empty - break; - case telegram_api::chatInviteExported::ID: { - auto chat_invite_exported = move_tl_object_as(invite_link_ptr); - new_invite_link = std::move(chat_invite_exported->link_); - break; - } - default: - UNREACHABLE(); - } + if (exported_chat_invite != nullptr) { + new_invite_link = std::move(exported_chat_invite->link_); } if (new_invite_link != invite_link) { @@ -11723,7 +11647,6 @@ void ContactsManager::on_update_chat_delete_user(ChatId chat_id, UserId user_id, void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParticipantStatus status) { if (c->status != status) { LOG(INFO) << "Update " << chat_id << " status from " << c->status << " to " << status; - bool need_drop_invite_link = c->status.is_left() != status.is_left(); bool need_reload_group_call = c->status.can_manage_calls() != status.can_manage_calls(); c->status = status; @@ -11736,12 +11659,6 @@ void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParti drop_chat_full(chat_id); } - if (need_drop_invite_link) { - auto it = dialog_invite_links_.find(DialogId(chat_id)); - if (it != dialog_invite_links_.end()) { - invalidate_invite_link_info(it->second); - } - } if (need_reload_group_call) { send_closure_later(G()->messages_manager(), &MessagesManager::on_update_dialog_group_call_rights, DialogId(chat_id)); @@ -12015,11 +11932,6 @@ void ContactsManager::drop_chat_full(ChatId chat_id) { ChatFull *chat_full = get_chat_full_force(chat_id, "drop_chat_full"); if (chat_full == nullptr) { drop_chat_photos(chat_id, false, false, "drop_chat_full"); - - auto it = dialog_invite_links_.find(DialogId(chat_id)); - if (it != dialog_invite_links_.end()) { - invalidate_invite_link_info(it->second); - } return; } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 2d9b0b67f..850e30da9 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -222,11 +222,6 @@ class ContactsManager : public Actor { static Slice get_dialog_invite_link_hash(const string &invite_link); - void on_get_chat_invite_link(ChatId chat_id, tl_object_ptr &&invite_link_ptr); - - void on_get_channel_invite_link(ChannelId channel_id, - tl_object_ptr &&invite_link_ptr); - void on_get_dialog_invite_link_info(const string &invite_link, tl_object_ptr &&chat_invite_ptr, Promise &&promise); @@ -393,18 +388,13 @@ class ContactsManager : public Actor { void transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, Promise &&promise); - void export_chat_invite_link(ChatId chat_id, Promise &&promise); - - void export_channel_invite_link(ChannelId channel_id, Promise &&promise); + void export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, + Promise> &&promise); void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); - string get_chat_invite_link(ChatId chat_id) const; - - string get_channel_invite_link(ChannelId channel_id); - ChannelId migrate_chat_to_megagroup(ChatId chat_id, Promise &promise); vector get_created_public_dialogs(PublicDialogType type, Promise &&promise); @@ -1035,6 +1025,7 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FULL_FLAG_CAN_VIEW_STATISTICS = 1 << 20; static constexpr int32 CHANNEL_FULL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 21; static constexpr int32 CHANNEL_FULL_FLAG_IS_BLOCKED = 1 << 22; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_EXPORTED_INVITE = 1 << 23; static constexpr int32 CHAT_INVITE_FLAG_IS_CHANNEL = 1 << 0; static constexpr int32 CHAT_INVITE_FLAG_IS_BROADCAST = 1 << 1; @@ -1176,7 +1167,7 @@ class ContactsManager : public Actor { void on_update_chat_full_participants(ChatFull *chat_full, ChatId chat_id, vector participants, int32 version, bool from_update); void on_update_chat_full_invite_link(ChatFull *chat_full, - tl_object_ptr &&invite_link_ptr); + tl_object_ptr &&invite_link); void on_update_channel_photo(Channel *c, ChannelId channel_id, tl_object_ptr &&chat_photo_ptr); @@ -1189,7 +1180,7 @@ class ContactsManager : public Actor { void on_update_channel_full_photo(ChannelFull *channel_full, ChannelId channel_id, Photo photo); void on_update_channel_full_invite_link(ChannelFull *channel_full, - tl_object_ptr &&invite_link_ptr); + tl_object_ptr &&invite_link); void on_update_channel_full_linked_channel_id(ChannelFull *channel_full, ChannelId channel_id, ChannelId linked_channel_id); void on_update_channel_full_location(ChannelFull *channel_full, ChannelId channel_id, const DialogLocation &location); @@ -1354,7 +1345,7 @@ class ContactsManager : public Actor { static bool is_valid_invite_link(const string &invite_link); - bool update_invite_link(string &invite_link, tl_object_ptr &&invite_link_ptr); + bool update_invite_link(string &invite_link, tl_object_ptr &&exported_chat_invite); const DialogParticipant *get_chat_participant(ChatId chat_id, UserId user_id) const; @@ -1511,7 +1502,6 @@ class ContactsManager : public Actor { std::unordered_set invite_links; int32 accessible_before = 0; }; - std::unordered_map dialog_invite_links_; // in-memory cache for invite links std::unordered_map> invite_link_infos_; std::unordered_map dialog_access_by_invite_link_; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 9f967fe21..5555c69a7 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -30414,43 +30414,6 @@ vector MessagesManager::get_dialog_administrators(DialogId return {}; } -void MessagesManager::export_dialog_invite_link(DialogId dialog_id, Promise &&promise) { - LOG(INFO) << "Receive ExportDialogInviteLink request for " << dialog_id; - if (!have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(3, "Chat not found")); - } - - switch (dialog_id.get_type()) { - case DialogType::User: - return promise.set_error(Status::Error(3, "Can't invite members to a private chat")); - case DialogType::Chat: - return td_->contacts_manager_->export_chat_invite_link(dialog_id.get_chat_id(), std::move(promise)); - case DialogType::Channel: - return td_->contacts_manager_->export_channel_invite_link(dialog_id.get_channel_id(), std::move(promise)); - case DialogType::SecretChat: - return promise.set_error(Status::Error(3, "Can't invite members to a secret chat")); - case DialogType::None: - default: - UNREACHABLE(); - } -} - -string MessagesManager::get_dialog_invite_link(DialogId dialog_id) { - switch (dialog_id.get_type()) { - case DialogType::Chat: - return td_->contacts_manager_->get_chat_invite_link(dialog_id.get_chat_id()); - case DialogType::Channel: - return td_->contacts_manager_->get_channel_invite_link(dialog_id.get_channel_id()); - case DialogType::User: - case DialogType::SecretChat: - case DialogType::None: - return string(); - default: - UNREACHABLE(); - return string(); - } -} - tl_object_ptr MessagesManager::get_channel_admin_log_events_filter( const tl_object_ptr &filters) { if (filters == nullptr) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 4f1ffde27..4b4c06c81 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -482,10 +482,6 @@ class MessagesManager : public Actor { vector get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise); - void export_dialog_invite_link(DialogId dialog_id, Promise &&promise); - - string get_dialog_invite_link(DialogId dialog_id); - void get_dialog_info_full(DialogId dialog_id, Promise &&promise); int64 get_dialog_event_log(DialogId dialog_id, const string &query, int64 from_event_id, int32 limit, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index a942407bb..146926750 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1985,23 +1985,6 @@ class GetChatAdministratorsRequest : public RequestActor<> { } }; -class GenerateChatInviteLinkRequest : public RequestOnceActor { - DialogId dialog_id_; - - void do_run(Promise &&promise) override { - td->messages_manager_->export_dialog_invite_link(dialog_id_, std::move(promise)); - } - - void do_send_result() override { - send_result(make_tl_object(td->messages_manager_->get_dialog_invite_link(dialog_id_))); - } - - public: - GenerateChatInviteLinkRequest(ActorShared td, uint64 request_id, int64 dialog_id) - : RequestOnceActor(std::move(td), request_id), dialog_id_(dialog_id) { - } -}; - class CheckChatInviteLinkRequest : public RequestActor<> { string invite_link_; @@ -6293,7 +6276,9 @@ void Td::on_request(uint64 id, td_api::getChatAdministrators &request) { } void Td::on_request(uint64 id, const td_api::generateChatInviteLink &request) { - CREATE_REQUEST(GenerateChatInviteLinkRequest, request.chat_id_); + CREATE_REQUEST_PROMISE(); + contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.usage_limit_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 11f42bf9b..23f3e3792 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2683,7 +2683,11 @@ class CliClient final : public Actor { } else if (op == "dgc") { send_request(td_api::make_object(as_group_call_id(args))); } else if (op == "gcil") { - send_request(td_api::make_object(as_chat_id(args))); + string chat_id; + int32 expire_date; + int32 usage_limit; + get_args(args, chat_id, expire_date, usage_limit); + send_request(td_api::make_object(as_chat_id(chat_id), expire_date, usage_limit)); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From 87a2423f5aa333a35e1f73c5f47994a1844f164a Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 12 Jan 2021 19:21:12 +0300 Subject: [PATCH 033/232] Add editChatInviteLink method. --- td/generate/scheme/td_api.tl | 16 +++-- td/generate/scheme/td_api.tlo | Bin 192372 -> 192608 bytes td/telegram/ContactsManager.cpp | 113 ++++++++++++++++++++++++++++---- td/telegram/ContactsManager.h | 5 ++ td/telegram/Td.cpp | 9 ++- td/telegram/Td.h | 4 +- td/telegram/cli.cpp | 11 +++- 7 files changed, 138 insertions(+), 20 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 7a6228c45..c524e1d11 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4349,17 +4349,25 @@ readFilePart file_id:int32 offset:int32 count:int32 = FilePart; deleteFile file_id:int32 = Ok; -//@description Generates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right +//@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right //@chat_id Chat identifier //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@usage_limit Maximum number of times the link can be used; pass 0 if not limited -generateChatInviteLink chat_id:int53 expire_date:int32 usage_limit:int32 = ChatInviteLink; +createChatInviteLink chat_id:int53 expire_date:int32 usage_limit:int32 = ChatInviteLink; -//@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; should begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" +//@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right +//@chat_id Chat identifier +//@invite_link Invite link to be edited; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" +//@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never +//@usage_limit Maximum number of times the link can be used; pass 0 if not limited +//@is_revoked True, if the link is revoked +editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 usage_limit:int32 is_revoked:Bool = ChatInviteLink; + +//@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; //@description Uses an invite link to add the current user to the chat if possible. The new member will not be added until the chat state has been synchronized with the server -//@invite_link Invite link to import; should begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" +//@invite_link Invite link to import; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" joinChatByInviteLink invite_link:string = Chat; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 2cbf5b843b255b0c0f19a60681ee42542d303036..00997a39296c16944a330fbec310437de57a55ef 100644 GIT binary patch delta 123 zcmV->0EGYa-V5O13xI?Hv;q_+0g|^BCIXWgAoDeAgcM_PWnpw>Lug@iNp5y&bY)Cw zZfgJl07Ojg)tAuS0W6nPS^`|Rs6PTg4hm^n4*3#gWNCCmliqh0mlP@jD3iQMAd> promise_; + DialogId dialog_id_; + + public: + explicit EditChatInviteLinkQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit, bool is_revoked) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + int32 flags = 0; + if (expire_date > 0) { + flags |= telegram_api::messages_editExportedChatInvite::EXPIRE_DATE_MASK; + } + if (usage_limit > 0) { + flags |= telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK; + } + if (is_revoked) { + flags |= telegram_api::messages_editExportedChatInvite::REVOKED_MASK; + } + send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( + flags, false /*ignored*/, std::move(input_peer), invite_link, expire_date, usage_limit))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(result); + + td->contacts_manager_->on_get_users(std::move(result->users_), "EditChatInviteLinkQuery"); + + auto recent_importers = std::move(result->recent_importers_); + auto ptr = std::move(result->invite_); + int32 expire_date = 0; + if ((ptr->flags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { + expire_date = ptr->expire_date_; + } + int32 usage_limit = 0; + if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_LIMIT_MASK) != 0) { + usage_limit = ptr->usage_limit_; + } + int32 usage_count = 0; + if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_MASK) != 0) { + usage_count = ptr->usage_; + } + auto invite_link = td_api::make_object( + ptr->link_, ptr->admin_id_, ptr->date_, expire_date, usage_limit, usage_count, ptr->expired_, ptr->revoked_); + promise_.set_value(std::move(invite_link)); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + td->updates_manager_->get_difference("EditChatInviteLinkQuery"); + } +}; + class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -6480,56 +6546,77 @@ void ContactsManager::transfer_channel_ownership( ->send(channel_id, user_id, std::move(input_check_password)); } -void ContactsManager::export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, - Promise> &&promise) { - LOG(INFO) << "Receive ExportDialogInviteLink request for " << dialog_id; +Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id) { if (!td_->messages_manager_->have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(3, "Chat not found")); + return Status::Error(3, "Chat not found"); } switch (dialog_id.get_type()) { case DialogType::User: - return promise.set_error(Status::Error(3, "Can't invite members to a private chat")); + return Status::Error(3, "Can't invite members to a private chat"); case DialogType::Chat: { const Chat *c = get_chat(dialog_id.get_chat_id()); if (c == nullptr) { - return promise.set_error(Status::Error(3, "Chat info not found")); + return Status::Error(3, "Chat info not found"); } if (!c->is_active) { - return promise.set_error(Status::Error(3, "Chat is deactivated")); + return Status::Error(3, "Chat is deactivated"); } if (!get_chat_status(c).is_administrator() || !get_chat_status(c).can_invite_users()) { - return promise.set_error(Status::Error(3, "Not enough rights to export chat invite link")); + return Status::Error(3, "Not enough rights to manage chat invite link"); } break; } case DialogType::Channel: { const Channel *c = get_channel(dialog_id.get_channel_id()); if (c == nullptr) { - return promise.set_error(Status::Error(3, "Chat info not found")); + return Status::Error(3, "Chat info not found"); } if (!get_channel_status(c).is_administrator() || !get_channel_status(c).can_invite_users()) { - return promise.set_error(Status::Error(3, "Not enough rights to export chat invite link")); + return Status::Error(3, "Not enough rights to manage chat invite link"); } break; } case DialogType::SecretChat: - return promise.set_error(Status::Error(3, "Can't invite members to a secret chat")); + return Status::Error(3, "Can't invite members to a secret chat"); case DialogType::None: default: UNREACHABLE(); } + return Status::OK(); +} + +void ContactsManager::export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, + Promise> &&promise) { + LOG(INFO) << "Receive CreateDialogInviteLink request for " << dialog_id; + + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); td_->create_handler(std::move(promise))->send(dialog_id, expire_date, usage_limit); } +void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, int32 expire_date, + int32 usage_limit, bool is_revoked, + Promise> &&promise) { + LOG(INFO) << "Receive EditDialogInviteLink request for link " << invite_link << " in " << dialog_id; + + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (!is_valid_invite_link(invite_link)) { + return promise.set_error(Status::Error(400, "Wrong invite link")); + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, invite_link, expire_date, usage_limit, is_revoked); +} + void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { if (invite_link_infos_.count(invite_link) > 0) { return promise.set_value(Unit()); } if (!is_valid_invite_link(invite_link)) { - return promise.set_error(Status::Error(3, "Wrong invite link")); + return promise.set_error(Status::Error(400, "Wrong invite link")); } td_->create_handler(std::move(promise))->send(invite_link); @@ -6537,7 +6624,7 @@ void ContactsManager::check_dialog_invite_link(const string &invite_link, Promis void ContactsManager::import_dialog_invite_link(const string &invite_link, Promise &&promise) { if (!is_valid_invite_link(invite_link)) { - return promise.set_error(Status::Error(3, "Wrong invite link")); + return promise.set_error(Status::Error(400, "Wrong invite link")); } td_->create_handler(std::move(promise))->send(invite_link); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 850e30da9..15bd1eb8d 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -391,6 +391,9 @@ class ContactsManager : public Actor { void export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, Promise> &&promise); + void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, + bool is_revoked, Promise> &&promise); + void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); @@ -1343,6 +1346,8 @@ class ContactsManager : public Actor { void remove_dialog_access_by_invite_link(DialogId dialog_id); + Status can_manage_dialog_invite_links(DialogId dialog_id); + static bool is_valid_invite_link(const string &invite_link); bool update_invite_link(string &invite_link, tl_object_ptr &&exported_chat_invite); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 146926750..d89a5ba12 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6275,12 +6275,19 @@ void Td::on_request(uint64 id, td_api::getChatAdministrators &request) { CREATE_REQUEST(GetChatAdministratorsRequest, request.chat_id_); } -void Td::on_request(uint64 id, const td_api::generateChatInviteLink &request) { +void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.usage_limit_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { + CREATE_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.invite_link_); + contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, request.expire_date_, + request.usage_limit_, request.is_revoked_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 5acb98ef7..c0e782340 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -786,7 +786,9 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatAdministrators &request); - void on_request(uint64 id, const td_api::generateChatInviteLink &request); + void on_request(uint64 id, const td_api::createChatInviteLink &request); + + void on_request(uint64 id, td_api::editChatInviteLink &request); void on_request(uint64 id, td_api::checkChatInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 23f3e3792..c8309dbb4 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2687,7 +2687,16 @@ class CliClient final : public Actor { int32 expire_date; int32 usage_limit; get_args(args, chat_id, expire_date, usage_limit); - send_request(td_api::make_object(as_chat_id(chat_id), expire_date, usage_limit)); + send_request(td_api::make_object(as_chat_id(chat_id), expire_date, usage_limit)); + } else if (op == "ecil") { + string chat_id; + string invite_link; + int32 expire_date; + int32 usage_limit; + bool is_revoked; + get_args(args, chat_id, invite_link, expire_date, usage_limit, is_revoked); + send_request(td_api::make_object(as_chat_id(chat_id), invite_link, expire_date, + usage_limit, is_revoked)); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From b5cd03365d1eb4a83546b3a9580c73d4b7e59bd8 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 13 Jan 2021 16:28:24 +0300 Subject: [PATCH 034/232] Add DialogInviteLink class. --- CMakeLists.txt | 2 + td/telegram/ContactsManager.cpp | 42 ++--------- td/telegram/ContactsManager.h | 1 + td/telegram/DialogAdministrator.h | 2 +- td/telegram/DialogInviteLink.cpp | 85 +++++++++++++++++++++++ td/telegram/DialogInviteLink.h | 112 ++++++++++++++++++++++++++++++ 6 files changed, 208 insertions(+), 36 deletions(-) create mode 100644 td/telegram/DialogInviteLink.cpp create mode 100644 td/telegram/DialogInviteLink.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bb81b3b88..7faa9a005 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -291,6 +291,7 @@ set(TDLIB_SOURCE td/telegram/DialogDb.cpp td/telegram/DialogFilter.cpp td/telegram/DialogId.cpp + td/telegram/DialogInviteLink.cpp td/telegram/DialogLocation.cpp td/telegram/DialogParticipant.cpp td/telegram/DialogSource.cpp @@ -456,6 +457,7 @@ set(TDLIB_SOURCE td/telegram/DialogFilter.h td/telegram/DialogFilterId.h td/telegram/DialogId.h + td/telegram/DialogInviteLink.h td/telegram/DialogListId.h td/telegram/DialogLocation.h td/telegram/DialogParticipant.h diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 2db8b22bf..00165411e 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1571,21 +1571,8 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(ptr); - int32 expire_date = 0; - if ((ptr->flags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { - expire_date = ptr->expire_date_; - } - int32 usage_limit = 0; - if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_LIMIT_MASK) != 0) { - usage_limit = ptr->usage_limit_; - } - int32 usage_count = 0; - if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_MASK) != 0) { - usage_count = ptr->usage_; - } - auto invite_link = td_api::make_object( - ptr->link_, ptr->admin_id_, ptr->date_, expire_date, usage_limit, usage_count, ptr->expired_, ptr->revoked_); - promise_.set_value(std::move(invite_link)); + DialogInviteLink invite_link(std::move(ptr)); + promise_.set_value(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); } void on_error(uint64 id, Status status) override { @@ -1635,23 +1622,8 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { td->contacts_manager_->on_get_users(std::move(result->users_), "EditChatInviteLinkQuery"); - auto recent_importers = std::move(result->recent_importers_); - auto ptr = std::move(result->invite_); - int32 expire_date = 0; - if ((ptr->flags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { - expire_date = ptr->expire_date_; - } - int32 usage_limit = 0; - if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_LIMIT_MASK) != 0) { - usage_limit = ptr->usage_limit_; - } - int32 usage_count = 0; - if ((ptr->flags_ & telegram_api::chatInviteExported::USAGE_MASK) != 0) { - usage_count = ptr->usage_; - } - auto invite_link = td_api::make_object( - ptr->link_, ptr->admin_id_, ptr->date_, expire_date, usage_limit, usage_count, ptr->expired_, ptr->revoked_); - promise_.set_value(std::move(invite_link)); + DialogInviteLink invite_link(std::move(result->invite_)); + promise_.set_value(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); } void on_error(uint64 id, Status status) override { @@ -6602,9 +6574,9 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - if (!is_valid_invite_link(invite_link)) { - return promise.set_error(Status::Error(400, "Wrong invite link")); - } + // if (!is_valid_invite_link(invite_link)) { + // return promise.set_error(Status::Error(400, "Wrong invite link")); + // } td_->create_handler(std::move(promise)) ->send(dialog_id, invite_link, expire_date, usage_limit, is_revoked); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 15bd1eb8d..5dea92fe0 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -15,6 +15,7 @@ #include "td/telegram/Contact.h" #include "td/telegram/DialogAdministrator.h" #include "td/telegram/DialogId.h" +#include "td/telegram/DialogInviteLink.h" #include "td/telegram/DialogLocation.h" #include "td/telegram/DialogParticipant.h" #include "td/telegram/files/FileId.h" diff --git a/td/telegram/DialogAdministrator.h b/td/telegram/DialogAdministrator.h index 4a6a51e77..a46eeb116 100644 --- a/td/telegram/DialogAdministrator.h +++ b/td/telegram/DialogAdministrator.h @@ -22,7 +22,7 @@ class DialogAdministrator { string rank_; bool is_creator_ = false; - friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogAdministrator &location); + friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogAdministrator &administrator); public: DialogAdministrator() = default; diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp new file mode 100644 index 000000000..e3441bd55 --- /dev/null +++ b/td/telegram/DialogInviteLink.cpp @@ -0,0 +1,85 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/DialogInviteLink.h" + +#include "td/telegram/ContactsManager.h" +#include "td/telegram/Global.h" + +namespace td { + +DialogInviteLink::DialogInviteLink(tl_object_ptr exported_invite) { + if (exported_invite == nullptr) { + return; + } + + invite_link_ = std::move(exported_invite->link_); + administrator_user_id_ = UserId(exported_invite->admin_id_); + if (!administrator_user_id_.is_valid()) { + LOG(ERROR) << "Receive invalid " << administrator_user_id_ << " as creator of a link " << invite_link_; + administrator_user_id_ = UserId(); + } + date_ = exported_invite->date_; + if (date_ < 1000000000) { + LOG(ERROR) << "Receive wrong date " << date_ << " as creation date of a link " << invite_link_; + date_ = 0; + } + if ((exported_invite->flags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { + expire_date_ = exported_invite->expire_date_; + } + if ((exported_invite->flags_ & telegram_api::chatInviteExported::USAGE_LIMIT_MASK) != 0) { + usage_limit_ = exported_invite->usage_limit_; + } + if ((exported_invite->flags_ & telegram_api::chatInviteExported::USAGE_MASK) != 0) { + usage_count_ = exported_invite->usage_; + } +} + +bool DialogInviteLink::is_expired() const { + return (expire_date_ != 0 && G()->unix_time() >= expire_date_) || (usage_limit_ != 0 && usage_count_ >= usage_limit_); +} + +int32 DialogInviteLink::get_expire_time() const { + if (expire_date_ == 0) { + return 0; + } + if (usage_limit_ != 0 && usage_count_ >= usage_limit_) { + // already expired + return 0; + } + return td::max(expire_date_ - G()->unix_time(), 0); +} + +td_api::object_ptr DialogInviteLink::get_chat_invite_link_object( + const ContactsManager *contacts_manager) const { + CHECK(contacts_manager != nullptr); + if (!is_valid()) { + return nullptr; + } + + return td_api::make_object( + invite_link_, contacts_manager->get_user_id_object(administrator_user_id_, "get_chat_invite_link_object"), date_, + expire_date_, usage_limit_, usage_count_, is_expired(), is_revoked_); +} + +bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { + return lhs.invite_link_ == rhs.invite_link_ && lhs.administrator_user_id_ == rhs.administrator_user_id_ && + lhs.date_ == rhs.date_ && lhs.expire_date_ == rhs.expire_date_ && lhs.usage_limit_ == rhs.usage_limit_ && + lhs.usage_count_ == rhs.usage_count_ && lhs.is_revoked_ == rhs.is_revoked_; +} + +bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link) { + return string_builder << "ChatInviteLink[" << invite_link.invite_link_ << " by " << invite_link.administrator_user_id_ + << " created at " << invite_link.date_ << " expiring at " << invite_link.expire_date_ + << " used by " << invite_link.usage_count_ << " with usage limit " << invite_link.usage_limit_ + << "]"; +} + +} // namespace td diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h new file mode 100644 index 000000000..ffa96d842 --- /dev/null +++ b/td/telegram/DialogInviteLink.h @@ -0,0 +1,112 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +class ContactsManager; + +class DialogInviteLink { + string invite_link_; + UserId administrator_user_id_; + int32 date_ = 0; + int32 expire_date_ = 0; + int32 usage_limit_ = 0; + int32 usage_count_ = 0; + bool is_revoked_ = false; + + friend bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link); + + public: + DialogInviteLink() = default; + + DialogInviteLink(tl_object_ptr exported_invite); + + td_api::object_ptr get_chat_invite_link_object(const ContactsManager *contacts_manager) const; + + bool is_valid() const { + return !invite_link_.empty() && administrator_user_id_.is_valid() && date_ > 0; + } + + bool is_expired() const; + + int32 get_expire_time() const; + + UserId get_administrator_user_id() const { + return administrator_user_id_; + } + + template + void store(StorerT &storer) const { + using td::store; + bool has_expire_date = expire_date_ != 0; + bool has_usage_limit = usage_limit_ != 0; + bool has_usage_count = usage_count_ != 0; + BEGIN_STORE_FLAGS(); + STORE_FLAG(is_revoked_); + STORE_FLAG(has_expire_date); + STORE_FLAG(has_usage_limit); + STORE_FLAG(has_usage_count); + END_STORE_FLAGS(); + store(invite_link_, storer); + store(administrator_user_id_, storer); + store(date_, storer); + if (has_expire_date) { + store(expire_date_, storer); + } + if (has_usage_limit) { + store(usage_limit_, storer); + } + if (has_usage_count) { + store(usage_count_, storer); + } + } + + template + void parse(ParserT &parser) { + using td::parse; + bool has_expire_date; + bool has_usage_limit; + bool has_usage_count; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(is_revoked_); + PARSE_FLAG(has_expire_date); + PARSE_FLAG(has_usage_limit); + PARSE_FLAG(has_usage_count); + END_PARSE_FLAGS(); + parse(invite_link_, parser); + parse(administrator_user_id_, parser); + parse(date_, parser); + if (has_expire_date) { + parse(expire_date_, parser); + } + if (has_usage_limit) { + parse(usage_limit_, parser); + } + if (has_usage_count) { + parse(usage_count_, parser); + } + } +}; + +bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs); + +bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs); + +StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link); + +} // namespace td From 3df10dc1038408f9acdddda9c9b31298510fc0d3 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 13 Jan 2021 16:50:41 +0300 Subject: [PATCH 035/232] Check invite link validness. --- td/telegram/ContactsManager.cpp | 9 +++++++++ td/telegram/DialogInviteLink.cpp | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 00165411e..36e27287e 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1572,6 +1572,12 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(ptr); DialogInviteLink invite_link(std::move(ptr)); + if (!invite_link.is_valid()) { + return on_error(id, Status::Error(500, "Receive invalid invite link")); + } + if (invite_link.get_administrator_user_id() != td->contacts_manager_->get_my_id()) { + return on_error(id, Status::Error(500, "Receive invalid invite link creator")); + } promise_.set_value(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); } @@ -1623,6 +1629,9 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { td->contacts_manager_->on_get_users(std::move(result->users_), "EditChatInviteLinkQuery"); DialogInviteLink invite_link(std::move(result->invite_)); + if (!invite_link.is_valid()) { + return on_error(id, Status::Error(500, "Receive invalid invite link")); + } promise_.set_value(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); } diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index e3441bd55..e1ea483cf 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -29,12 +29,24 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrflags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { expire_date_ = exported_invite->expire_date_; + if (expire_date_ < 0) { + LOG(ERROR) << "Receive wrong date " << expire_date_ << " as expire date of a link " << invite_link_; + expire_date_ = 0; + } } if ((exported_invite->flags_ & telegram_api::chatInviteExported::USAGE_LIMIT_MASK) != 0) { usage_limit_ = exported_invite->usage_limit_; + if (usage_limit_ < 0) { + LOG(ERROR) << "Receive wrong usage limit " << usage_limit_ << " for a link " << invite_link_; + usage_limit_ = 0; + } } if ((exported_invite->flags_ & telegram_api::chatInviteExported::USAGE_MASK) != 0) { usage_count_ = exported_invite->usage_; + if (usage_count_ < 0) { + LOG(ERROR) << "Receive wrong usage count " << usage_count_ << " for a link " << invite_link_; + usage_count_ = 0; + } } } From 5016197cf9529f8aa30621e8979ec1fd97869b44 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 13 Jan 2021 16:59:10 +0300 Subject: [PATCH 036/232] Load self when creating chat invite link. --- td/telegram/ContactsManager.cpp | 17 +++++++++++++++++ td/telegram/ContactsManager.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 36e27287e..f8f3b09d3 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -6571,6 +6571,23 @@ void ContactsManager::export_dialog_invite_link(DialogId dialog_id, int32 expire Promise> &&promise) { LOG(INFO) << "Receive CreateDialogInviteLink request for " << dialog_id; + get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, expire_date, usage_limit, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &ContactsManager::export_dialog_invite_link_impl, dialog_id, expire_date, usage_limit, + std::move(promise)); + } + })); +} + +void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 expire_date, int32 usage_limit, + Promise> &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); td_->create_handler(std::move(promise))->send(dialog_id, expire_date, usage_limit); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 5dea92fe0..3df7da40e 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -1345,6 +1345,9 @@ class ContactsManager : public Actor { static bool is_channel_public(const Channel *c); + void export_dialog_invite_link_impl(DialogId dialog_id, int32 expire_date, int32 usage_limit, + Promise> &&promise); + void remove_dialog_access_by_invite_link(DialogId dialog_id); Status can_manage_dialog_invite_links(DialogId dialog_id); From 1816a6c7a5db4f514db47063f80ffb98770a2a0f Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 13 Jan 2021 18:16:33 +0300 Subject: [PATCH 037/232] Support permanent invite links. --- td/generate/scheme/td_api.tl | 6 ++++-- td/generate/scheme/td_api.tlo | Bin 192608 -> 192688 bytes td/telegram/ContactsManager.cpp | 18 ++++++++++++------ td/telegram/ContactsManager.h | 4 ++-- td/telegram/DialogInviteLink.cpp | 13 +++++++++++-- td/telegram/DialogInviteLink.h | 3 +++ td/telegram/Td.cpp | 2 +- td/telegram/cli.cpp | 5 +++-- 8 files changed, 36 insertions(+), 15 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c524e1d11..496db060f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -887,8 +887,9 @@ chatsNearby users_nearby:vector supergroups_nearby:vectorXVt5%>WX+c}&QMbNWR; zChf@z6?>-N`M{_-`EG?bSZA|N<#wG)#+qX+EXrxs)7RKDNr1(t-DT97{>P8W1EgGK rk_`t~bi0cKQwkrLIbA@DNoVpN4~5AM_gSa^^<&D1s3`Df`mg{1sry5x delta 173 zcmdn+ko&;{?hQBCSzLVHUEO?#{ZGCuh{czjkyzrHSC(0l>XVt5%>WX+c}&QMV|w8s zM(xQ86?>)|v@^0!=B#uF>ue6G+#XWNSaVF_vuSpVNODnXVo9nqsu>JmQ>K4-#Hce} w#(~LY@&#iKu;%Sk9GFu0z|83dsZ2VP_jo8wp74Toy01S|K19Umessages_manager_->get_input_peer(dialog_id, AccessRights::Read); if (input_peer == nullptr) { @@ -1558,6 +1558,9 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { if (usage_limit > 0) { flags |= telegram_api::messages_exportChatInvite::USAGE_LIMIT_MASK; } + if (is_permanent) { + flags |= telegram_api::messages_exportChatInvite::LEGACY_REVOKE_PERMANENT_MASK; + } send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( flags, false /*ignored*/, std::move(input_peer), expire_date, usage_limit))); } @@ -1582,8 +1585,8 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ExportChatInviteLinkQuery"); promise_.set_error(std::move(status)); - td->updates_manager_->get_difference("ExportChatInviteLinkQuery"); } }; @@ -1636,8 +1639,8 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "EditChatInviteLinkQuery"); promise_.set_error(std::move(status)); - td->updates_manager_->get_difference("EditChatInviteLinkQuery"); } }; @@ -6568,21 +6571,23 @@ Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id) { } void ContactsManager::export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, + bool is_permanent, Promise> &&promise) { LOG(INFO) << "Receive CreateDialogInviteLink request for " << dialog_id; - get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, expire_date, usage_limit, + get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, expire_date, usage_limit, is_permanent, promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { promise.set_error(result.move_as_error()); } else { send_closure(actor_id, &ContactsManager::export_dialog_invite_link_impl, dialog_id, expire_date, usage_limit, - std::move(promise)); + is_permanent, std::move(promise)); } })); } void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 expire_date, int32 usage_limit, + bool is_permanent, Promise> &&promise) { if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); @@ -6590,7 +6595,8 @@ void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 e TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - td_->create_handler(std::move(promise))->send(dialog_id, expire_date, usage_limit); + td_->create_handler(std::move(promise)) + ->send(dialog_id, expire_date, usage_limit, is_permanent); } void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, int32 expire_date, diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 3df7da40e..3f7d57b85 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -389,7 +389,7 @@ class ContactsManager : public Actor { void transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, Promise &&promise); - void export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, + void export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent, Promise> &&promise); void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, @@ -1345,7 +1345,7 @@ class ContactsManager : public Actor { static bool is_channel_public(const Channel *c); - void export_dialog_invite_link_impl(DialogId dialog_id, int32 expire_date, int32 usage_limit, + void export_dialog_invite_link_impl(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent, Promise> &&promise); void remove_dialog_access_by_invite_link(DialogId dialog_id); diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index e1ea483cf..eace42662 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -48,6 +48,14 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrrevoked_; + is_permanent_ = exported_invite->permanent_; + + if (is_permanent_ && (usage_limit_ > 0 || expire_date_ > 0)) { + LOG(ERROR) << "Receive wron permanent " << *this; + expire_date_ = 0; + usage_limit_ = 0; + } } bool DialogInviteLink::is_expired() const { @@ -74,13 +82,14 @@ td_api::object_ptr DialogInviteLink::get_chat_invite_lin return td_api::make_object( invite_link_, contacts_manager->get_user_id_object(administrator_user_id_, "get_chat_invite_link_object"), date_, - expire_date_, usage_limit_, usage_count_, is_expired(), is_revoked_); + expire_date_, usage_limit_, usage_count_, is_permanent_, is_expired(), is_revoked_); } bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { return lhs.invite_link_ == rhs.invite_link_ && lhs.administrator_user_id_ == rhs.administrator_user_id_ && lhs.date_ == rhs.date_ && lhs.expire_date_ == rhs.expire_date_ && lhs.usage_limit_ == rhs.usage_limit_ && - lhs.usage_count_ == rhs.usage_count_ && lhs.is_revoked_ == rhs.is_revoked_; + lhs.usage_count_ == rhs.usage_count_ && lhs.is_permanent_ == rhs.is_permanent_ && + lhs.is_revoked_ == rhs.is_revoked_; } bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index ffa96d842..b87d713dc 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -26,6 +26,7 @@ class DialogInviteLink { int32 usage_limit_ = 0; int32 usage_count_ = 0; bool is_revoked_ = false; + bool is_permanent_ = false; friend bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs); @@ -58,6 +59,7 @@ class DialogInviteLink { bool has_usage_count = usage_count_ != 0; BEGIN_STORE_FLAGS(); STORE_FLAG(is_revoked_); + STORE_FLAG(is_permanent_); STORE_FLAG(has_expire_date); STORE_FLAG(has_usage_limit); STORE_FLAG(has_usage_count); @@ -84,6 +86,7 @@ class DialogInviteLink { bool has_usage_count; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_revoked_); + PARSE_FLAG(is_permanent_); PARSE_FLAG(has_expire_date); PARSE_FLAG(has_usage_limit); PARSE_FLAG(has_usage_count); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index d89a5ba12..ca5bf27d2 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6278,7 +6278,7 @@ void Td::on_request(uint64 id, td_api::getChatAdministrators &request) { void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.usage_limit_, - std::move(promise)); + request.is_permanent_, std::move(promise)); } void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index c8309dbb4..bbba8178f 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2682,12 +2682,13 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_group_call_id(args))); } else if (op == "dgc") { send_request(td_api::make_object(as_group_call_id(args))); - } else if (op == "gcil") { + } else if (op == "ccilp" || op == "ccilt") { string chat_id; int32 expire_date; int32 usage_limit; get_args(args, chat_id, expire_date, usage_limit); - send_request(td_api::make_object(as_chat_id(chat_id), expire_date, usage_limit)); + send_request(td_api::make_object(as_chat_id(chat_id), expire_date, usage_limit, + op == "ccilp")); } else if (op == "ecil") { string chat_id; string invite_link; From 4761d7385c654bb8fd8063e9ae5fe75d2e902897 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 13 Jan 2021 18:30:58 +0300 Subject: [PATCH 038/232] Move invite link methods to DialogInviteLink. --- td/telegram/ContactsManager.cpp | 41 +++++--------------------------- td/telegram/ContactsManager.h | 6 ----- td/telegram/DialogInviteLink.cpp | 31 ++++++++++++++++++++++++ td/telegram/DialogInviteLink.h | 7 ++++++ 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index cd9e839b5..4eb4993c5 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1655,7 +1655,7 @@ class CheckDialogInviteLinkQuery : public Td::ResultHandler { void send(const string &invite_link) { invite_link_ = invite_link; send_query(G()->net_query_creator().create( - telegram_api::messages_checkChatInvite(ContactsManager::get_dialog_invite_link_hash(invite_link_).str()))); + telegram_api::messages_checkChatInvite(DialogInviteLink::get_dialog_invite_link_hash(invite_link_).str()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1687,7 +1687,7 @@ class ImportDialogInviteLinkQuery : public Td::ResultHandler { void send(const string &invite_link) { invite_link_ = invite_link; send_query(G()->net_query_creator().create( - telegram_api::messages_importChatInvite(ContactsManager::get_dialog_invite_link_hash(invite_link).str()))); + telegram_api::messages_importChatInvite(DialogInviteLink::get_dialog_invite_link_hash(invite_link).str()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2906,9 +2906,6 @@ class ContactsManager::UploadProfilePhotoCallback : public FileManager::UploadCa } }; -const CSlice ContactsManager::INVITE_LINK_URLS[3] = {"t.me/joinchat/", "telegram.me/joinchat/", - "telegram.dog/joinchat/"}; - ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { upload_profile_photo_callback_ = std::make_shared(); @@ -6606,7 +6603,7 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - // if (!is_valid_invite_link(invite_link)) { + // if (!DialogInviteLink::is_valid_invite_link(invite_link)) { // return promise.set_error(Status::Error(400, "Wrong invite link")); // } @@ -6619,7 +6616,7 @@ void ContactsManager::check_dialog_invite_link(const string &invite_link, Promis return promise.set_value(Unit()); } - if (!is_valid_invite_link(invite_link)) { + if (!DialogInviteLink::is_valid_invite_link(invite_link)) { return promise.set_error(Status::Error(400, "Wrong invite link")); } @@ -6627,7 +6624,7 @@ void ContactsManager::check_dialog_invite_link(const string &invite_link, Promis } void ContactsManager::import_dialog_invite_link(const string &invite_link, Promise &&promise) { - if (!is_valid_invite_link(invite_link)) { + if (!DialogInviteLink::is_valid_invite_link(invite_link)) { return promise.set_error(Status::Error(400, "Wrong invite link")); } @@ -11485,32 +11482,6 @@ void ContactsManager::remove_dialog_access_by_invite_link(DialogId dialog_id) { invite_link_info_expire_timeout_.cancel_timeout(dialog_id.get()); } -bool ContactsManager::is_valid_invite_link(const string &invite_link) { - return !get_dialog_invite_link_hash(invite_link).empty(); -} - -Slice ContactsManager::get_dialog_invite_link_hash(const string &invite_link) { - auto lower_cased_invite_link_str = to_lower(invite_link); - Slice lower_cased_invite_link = lower_cased_invite_link_str; - size_t offset = 0; - if (begins_with(lower_cased_invite_link, "https://")) { - offset = 8; - } else if (begins_with(lower_cased_invite_link, "http://")) { - offset = 7; - } - lower_cased_invite_link.remove_prefix(offset); - - for (auto &url : INVITE_LINK_URLS) { - if (begins_with(lower_cased_invite_link, url)) { - Slice hash = Slice(invite_link).substr(url.size() + offset); - hash.truncate(hash.find('#')); - hash.truncate(hash.find('?')); - return hash; - } - } - return Slice(); -} - bool ContactsManager::update_invite_link(string &invite_link, tl_object_ptr &&exported_chat_invite) { string new_invite_link; @@ -11522,7 +11493,7 @@ bool ContactsManager::update_invite_link(string &invite_link, if (!invite_link.empty()) { invite_link_infos_.erase(invite_link); } - LOG_IF(ERROR, !new_invite_link.empty() && !is_valid_invite_link(new_invite_link)) + LOG_IF(ERROR, !new_invite_link.empty() && !DialogInviteLink::is_valid_invite_link(new_invite_link)) << "Unsupported invite link " << new_invite_link; invite_link = std::move(new_invite_link); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 3f7d57b85..e350966c4 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -221,8 +221,6 @@ class ContactsManager : public Actor { bool on_get_channel_error(ChannelId channel_id, const Status &status, const string &source); - static Slice get_dialog_invite_link_hash(const string &invite_link); - void on_get_dialog_invite_link_info(const string &invite_link, tl_object_ptr &&chat_invite_ptr, Promise &&promise); @@ -1044,8 +1042,6 @@ class ContactsManager : public Actor { static constexpr int32 ACCOUNT_UPDATE_LAST_NAME = 1 << 1; static constexpr int32 ACCOUNT_UPDATE_ABOUT = 1 << 2; - static const CSlice INVITE_LINK_URLS[3]; - static bool have_input_peer_user(const User *u, AccessRights access_rights); static bool have_input_peer_chat(const Chat *c, AccessRights access_rights); bool have_input_peer_channel(const Channel *c, ChannelId channel_id, AccessRights access_rights, @@ -1352,8 +1348,6 @@ class ContactsManager : public Actor { Status can_manage_dialog_invite_links(DialogId dialog_id); - static bool is_valid_invite_link(const string &invite_link); - bool update_invite_link(string &invite_link, tl_object_ptr &&exported_chat_invite); const DialogParticipant *get_chat_participant(ChatId chat_id, UserId user_id) const; diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index eace42662..51e5a3dec 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -9,8 +9,13 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" +#include "td/utils/misc.h" + namespace td { +const CSlice DialogInviteLink::INVITE_LINK_URLS[3] = {"t.me/joinchat/", "telegram.me/joinchat/", + "telegram.dog/joinchat/"}; + DialogInviteLink::DialogInviteLink(tl_object_ptr exported_invite) { if (exported_invite == nullptr) { return; @@ -58,6 +63,32 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrunix_time() >= expire_date_) || (usage_limit_ != 0 && usage_count_ >= usage_limit_); } diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index b87d713dc..5440598d6 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -11,6 +11,7 @@ #include "td/telegram/UserId.h" #include "td/utils/common.h" +#include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include "td/utils/tl_helpers.h" @@ -32,11 +33,17 @@ class DialogInviteLink { friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link); + static const CSlice INVITE_LINK_URLS[3]; + public: DialogInviteLink() = default; DialogInviteLink(tl_object_ptr exported_invite); + static bool is_valid_invite_link(Slice invite_link); + + static Slice get_dialog_invite_link_hash(Slice invite_link); + td_api::object_ptr get_chat_invite_link_object(const ContactsManager *contacts_manager) const; bool is_valid() const { From 9f3735495164e8ccf0a7e429c28d18b694acc2aa Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 13 Jan 2021 19:14:08 +0300 Subject: [PATCH 039/232] Return full invite links in *FullInfo. --- td/generate/scheme/td_api.tl | 48 ++++++++--------- td/generate/scheme/td_api.tlo | Bin 192688 -> 192688 bytes td/telegram/ContactsManager.cpp | 89 ++++++++++++++++++------------- td/telegram/ContactsManager.h | 8 +-- td/telegram/DialogInviteLink.cpp | 1 + td/telegram/DialogInviteLink.h | 4 ++ 6 files changed, 87 insertions(+), 63 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 496db060f..08ef09e9a 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -545,6 +545,25 @@ supergroupMembersFilterMention query:string message_thread_id:int53 = Supergroup supergroupMembersFilterBots = SupergroupMembersFilter; +//@description Contains a chat invite link @invite_link Chat invite link @administrator_user_id User identifier of an administrator created the link +//@date Point in time (Unix timestamp) when the link was created @expire_date Point in time (Unix timestamp) when the link will expire; 0 if never +//@usage_limit Maximum number of times the link can be used; 0 if not limited @usage_count Number of times the link has already been used +//@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator at any time +//@is_expired True, if the link is already expired @is_revoked True, if the link was revoked +chatInviteLink invite_link:string administrator_user_id:int32 date:int32 expire_date:int32 usage_limit:int32 usage_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; + +//@description Contains information about a chat invite link +//@chat_id Chat identifier of the invite link; 0 if the user has no access to the chat before joining +//@accessible_for If non-zero, the amount of time for which read access to the chat will remain available, in seconds +//@type Contains information about the type of the chat +//@title Title of the chat +//@photo Chat photo; may be null +//@member_count Number of members in the chat +//@member_user_ids User identifiers of some chat members that may be known to the current user +//@is_public True, if the chat is a public supergroup or channel, i.e. it has a username or it is a location-based supergroup +chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string photo:chatPhotoInfo member_count:int32 member_user_ids:vector is_public:Bool = ChatInviteLinkInfo; + + //@description Represents a basic group of 0-200 users (must be upgraded to a supergroup to accommodate more than 200 users) //@id Group identifier //@member_count Number of members in the group @@ -558,8 +577,8 @@ basicGroup id:int32 member_count:int32 status:ChatMemberStatus is_active:Bool up //@param_description Group description //@creator_user_id User identifier of the creator of the group; 0 if unknown //@members Group members -//@invite_link Invite link for this group; available only after it has been generated at least once and only for the group creator -basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 members:vector invite_link:string = BasicGroupFullInfo; +//@invite_link Permanent invite link for this group; only for the group creator +basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 members:vector invite_link:chatInviteLink = BasicGroupFullInfo; //@description Represents a supergroup or channel with zero or more members (subscribers in the case of channels). From the point of view of the system, a channel is a special kind of a supergroup: only administrators can post and see the list of members, and posts from all administrators use the name and photo of the channel instead of individual names and profile photos. Unlike supergroups, channels can have an unlimited number of subscribers @@ -596,10 +615,10 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co //@is_all_history_available True, if new chat members will have access to old messages. In public or discussion groups and both public and private channels, old messages are always available, so this option affects only private supergroups without a linked chat. The value of this field is only available for chat administrators //@sticker_set_id Identifier of the supergroup sticker set; 0 if none //@location Location to which the supergroup is connected; may be null -//@invite_link Invite link for this chat +//@invite_link Permanent invite link for this chat; for chat administrators only //@upgraded_from_basic_group_id Identifier of the basic group from which supergroup was upgraded; 0 if none //@upgraded_from_max_message_id Identifier of the last message in the basic group from which supergroup was upgraded; 0 if none -supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_get_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:string upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo; +supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_get_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:chatInviteLink upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo; //@class SecretChatState @description Describes the current secret chat state @@ -884,25 +903,6 @@ chatNearby chat_id:int53 distance:int32 = ChatNearby; chatsNearby users_nearby:vector supergroups_nearby:vector = ChatsNearby; -//@description Contains a chat invite link @invite_link Chat invite link @administrator_user_id User identifier of an administrator created the link -//@date Point in time (Unix timestamp) when the link was created @expire_date Point in time (Unix timestamp) when the link will expire; 0 if never -//@usage_limit Maximum number of times the link can be used; 0 if not limited @usage_count Number of times the link has already been used -//@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator at any time -//@is_expired True, if the link is already expired @is_revoked True, if the link was revoked -chatInviteLink invite_link:string administrator_user_id:int32 date:int32 expire_date:int32 usage_limit:int32 usage_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; - -//@description Contains information about a chat invite link -//@chat_id Chat identifier of the invite link; 0 if the user has no access to the chat before joining -//@accessible_for If non-zero, the amount of time for which read access to the chat will remain available, in seconds -//@type Contains information about the type of the chat -//@title Title of the chat -//@photo Chat photo; may be null -//@member_count Number of members in the chat -//@member_user_ids User identifiers of some chat members that may be known to the current user -//@is_public True, if the chat is a public supergroup or channel, i.e. it has a username or it is a location-based supergroup -chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string photo:chatPhotoInfo member_count:int32 member_user_ids:vector is_public:Bool = ChatInviteLinkInfo; - - //@class PublicChatType @description Describes a type of public chats //@description The chat is public, because it has username @@ -4354,7 +4354,7 @@ deleteFile file_id:int32 = Ok; //@chat_id Chat identifier //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@usage_limit Maximum number of times the link can be used; pass 0 if not limited -//@is_permanent True, if new permanent chat link needs to be created instead of the previous +//@is_permanent True, if new permanent chat link needs to be created instead of the previous one createChatInviteLink chat_id:int53 expire_date:int32 usage_limit:int32 is_permanent:Bool = ChatInviteLink; //@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 4fcafafa08cac95e683520e684f517b715b9bd71..68ae908dd4f8750c2a34c27ff1195733fbbc0e2b 100644 GIT binary patch delta 237 zcmdn+kb46VMKH7cxnDJRa}4tomWfA1HnVUPsIZitzqD#|g-(sM3`kBWDX};+*}W*g zw7{)2C&x1{EuR4-K3TxyfJFEU?k|iC5K!dIro&;|b`T`l{K8}V3lGKxWr`rfgo;ZG zQj5}2i~xz>JSOBLGX0|xqxkj%`x(zLv4D)5KH(5!1K17QMGiA^7=ax)UEwR^46xwz qg4>J=+wXj3Jdp*KnQSn_0?gR{B!TILE=0`(FD46!5j_1&OA-MXA8Qi; delta 225 zcmdn+kb46VMKH5WWXk@rIfi)(%VZHIk void ContactsManager::ChatFull::store(StorerT &storer) const { using td::store; bool has_description = !description.empty(); - bool has_invite_link = !invite_link.empty(); + bool has_legacy_invite_link = false; bool has_photo = !photo.is_empty(); + bool has_invite_link = invite_link.is_valid(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_description); - STORE_FLAG(has_invite_link); + STORE_FLAG(has_legacy_invite_link); STORE_FLAG(can_set_username); STORE_FLAG(has_photo); + STORE_FLAG(has_invite_link); END_STORE_FLAGS(); store(version, storer); store(creator_user_id, storer); @@ -3505,25 +3507,27 @@ void ContactsManager::ChatFull::store(StorerT &storer) const { if (has_description) { store(description, storer); } - if (has_invite_link) { - store(invite_link, storer); - } if (has_photo) { store(photo, storer); } + if (has_invite_link) { + store(invite_link, storer); + } } template void ContactsManager::ChatFull::parse(ParserT &parser) { using td::parse; bool has_description; - bool has_invite_link; + bool legacy_has_invite_link; bool has_photo; + bool has_invite_link; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_description); - PARSE_FLAG(has_invite_link); + PARSE_FLAG(legacy_has_invite_link); PARSE_FLAG(can_set_username); PARSE_FLAG(has_photo); + PARSE_FLAG(has_invite_link); END_PARSE_FLAGS(); parse(version, parser); parse(creator_user_id, parser); @@ -3531,12 +3535,16 @@ void ContactsManager::ChatFull::parse(ParserT &parser) { if (has_description) { parse(description, parser); } - if (has_invite_link) { - parse(invite_link, parser); + if (legacy_has_invite_link) { + string legacy_invite_link; + parse(legacy_invite_link, parser); } if (has_photo) { parse(photo, parser); } + if (has_invite_link) { + parse(invite_link, parser); + } } template @@ -3699,7 +3707,7 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { bool has_administrator_count = administrator_count != 0; bool has_restricted_count = restricted_count != 0; bool has_banned_count = banned_count != 0; - bool has_invite_link = !invite_link.empty(); + bool legacy_has_invite_link = false; bool has_sticker_set = sticker_set_id.is_valid(); bool has_linked_channel_id = linked_channel_id.is_valid(); bool has_migrated_from_max_message_id = migrated_from_max_message_id.is_valid(); @@ -3711,12 +3719,13 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { bool has_stats_dc_id = stats_dc_id.is_exact(); bool has_photo = !photo.is_empty(); bool legacy_has_active_group_call_id = false; + bool has_invite_link = invite_link.is_valid(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_description); STORE_FLAG(has_administrator_count); STORE_FLAG(has_restricted_count); STORE_FLAG(has_banned_count); - STORE_FLAG(has_invite_link); + STORE_FLAG(legacy_has_invite_link); STORE_FLAG(has_sticker_set); STORE_FLAG(has_linked_channel_id); STORE_FLAG(has_migrated_from_max_message_id); @@ -3736,6 +3745,7 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { STORE_FLAG(is_can_view_statistics_inited); STORE_FLAG(can_view_statistics); STORE_FLAG(legacy_has_active_group_call_id); + STORE_FLAG(has_invite_link); END_STORE_FLAGS(); if (has_description) { store(description, storer); @@ -3750,9 +3760,6 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { if (has_banned_count) { store(banned_count, storer); } - if (has_invite_link) { - store(invite_link, storer); - } if (has_sticker_set) { store(sticker_set_id, storer); } @@ -3784,6 +3791,9 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { if (has_photo) { store(photo, storer); } + if (has_invite_link) { + store(invite_link, storer); + } } template @@ -3793,7 +3803,7 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { bool has_administrator_count; bool has_restricted_count; bool has_banned_count; - bool has_invite_link; + bool legacy_has_invite_link; bool has_sticker_set; bool has_linked_channel_id; bool has_migrated_from_max_message_id; @@ -3806,12 +3816,13 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { bool has_stats_dc_id; bool has_photo; bool legacy_has_active_group_call_id; + bool has_invite_link; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_description); PARSE_FLAG(has_administrator_count); PARSE_FLAG(has_restricted_count); PARSE_FLAG(has_banned_count); - PARSE_FLAG(has_invite_link); + PARSE_FLAG(legacy_has_invite_link); PARSE_FLAG(has_sticker_set); PARSE_FLAG(has_linked_channel_id); PARSE_FLAG(has_migrated_from_max_message_id); @@ -3831,6 +3842,7 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { PARSE_FLAG(is_can_view_statistics_inited); PARSE_FLAG(can_view_statistics); PARSE_FLAG(legacy_has_active_group_call_id); + PARSE_FLAG(has_invite_link); END_PARSE_FLAGS(); if (has_description) { parse(description, parser); @@ -3845,8 +3857,9 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { if (has_banned_count) { parse(banned_count, parser); } - if (has_invite_link) { - parse(invite_link, parser); + if (legacy_has_invite_link) { + string legacy_invite_link; + parse(legacy_invite_link, parser); } if (has_sticker_set) { parse(sticker_set_id, parser); @@ -3883,6 +3896,9 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { InputGroupCallId input_group_call_id; parse(input_group_call_id, parser); } + if (has_invite_link) { + parse(invite_link, parser); + } if (legacy_can_view_statistics) { LOG(DEBUG) << "Ignore legacy can view statistics flag"; @@ -11166,7 +11182,7 @@ void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, Ch void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full, tl_object_ptr &&invite_link) { CHECK(chat_full != nullptr); - if (update_invite_link(chat_full->invite_link, std::move(invite_link))) { + if (update_persistent_invite_link(chat_full->invite_link, std::move(invite_link))) { chat_full->is_changed = true; } } @@ -11174,7 +11190,7 @@ void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full, void ContactsManager::on_update_channel_full_invite_link( ChannelFull *channel_full, tl_object_ptr &&invite_link) { CHECK(channel_full != nullptr); - if (update_invite_link(channel_full->invite_link, std::move(invite_link))) { + if (update_persistent_invite_link(channel_full->invite_link, std::move(invite_link))) { channel_full->is_changed = true; } } @@ -11482,19 +11498,14 @@ void ContactsManager::remove_dialog_access_by_invite_link(DialogId dialog_id) { invite_link_info_expire_timeout_.cancel_timeout(dialog_id.get()); } -bool ContactsManager::update_invite_link(string &invite_link, - tl_object_ptr &&exported_chat_invite) { - string new_invite_link; - if (exported_chat_invite != nullptr) { - new_invite_link = std::move(exported_chat_invite->link_); - } - +bool ContactsManager::update_persistent_invite_link( + DialogInviteLink &invite_link, tl_object_ptr &&exported_chat_invite) { + DialogInviteLink new_invite_link(std::move(exported_chat_invite)); if (new_invite_link != invite_link) { - if (!invite_link.empty()) { - invite_link_infos_.erase(invite_link); + if (invite_link.is_valid() && invite_link.get_invite_link() != new_invite_link.get_invite_link()) { + // old link was invalidated + invite_link_infos_.erase(invite_link.get_invite_link()); } - LOG_IF(ERROR, !new_invite_link.empty() && !DialogInviteLink::is_valid_invite_link(new_invite_link)) - << "Unsupported invite link " << new_invite_link; invite_link = std::move(new_invite_link); return true; @@ -12002,7 +12013,7 @@ void ContactsManager::drop_chat_full(ChatId chat_id) { // chat_full->creator_user_id = UserId(); chat_full->participants.clear(); chat_full->version = -1; - update_invite_link(chat_full->invite_link, nullptr); + on_update_chat_full_invite_link(chat_full, nullptr); update_chat_online_member_count(chat_full, chat_id, true); chat_full->is_changed = true; update_chat_full(chat_full, chat_id); @@ -12850,12 +12861,18 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha for (const auto &participant : chat_full->participants) { auto u = get_user(participant.user_id); if (u != nullptr && is_bot_info_expired(participant.user_id, u->bot_info_version)) { - LOG(INFO) << "Have outdated botInfo for " << participant.user_id << ", expected version " << u->bot_info_version; + LOG(INFO) << "Have outdated botInfo for " << participant.user_id << " in " << chat_id << "; expected version " + << u->bot_info_version; return true; } } - LOG(INFO) << "Full " << chat_id << " is up-to-date with version " << chat_full->version; + if (c->status.is_creator() && !chat_full->invite_link.is_valid()) { + LOG(INFO) << "Have outdated invite link in " << chat_id; + return true; + } + + LOG(DEBUG) << "Full " << chat_id << " is up-to-date with version " << chat_full->version; return false; } @@ -14345,7 +14362,7 @@ tl_object_ptr ContactsManager::get_basic_group_full_ get_user_id_object(chat_full->creator_user_id, "basicGroupFullInfo"), transform(chat_full->participants, [this](const DialogParticipant &chat_participant) { return get_chat_member_object(chat_participant); }), - chat_full->invite_link); + chat_full->invite_link.get_chat_invite_link_object(this)); } td_api::object_ptr ContactsManager::get_update_unknown_supergroup_object( @@ -14396,7 +14413,7 @@ tl_object_ptr ContactsManager::get_supergroup_full_i slow_mode_delay_expires_in, channel_full->can_get_participants, channel_full->can_set_username, channel_full->can_set_sticker_set, channel_full->can_set_location, channel_full->can_view_statistics, channel_full->is_all_history_available, channel_full->sticker_set_id.get(), - channel_full->location.get_chat_location_object(), channel_full->invite_link, + channel_full->location.get_chat_location_object(), channel_full->invite_link.get_chat_invite_link_object(this), get_basic_group_id_object(channel_full->migrated_from_chat_id, "get_supergroup_full_info_object"), channel_full->migrated_from_max_message_id.get()); } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index e350966c4..25a4601a6 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -739,7 +739,7 @@ class ContactsManager : public Actor { string description; - string invite_link; + DialogInviteLink invite_link; bool can_set_username = false; @@ -815,7 +815,8 @@ class ContactsManager : public Actor { int32 administrator_count = 0; int32 restricted_count = 0; int32 banned_count = 0; - string invite_link; + + DialogInviteLink invite_link; uint32 speculative_version = 1; uint32 repair_request_version = 0; @@ -1348,7 +1349,8 @@ class ContactsManager : public Actor { Status can_manage_dialog_invite_links(DialogId dialog_id); - bool update_invite_link(string &invite_link, tl_object_ptr &&exported_chat_invite); + bool update_persistent_invite_link(DialogInviteLink &invite_link, + tl_object_ptr &&exported_chat_invite); const DialogParticipant *get_chat_participant(ChatId chat_id, UserId user_id) const; diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index 51e5a3dec..1512dcc2e 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -22,6 +22,7 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrlink_); + LOG_IF(ERROR, !is_valid_invite_link(invite_link_)) << "Unsupported invite link " << invite_link_; administrator_user_id_ = UserId(exported_invite->admin_id_); if (!administrator_user_id_.is_valid()) { LOG(ERROR) << "Receive invalid " << administrator_user_id_ << " as creator of a link " << invite_link_; diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index 5440598d6..1cd787eb1 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -54,6 +54,10 @@ class DialogInviteLink { int32 get_expire_time() const; + const string &get_invite_link() const { + return invite_link_; + } + UserId get_administrator_user_id() const { return administrator_user_id_; } From efc12342c204b2bf41ae467d96e870a0ce3d8bd5 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 14 Jan 2021 19:39:51 +0300 Subject: [PATCH 040/232] Add getChatInviteLinks method. --- td/generate/scheme/td_api.tl | 7 +++ td/generate/scheme/td_api.tlo | Bin 192688 -> 193100 bytes td/telegram/ContactsManager.cpp | 85 ++++++++++++++++++++++++++++++-- td/telegram/ContactsManager.h | 3 ++ td/telegram/Td.cpp | 7 +++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 10 +++- 7 files changed, 109 insertions(+), 5 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 08ef09e9a..dc226d9d1 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -552,6 +552,9 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@is_expired True, if the link is already expired @is_revoked True, if the link was revoked chatInviteLink invite_link:string administrator_user_id:int32 date:int32 expire_date:int32 usage_limit:int32 usage_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; +//@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links +chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; + //@description Contains information about a chat invite link //@chat_id Chat identifier of the invite link; 0 if the user has no access to the chat before joining //@accessible_for If non-zero, the amount of time for which read access to the chat will remain available, in seconds @@ -4365,6 +4368,10 @@ createChatInviteLink chat_id:int53 expire_date:int32 usage_limit:int32 is_perman //@is_revoked True, if the link is revoked editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 usage_limit:int32 is_revoked:Bool = ChatInviteLink; +//@description Returns exported invite links for a chat @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned +//@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning @limit Maximum number of invite links to return +getChatInviteLinks chat_id:int53 administrator_user_id:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; + //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 68ae908dd4f8750c2a34c27ff1195733fbbc0e2b..ee686f866cc28f53dfe7db578ab57be8cabc38af 100644 GIT binary patch delta 210 zcmdn+ko(LNZr(?;^{p77Kw~4XI)||Gv|Vic&KZd%o_S@NC8<7{dD)ZsnIt!xa71V` zCT;FDws)5Rt4T&tQw$QDY|v!D!;=Zt7@q^wIVnJ4@&ykO7La7~fvW8Xsu*ilGfv)a ztH$)-UdG$=VUSRIY6+@s#S9?T(-(YT)S0YMVIVA&pO#jfS`v?B>~wn#Mycs%W0{n} MCT}-LV`}IE0BRgcAOHXW delta 59 zcmX@}gnPq7Zr(?;^{p77Kz$>xI>+V&jtFhW#LYX6?cJLvRBxY9%~-ISang21b*BIJ L+i#>Zm2?3BLb4Q? diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 6f892d834..3a3f34d7b 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1644,6 +1644,70 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { } }; +class GetExportedChatInvitesQuery : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetExportedChatInvitesQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, UserId administrator_user_id, const string &offset_invite_link, int32 limit) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + auto input_user = td->contacts_manager_->get_input_user(administrator_user_id); + + int32 flags = 0; + if (input_user != nullptr) { + flags |= telegram_api::messages_getExportedChatInvites::ADMIN_ID_MASK; + } + if (!offset_invite_link.empty()) { + flags |= telegram_api::messages_getExportedChatInvites::OFFSET_LINK_MASK; + } + send_query(G()->net_query_creator().create(telegram_api::messages_getExportedChatInvites( + flags, std::move(input_peer), std::move(input_user), offset_invite_link, limit))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetExportedChatInvitesQuery: " << to_string(result); + + td->contacts_manager_->on_get_users(std::move(result->users_), "GetExportedChatInvitesQuery"); + + int32 total_count = result->count_; + if (total_count < static_cast(result->invites_.size())) { + LOG(ERROR) << "Receive wrong total count of invite links " << total_count << " in " << dialog_id_; + total_count = static_cast(result->invites_.size()); + } + vector> invite_links; + for (auto &invite : result->invites_) { + DialogInviteLink invite_link(std::move(invite)); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Receive invalid invite link in " << dialog_id_; + total_count--; + continue; + } + invite_links.push_back(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); + } + promise_.set_value(td_api::make_object(total_count, std::move(invite_links))); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetExportedChatInvitesQuery"); + promise_.set_error(std::move(status)); + } +}; + class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -6586,8 +6650,6 @@ Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id) { void ContactsManager::export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent, Promise> &&promise) { - LOG(INFO) << "Receive CreateDialogInviteLink request for " << dialog_id; - get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, expire_date, usage_limit, is_permanent, promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { @@ -6615,8 +6677,6 @@ void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 e void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise) { - LOG(INFO) << "Receive EditDialogInviteLink request for link " << invite_link << " in " << dialog_id; - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); // if (!DialogInviteLink::is_valid_invite_link(invite_link)) { @@ -6627,6 +6687,23 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & ->send(dialog_id, invite_link, expire_date, usage_limit, is_revoked); } +void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, + const string &offset_invite_link, int32 limit, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (administrator_user_id != UserId() && !have_input_user(administrator_user_id)) { + return promise.set_error(Status::Error(400, "Administrator user not found")); + } + + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, administrator_user_id, offset_invite_link, limit); +} + void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { if (invite_link_infos_.count(invite_link) > 0) { return promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 25a4601a6..af9323224 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -393,6 +393,9 @@ class ContactsManager : public Actor { void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise); + void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, const string &offset_invite_link, + int32 limit, Promise> &&promise); + void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index ca5bf27d2..d280ba6d4 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6288,6 +6288,13 @@ void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { request.usage_limit_, request.is_revoked_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { + CREATE_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.offset_invite_link_); + contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.administrator_user_id_), + request.offset_invite_link_, request.limit_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index c0e782340..775bcb868 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -790,6 +790,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::editChatInviteLink &request); + void on_request(uint64 id, td_api::getChatInviteLinks &request); + void on_request(uint64 id, td_api::checkChatInviteLink &request); void on_request(uint64 id, td_api::joinChatByInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index bbba8178f..b413a4f6f 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2698,6 +2698,14 @@ class CliClient final : public Actor { get_args(args, chat_id, invite_link, expire_date, usage_limit, is_revoked); send_request(td_api::make_object(as_chat_id(chat_id), invite_link, expire_date, usage_limit, is_revoked)); + } else if (op == "gcil") { + string chat_id; + string administrator_user_id; + string offset_invite_link; + string limit; + get_args(args, chat_id, administrator_user_id, offset_invite_link, limit); + send_request(td_api::make_object( + as_chat_id(chat_id), as_user_id(administrator_user_id), offset_invite_link, as_limit(limit))); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { @@ -3691,7 +3699,7 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "grib") { send_request(td_api::make_object()); - } else if (op == "spc" || op == "su" || op == "sch") { + } else if (op == "spc" || op == "su") { send_request(td_api::make_object(args)); } else if (op == "spcs") { send_request(td_api::make_object(args)); From d5c0028e1e29cf92112a6045e607825738cdf558 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 14 Jan 2021 23:21:23 +0300 Subject: [PATCH 041/232] Add getChatInviteLinkUsers. --- td/generate/scheme/td_api.tl | 12 ++++- td/generate/scheme/td_api.tlo | Bin 193100 -> 193680 bytes td/telegram/ContactsManager.cpp | 82 ++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 4 ++ td/telegram/Td.cpp | 7 +++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 10 ++++ 7 files changed, 116 insertions(+), 1 deletion(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index dc226d9d1..1346e9e4b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -566,6 +566,12 @@ chatInviteLinks total_count:int32 invite_links:vector = ChatInvi //@is_public True, if the chat is a public supergroup or channel, i.e. it has a username or it is a location-based supergroup chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string photo:chatPhotoInfo member_count:int32 member_user_ids:vector is_public:Bool = ChatInviteLinkInfo; +//@description Describes a user joined a chat by an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat +chatInviteLinkUser user_id:int32 joined_chat_date:int32 = ChatInviteLinkUser; + +//@description Contains a list of users joined a chat by an invite link @total_count Approximate total count of users found @users List of users, joined a chat by an invite link +chatInviteLinkUsers total_count:int32 users:vector = ChatInviteLinkUsers; + //@description Represents a basic group of 0-200 users (must be upgraded to a supergroup to accommodate more than 200 users) //@id Group identifier @@ -4368,10 +4374,14 @@ createChatInviteLink chat_id:int53 expire_date:int32 usage_limit:int32 is_perman //@is_revoked True, if the link is revoked editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 usage_limit:int32 is_revoked:Bool = ChatInviteLink; -//@description Returns exported invite links for a chat @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned +//@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned //@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning @limit Maximum number of invite links to return getChatInviteLinks chat_id:int53 administrator_user_id:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; +//@description Returns users joined a chat by an invite link. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @invite_link Invite link for which to return users +//@offset_user A user from which to return next users; use null to get results from the beginning @limit Maximum number of users to return +getChatInviteLinkUsers chat_id:int53 invite_link:string offset_user:chatInviteLinkUser limit:int32 = ChatInviteLinkUsers; + //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index ee686f866cc28f53dfe7db578ab57be8cabc38af..45a58fda18920d4faf534d62b0c8a8a859bda9fd 100644 GIT binary patch delta 309 zcmX@}gnPnEZr(?;^{p77Kx-qfI)~`Wr|)froHG(jJoCyjOHzF@^Rh#WCr@OT7VS0I zwNe;E#*tHcvk6CwHe<@>oyPX=GGP75X!=u&7(n8aCrs0syrEWOk~N18h!3;*svK7g z*mO*N#USy?0@Vhrtfj@NMUx$)+WI94*?6Nt-22?A@D7YPOfuFmkM6oV>kOgXzEh Kc9%>hpKbs!CKGD_ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 3a3f34d7b..bcb37513a 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1708,6 +1708,67 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { } }; +class GetChatInviteImportersQuery : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetChatInviteImportersQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link, int32 offset_date, UserId offset_user_id, int32 limit) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + auto input_user = td->contacts_manager_->get_input_user(offset_user_id); + if (input_user == nullptr) { + input_user = make_tl_object(); + } + + send_query(G()->net_query_creator().create(telegram_api::messages_getChatInviteImporters( + std::move(input_peer), invite_link, offset_date, std::move(input_user), limit))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetChatInviteImportersQuery: " << to_string(result); + + td->contacts_manager_->on_get_users(std::move(result->users_), "GetChatInviteImportersQuery"); + + int32 total_count = result->count_; + if (total_count < static_cast(result->importers_.size())) { + LOG(ERROR) << "Receive wrong total count of invite link users " << total_count << " in " << dialog_id_; + total_count = static_cast(result->importers_.size()); + } + vector> invite_link_users; + for (auto &importer : result->importers_) { + UserId user_id(importer->user_id_); + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive invalid invite link " << user_id << " in " << dialog_id_; + total_count--; + continue; + } + invite_link_users.push_back(td_api::make_object( + td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkUser"), importer->date_)); + } + promise_.set_value(td_api::make_object(total_count, std::move(invite_link_users))); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetChatInviteImportersQuery"); + promise_.set_error(std::move(status)); + } +}; + class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -6704,6 +6765,27 @@ void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId adminis ->send(dialog_id, administrator_user_id, offset_invite_link, limit); } +void ContactsManager::get_dialog_invite_link_users(DialogId dialog_id, const string &invite_link, + td_api::object_ptr offset_user, + int32 limit, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + + UserId offset_user_id; + int32 offset_date = 0; + if (offset_user != nullptr) { + offset_user_id = UserId(offset_user->user_id_); + offset_date = offset_user->joined_chat_date_; + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, invite_link, offset_date, offset_user_id, limit); +} + void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { if (invite_link_infos_.count(invite_link) > 0) { return promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index af9323224..6395c98b7 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -396,6 +396,10 @@ class ContactsManager : public Actor { void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, const string &offset_invite_link, int32 limit, Promise> &&promise); + void get_dialog_invite_link_users(DialogId dialog_id, const string &invite_link, + td_api::object_ptr offset_user, int32 limit, + Promise> &&promise); + void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index d280ba6d4..52e8eb44e 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6295,6 +6295,13 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { request.offset_invite_link_, request.limit_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::getChatInviteLinkUsers &request) { + CREATE_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.invite_link_); + contacts_manager_->get_dialog_invite_link_users(DialogId(request.chat_id_), request.invite_link_, + std::move(request.offset_user_), request.limit_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 775bcb868..1f9e8f21d 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -792,6 +792,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatInviteLinks &request); + void on_request(uint64 id, td_api::getChatInviteLinkUsers &request); + void on_request(uint64 id, td_api::checkChatInviteLink &request); void on_request(uint64 id, td_api::joinChatByInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index b413a4f6f..0b7efc4e3 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2706,6 +2706,16 @@ class CliClient final : public Actor { get_args(args, chat_id, administrator_user_id, offset_invite_link, limit); send_request(td_api::make_object( as_chat_id(chat_id), as_user_id(administrator_user_id), offset_invite_link, as_limit(limit))); + } else if (op == "gcilu") { + string chat_id; + string invite_link; + string offset_user_id; + int32 offset_date; + string limit; + get_args(args, chat_id, invite_link, offset_user_id, offset_date, limit); + send_request(td_api::make_object( + as_chat_id(chat_id), invite_link, + td_api::make_object(as_user_id(offset_user_id), offset_date), as_limit(limit))); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From 1941c0b9621f3a5fa1554350e682be19d685b618 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 14 Jan 2021 23:40:18 +0300 Subject: [PATCH 042/232] Fix loading ChatFull/ChannelFull. --- td/telegram/ContactsManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index bcb37513a..96e42861b 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -8831,6 +8831,7 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val dependencies.user_ids.insert(participant.user_id); dependencies.user_ids.insert(participant.inviter_user_id); } + dependencies.user_ids.insert(chat_full->invite_link.get_administrator_user_id()); resolve_dependencies_force(td_, dependencies, "chat_full"); for (auto &participant : chat_full->participants) { @@ -8918,6 +8919,7 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s add_dialog_and_dependencies(dependencies, DialogId(channel_full->linked_channel_id)); dependencies.chat_ids.insert(channel_full->migrated_from_chat_id); dependencies.user_ids.insert(channel_full->bot_user_ids.begin(), channel_full->bot_user_ids.end()); + dependencies.user_ids.insert(channel_full->invite_link.get_administrator_user_id()); resolve_dependencies_force(td_, dependencies, "channel_full"); for (auto &user_id : channel_full->bot_user_ids) { From dda1b626559d71a0713b3e962b499acbd992c93f Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 15 Jan 2021 01:30:05 +0300 Subject: [PATCH 043/232] Add suggestedActionSeeTicksHint. --- td/generate/scheme/td_api.tl | 3 +++ td/generate/scheme/td_api.tlo | Bin 193680 -> 193748 bytes td/telegram/SuggestedAction.cpp | 9 +++++++++ td/telegram/SuggestedAction.h | 2 +- td/telegram/cli.cpp | 5 ++++- 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 1346e9e4b..618e03545 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3094,6 +3094,9 @@ suggestedActionEnableArchiveAndMuteNewChats = SuggestedAction; //@description Suggests the user to check authorization phone number and change the phone number if it is inaccessible suggestedActionCheckPhoneNumber = SuggestedAction; +//@description Suggests the user to see a hint about meaning of one and two ticks on sent message +suggestedActionSeeTicksHint = SuggestedAction; + //@description Contains a counter @count Count count count:int32 = Count; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 45a58fda18920d4faf534d62b0c8a8a859bda9fd..dd7540b5c21f1286098eca46fbe8bb8e8f1c0d6f 100644 GIT binary patch delta 214 zcmbR6lKaX_?hR{nM2}T3NaPPLO;1lPE=f&sOfJdH&tu+vP$!(7F|}FJWV@sZqjt9h zNY%|_LOu*YP~^;}!(rQY5F|D|WBd6~`Z3nx39oT#}mNm|T*XpU1TMpiVeDV@k86$#zK-M(u71 zkgA)}Oo!#sXG2fsM%m!fLp~sIdJF8`F}B UtRNYN>57_6^4qt}U{Z(y0R0<0Q2+n{ diff --git a/td/telegram/SuggestedAction.cpp b/td/telegram/SuggestedAction.cpp index dea54ca2a..93c892997 100644 --- a/td/telegram/SuggestedAction.cpp +++ b/td/telegram/SuggestedAction.cpp @@ -12,6 +12,9 @@ SuggestedAction get_suggested_action(Slice action_str) { if (action_str == Slice("AUTOARCHIVE_POPULAR")) { return SuggestedAction::EnableArchiveAndMuteNewChats; } + if (action_str == Slice("NEWCOMER_TICKS")) { + return SuggestedAction::SeeTicksHint; + } return SuggestedAction::Empty; } @@ -19,6 +22,8 @@ string get_suggested_action_str(SuggestedAction action) { switch (action) { case SuggestedAction::EnableArchiveAndMuteNewChats: return "AUTOARCHIVE_POPULAR"; + case SuggestedAction::SeeTicksHint: + return "NEWCOMER_TICKS"; default: return string(); } @@ -33,6 +38,8 @@ SuggestedAction get_suggested_action(const td_api::object_ptr get_suggested_action_object(Suggeste return td_api::make_object(); case SuggestedAction::CheckPhoneNumber: return td_api::make_object(); + case SuggestedAction::SeeTicksHint: + return td_api::make_object(); default: UNREACHABLE(); return nullptr; diff --git a/td/telegram/SuggestedAction.h b/td/telegram/SuggestedAction.h index d588c6b6c..13607948a 100644 --- a/td/telegram/SuggestedAction.h +++ b/td/telegram/SuggestedAction.h @@ -13,7 +13,7 @@ namespace td { -enum class SuggestedAction : int32 { Empty, EnableArchiveAndMuteNewChats, CheckPhoneNumber }; +enum class SuggestedAction : int32 { Empty, EnableArchiveAndMuteNewChats, CheckPhoneNumber, SeeTicksHint }; SuggestedAction get_suggested_action(Slice action_str); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 0b7efc4e3..c8c9a8e53 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1364,6 +1364,9 @@ class CliClient final : public Actor { if (action == "number") { return td_api::make_object(); } + if (action == "ticks") { + return td_api::make_object(); + } return nullptr; } @@ -3882,7 +3885,7 @@ class CliClient final : public Actor { int64 x; get_args(args, chat_id, token, x); send_request(td_api::make_object(as_chat_id(chat_id), token, x)); - } else if (op == "hsa" || op == "glu" || op == "glua") { + } else if (op == "hsa") { send_request(td_api::make_object(as_suggested_action(args))); } else if (op == "glui" || op == "glu" || op == "glua") { string chat_id; From 3ae02a64c15b5a7a7d79d963ff73cd4baa13ca16 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 16 Jan 2021 19:23:57 +0300 Subject: [PATCH 044/232] Make ContactsManager struct declarations private. --- td/telegram/ContactsManager.cpp | 353 +++++++++++++++++++++++++++++++- td/telegram/ContactsManager.h | 350 ++----------------------------- 2 files changed, 361 insertions(+), 342 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 96e42861b..3f0345c35 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -14,6 +14,8 @@ #include "td/telegram/ConfigShared.h" #include "td/telegram/Dependencies.h" #include "td/telegram/DeviceTokenManager.h" +#include "td/telegram/DialogInviteLink.h" +#include "td/telegram/DialogLocation.h" #include "td/telegram/FileReferenceManager.h" #include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileType.h" @@ -30,6 +32,7 @@ #include "td/telegram/PasswordManager.h" #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" +#include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatActor.h" #include "td/telegram/ServerMessageId.h" #include "td/telegram/StickerSetId.hpp" @@ -1670,7 +1673,7 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { flags |= telegram_api::messages_getExportedChatInvites::OFFSET_LINK_MASK; } send_query(G()->net_query_creator().create(telegram_api::messages_getExportedChatInvites( - flags, std::move(input_peer), std::move(input_user), offset_invite_link, limit))); + flags, false /*ignored*/, std::move(input_peer), std::move(input_user), offset_invite_link, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3005,14 +3008,6 @@ class LoadAsyncGraphQuery : public Td::ResultHandler { } }; -bool ContactsManager::UserFull::is_expired() const { - return expires_at < Time::now(); -} - -bool ContactsManager::ChannelFull::is_expired() const { - return expires_at < Time::now(); -} - class ContactsManager::UploadProfilePhotoCallback : public FileManager::UploadCallback { public: void on_upload_ok(FileId file_id, tl_object_ptr input_file) override { @@ -3031,6 +3026,344 @@ class ContactsManager::UploadProfilePhotoCallback : public FileManager::UploadCa } }; +struct ContactsManager::User { + string first_name; + string last_name; + string username; + string phone_number; + int64 access_hash = -1; + + ProfilePhoto photo; + + vector restriction_reasons; + string inline_query_placeholder; + int32 bot_info_version = -1; + + int32 was_online = 0; + int32 local_was_online = 0; + + string language_code; + + std::unordered_set photo_ids; + + std::unordered_map online_member_dialogs; // id -> time + + static constexpr uint32 CACHE_VERSION = 3; + uint32 cache_version = 0; + + bool is_min_access_hash = true; + bool is_received = false; + bool is_verified = false; + bool is_support = false; + bool is_deleted = true; + bool is_bot = true; + bool can_join_groups = true; + bool can_read_all_group_messages = true; + bool is_inline_bot = false; + bool need_location_bot = false; + bool is_scam = false; + bool is_contact = false; + bool is_mutual_contact = false; + bool need_apply_min_photo = false; + + bool is_photo_inited = false; + + bool is_repaired = false; // whether cached value is rechecked + + bool is_name_changed = true; + bool is_username_changed = true; + bool is_photo_changed = true; + bool is_is_contact_changed = true; + bool is_is_deleted_changed = true; + bool is_default_permissions_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + bool is_status_changed = true; + bool is_online_status_changed = true; // whether online/offline has changed + bool is_update_user_sent = false; + + bool is_saved = false; // is current user version being saved/is saved to the database + bool is_being_saved = false; // is current user being saved to the database + bool is_status_saved = false; // is current user status being saved/is saved to the database + + bool is_received_from_server = false; // true, if the user was received from the server and not the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +struct ContactsManager::BotInfo { + int32 version = -1; + string description; + vector> commands; + bool is_changed = true; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +// do not forget to update drop_user_full and on_get_user_full +struct ContactsManager::UserFull { + Photo photo; + + string about; + + int32 common_chat_count = 0; + + bool is_blocked = false; + bool can_be_called = false; + bool supports_video_calls = false; + bool has_private_calls = false; + bool can_pin_messages = true; + bool need_phone_number_privacy_exception = false; + + bool is_common_chat_count_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + double expires_at = 0.0; + + bool is_expired() const { + return expires_at < Time::now(); + } + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +struct ContactsManager::Chat { + string title; + DialogPhoto photo; + int32 participant_count = 0; + int32 date = 0; + int32 version = -1; + int32 default_permissions_version = -1; + int32 pinned_message_version = -1; + ChannelId migrated_to_channel_id; + + DialogParticipantStatus status = DialogParticipantStatus::Banned(0); + RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; + + static constexpr uint32 CACHE_VERSION = 3; + uint32 cache_version = 0; + + bool is_active = false; + + bool is_title_changed = true; + bool is_photo_changed = true; + bool is_default_permissions_changed = true; + bool is_is_active_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + bool is_update_basic_group_sent = false; + + bool is_repaired = false; // whether cached value is rechecked + + bool is_saved = false; // is current chat version being saved/is saved to the database + bool is_being_saved = false; // is current chat being saved to the database + + bool is_received_from_server = false; // true, if the chat was received from the server and not the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +// do not forget to update drop_chat_full and on_get_chat_full +struct ContactsManager::ChatFull { + int32 version = -1; + UserId creator_user_id; + vector participants; + + Photo photo; + vector registered_photo_file_ids; + FileSourceId file_source_id; + + string description; + + DialogInviteLink invite_link; + + bool can_set_username = false; + + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +struct ContactsManager::Channel { + int64 access_hash = 0; + string title; + DialogPhoto photo; + string username; + vector restriction_reasons; + DialogParticipantStatus status = DialogParticipantStatus::Banned(0); + RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; + int32 date = 0; + int32 participant_count = 0; + + static constexpr uint32 CACHE_VERSION = 6; + uint32 cache_version = 0; + + bool has_linked_channel = false; + bool has_location = false; + bool sign_messages = false; + bool is_slow_mode_enabled = false; + + bool is_megagroup = false; + bool is_verified = false; + bool is_scam = false; + + bool is_title_changed = true; + bool is_username_changed = true; + bool is_photo_changed = true; + bool is_default_permissions_changed = true; + bool is_status_changed = true; + bool had_read_access = true; + bool was_member = false; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + bool is_update_supergroup_sent = false; + + bool is_repaired = false; // whether cached value is rechecked + + bool is_saved = false; // is current channel version being saved/is saved to the database + bool is_being_saved = false; // is current channel being saved to the database + + bool is_received_from_server = false; // true, if the channel was received from the server and not the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +// do not forget to update invalidate_channel_full and on_get_chat_full +struct ContactsManager::ChannelFull { + Photo photo; + vector registered_photo_file_ids; + FileSourceId file_source_id; + + string description; + int32 participant_count = 0; + int32 administrator_count = 0; + int32 restricted_count = 0; + int32 banned_count = 0; + + DialogInviteLink invite_link; + + uint32 speculative_version = 1; + uint32 repair_request_version = 0; + + StickerSetId sticker_set_id; + + ChannelId linked_channel_id; + + DialogLocation location; + + DcId stats_dc_id; + + int32 slow_mode_delay = 0; + int32 slow_mode_next_send_date = 0; + + MessageId migrated_from_max_message_id; + ChatId migrated_from_chat_id; + + vector bot_user_ids; + + bool can_get_participants = false; + bool can_set_username = false; + bool can_set_sticker_set = false; + bool can_set_location = false; + bool can_view_statistics = false; + bool is_can_view_statistics_inited = false; + bool is_all_history_available = true; + + bool is_slow_mode_next_send_date_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + double expires_at = 0.0; + + bool is_expired() const { + return expires_at < Time::now(); + } + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +struct ContactsManager::SecretChat { + int64 access_hash = 0; + UserId user_id; + SecretChatState state; + string key_hash; + int32 ttl = 0; + int32 date = 0; + int32 layer = 0; + FolderId initial_folder_id; + + bool is_outbound = false; + + bool is_state_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + bool is_saved = false; // is current secret chat version being saved/is saved to the database + bool is_being_saved = false; // is current secret chat being saved to the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +struct ContactsManager::InviteLinkInfo { + // known dialog + DialogId dialog_id; + + // unknown dialog + string title; + Photo photo; + int32 participant_count = 0; + vector participant_user_ids; + bool is_chat = false; + bool is_channel = false; + bool is_public = false; + bool is_megagroup = false; +}; + ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { upload_profile_photo_callback_ = std::make_shared(); @@ -3094,6 +3427,8 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent invite_link_info_expire_timeout_.set_callback_data(static_cast(this)); } +ContactsManager::~ContactsManager() = default; + void ContactsManager::tear_down() { parent_.reset(); } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 6395c98b7..7af4510d0 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -15,8 +15,6 @@ #include "td/telegram/Contact.h" #include "td/telegram/DialogAdministrator.h" #include "td/telegram/DialogId.h" -#include "td/telegram/DialogInviteLink.h" -#include "td/telegram/DialogLocation.h" #include "td/telegram/DialogParticipant.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" @@ -28,7 +26,6 @@ #include "td/telegram/Photo.h" #include "td/telegram/PublicDialogType.h" #include "td/telegram/QueryCombiner.h" -#include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/StickerSetId.h" #include "td/telegram/UserId.h" @@ -54,6 +51,9 @@ namespace td { struct BinlogEvent; +class DialogInviteLink; +class DialogLocation; + class Td; struct BotData { @@ -77,6 +77,11 @@ struct CanTransferOwnershipResult { class ContactsManager : public Actor { public: ContactsManager(Td *td, ActorShared<> parent); + ContactsManager(const ContactsManager &) = delete; + ContactsManager &operator=(const ContactsManager &) = delete; + ContactsManager(ContactsManager &&) = delete; + ContactsManager &operator=(ContactsManager &&) = delete; + ~ContactsManager() override; static UserId load_my_id(); @@ -570,89 +575,17 @@ class ContactsManager : public Actor { tl_object_ptr obj); private: - struct User { - string first_name; - string last_name; - string username; - string phone_number; - int64 access_hash = -1; + struct User; + struct UserFull; + struct Chat; + struct ChatFull; + struct Channel; + struct ChannelFull; + struct SecretChat; - ProfilePhoto photo; + struct BotInfo; - vector restriction_reasons; - string inline_query_placeholder; - int32 bot_info_version = -1; - - int32 was_online = 0; - int32 local_was_online = 0; - - string language_code; - - std::unordered_set photo_ids; - - std::unordered_map online_member_dialogs; // id -> time - - static constexpr uint32 CACHE_VERSION = 3; - uint32 cache_version = 0; - - bool is_min_access_hash = true; - bool is_received = false; - bool is_verified = false; - bool is_support = false; - bool is_deleted = true; - bool is_bot = true; - bool can_join_groups = true; - bool can_read_all_group_messages = true; - bool is_inline_bot = false; - bool need_location_bot = false; - bool is_scam = false; - bool is_contact = false; - bool is_mutual_contact = false; - bool need_apply_min_photo = false; - - bool is_photo_inited = false; - - bool is_repaired = false; // whether cached value is rechecked - - bool is_name_changed = true; - bool is_username_changed = true; - bool is_photo_changed = true; - bool is_is_contact_changed = true; - bool is_is_deleted_changed = true; - bool is_default_permissions_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - bool is_status_changed = true; - bool is_online_status_changed = true; // whether online/offline has changed - bool is_update_user_sent = false; - - bool is_saved = false; // is current user version being saved/is saved to the database - bool is_being_saved = false; // is current user being saved to the database - bool is_status_saved = false; // is current user status being saved/is saved to the database - - bool is_received_from_server = false; // true, if the user was received from the server and not the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; - - struct BotInfo { - int32 version = -1; - string description; - vector> commands; - bool is_changed = true; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; + struct InviteLinkInfo; struct UserPhotos { vector photos; @@ -661,255 +594,6 @@ class ContactsManager : public Actor { bool getting_now = false; }; - // do not forget to update drop_user_full and on_get_user_full - struct UserFull { - Photo photo; - - string about; - - int32 common_chat_count = 0; - - bool is_blocked = false; - bool can_be_called = false; - bool supports_video_calls = false; - bool has_private_calls = false; - bool can_pin_messages = true; - bool need_phone_number_privacy_exception = false; - - bool is_common_chat_count_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_send_update = true; // have new changes that need only to be sent to the client - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - double expires_at = 0.0; - - bool is_expired() const; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; - - struct Chat { - string title; - DialogPhoto photo; - int32 participant_count = 0; - int32 date = 0; - int32 version = -1; - int32 default_permissions_version = -1; - int32 pinned_message_version = -1; - ChannelId migrated_to_channel_id; - - DialogParticipantStatus status = DialogParticipantStatus::Banned(0); - RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; - - static constexpr uint32 CACHE_VERSION = 3; - uint32 cache_version = 0; - - bool is_active = false; - - bool is_title_changed = true; - bool is_photo_changed = true; - bool is_default_permissions_changed = true; - bool is_is_active_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - bool is_update_basic_group_sent = false; - - bool is_repaired = false; // whether cached value is rechecked - - bool is_saved = false; // is current chat version being saved/is saved to the database - bool is_being_saved = false; // is current chat being saved to the database - - bool is_received_from_server = false; // true, if the chat was received from the server and not the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; - - // do not forget to update drop_chat_full and on_get_chat_full - struct ChatFull { - int32 version = -1; - UserId creator_user_id; - vector participants; - - Photo photo; - vector registered_photo_file_ids; - FileSourceId file_source_id; - - string description; - - DialogInviteLink invite_link; - - bool can_set_username = false; - - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_send_update = true; // have new changes that need only to be sent to the client - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; - - struct Channel { - int64 access_hash = 0; - string title; - DialogPhoto photo; - string username; - vector restriction_reasons; - DialogParticipantStatus status = DialogParticipantStatus::Banned(0); - RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; - int32 date = 0; - int32 participant_count = 0; - - static constexpr uint32 CACHE_VERSION = 6; - uint32 cache_version = 0; - - bool has_linked_channel = false; - bool has_location = false; - bool sign_messages = false; - bool is_slow_mode_enabled = false; - - bool is_megagroup = false; - bool is_verified = false; - bool is_scam = false; - - bool is_title_changed = true; - bool is_username_changed = true; - bool is_photo_changed = true; - bool is_default_permissions_changed = true; - bool is_status_changed = true; - bool had_read_access = true; - bool was_member = false; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - bool is_update_supergroup_sent = false; - - bool is_repaired = false; // whether cached value is rechecked - - bool is_saved = false; // is current channel version being saved/is saved to the database - bool is_being_saved = false; // is current channel being saved to the database - - bool is_received_from_server = false; // true, if the channel was received from the server and not the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; - - // do not forget to update invalidate_channel_full and on_get_chat_full - struct ChannelFull { - Photo photo; - vector registered_photo_file_ids; - FileSourceId file_source_id; - - string description; - int32 participant_count = 0; - int32 administrator_count = 0; - int32 restricted_count = 0; - int32 banned_count = 0; - - DialogInviteLink invite_link; - - uint32 speculative_version = 1; - uint32 repair_request_version = 0; - - StickerSetId sticker_set_id; - - ChannelId linked_channel_id; - - DialogLocation location; - - DcId stats_dc_id; - - int32 slow_mode_delay = 0; - int32 slow_mode_next_send_date = 0; - - MessageId migrated_from_max_message_id; - ChatId migrated_from_chat_id; - - vector bot_user_ids; - - bool can_get_participants = false; - bool can_set_username = false; - bool can_set_sticker_set = false; - bool can_set_location = false; - bool can_view_statistics = false; - bool is_can_view_statistics_inited = false; - bool is_all_history_available = true; - - bool is_slow_mode_next_send_date_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_send_update = true; // have new changes that need only to be sent to the client - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - double expires_at = 0.0; - bool is_expired() const; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; - - struct SecretChat { - int64 access_hash = 0; - UserId user_id; - SecretChatState state; - string key_hash; - int32 ttl = 0; - int32 date = 0; - int32 layer = 0; - FolderId initial_folder_id; - - bool is_outbound = false; - - bool is_state_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - bool is_saved = false; // is current secret chat version being saved/is saved to the database - bool is_being_saved = false; // is current secret chat being saved to the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); - }; - - struct InviteLinkInfo { - // known dialog - DialogId dialog_id; - - // unknown dialog - string title; - Photo photo; - int32 participant_count = 0; - vector participant_user_ids; - bool is_chat = false; - bool is_channel = false; - bool is_public = false; - bool is_megagroup = false; - }; - struct DialogNearby { DialogId dialog_id; int32 distance; From ea70b5abafe72b0c63ec07591f545db16fd6a15f Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 17 Jan 2021 04:05:30 +0300 Subject: [PATCH 045/232] Update layer 123. --- td/generate/scheme/telegram_api.tl | 12 ++++++++---- td/generate/scheme/telegram_api.tlo | Bin 226796 -> 227544 bytes td/telegram/SecretChatActor.cpp | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index ffe3710a7..10d1c9af9 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -394,7 +394,7 @@ encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat; encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat; encryptedChatRequested#62718a82 flags:# folder_id:flags.0?int id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat; encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat; -encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat; +encryptedChatDiscarded#1e1c7c45 flags:# history_deleted:flags.0?true id:int = EncryptedChat; inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat; @@ -522,7 +522,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteExported#a9a847ea flags:# revoked:flags.0?true expired:flags.4?true permanent:flags.5?true link:string admin_id:int date:int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; +chatInviteExported#6e24fc9d flags:# revoked:flags.0?true expired:flags.4?true permanent:flags.5?true link:string admin_id:int date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; @@ -1348,7 +1348,7 @@ messages.createChat#9cb126e users:Vector title:string = Updates; messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat; -messages.discardEncryption#edd923c5 chat_id:int = Bool; +messages.discardEncryption#f393aea0 flags:# delete_history:flags.0?true chat_id:int = Bool; messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool; messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool; messages.sendEncrypted#44fa7a15 flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; @@ -1450,9 +1450,13 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; -messages.getExportedChatInvites#6d9cae03 flags:# peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.getExportedChatInvites#6d9cae03 flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; +messages.deleteRevokedExportedChatInvites#52041463 peer:InputPeer = Bool; +messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; +messages.deleteChat#83247d11 chat_id:int = Bool; +messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index f19c70fbd63414c34680a5d195436901bdf137c6..dc0ef9c57981e86e05d17cf358df4454651e71d3 100644 GIT binary patch delta 735 zcmZ8eUr1A76yN>6g?FiV%_S4pywQS%H@#2~Blh5OD7to&i6a#{C>al^-J@I zYO{QoHQxC3xSh)4S18dRD0eU-48COqDDN; zTDU0I%Z9!8-geOM&0B5lQfxGtOQfYxe>j--L{g){WJn4bbP?i|$hCwcsdPM<8wp8K zNrQpUjhz4ZtZ<7iOy&8O%%-~H`r=0N*)5R!OP_$vF->G^f+yMKX&pByKam;dkpnsz zQXTFE?j^&i`PYncQwKn$7kMCi8D0OMssqF5)hT9NJ$wfp3+O^2;KTyBE@3OFX(r7r znDw%%d`D?)ZM|lnY8#VMso)hU)kCS*1x{`>9*4x3W?f)LyM$vK6&&4R)=BIE|1O#{K3pO{pth?G zI6WnRyoOlVT+?)HO4tfpw;5iSrTTv&X9K;;>REB-1y=M#Eim|FGk9vqMcCO6@@>5f g@)@Q{Ra$VLjk;RdKmv#(*iHiTJashMgA3>X0<0-13IG5A delta 494 zcmccdk@wAO-VIYYSl&lfNKc-@v4Z85`-+vDT{vxW!K}$K)e_SUgqQ@TUtnSu*?gdE z1`9+*O!WqBh$eyMIuM4#at#Qh;g`hZ11siCRnZ?P8MJcH%3}8RNoU=J)wGFc($TvdCKvkZ3WtkNFAaRgo zT$>NB<7S*b=RcFkWS$KLV81mtY}nqgfzd*i6>RzBk8%3bC!Asw*#74%dtaRr2#1-9RaW7=cPn7e)IBqrqqi02l3l$pLji&|MpP5RS zr?Y%#YS=FDgJ}!n^fSMh=CB-9zWH`~&Tl4_={C!lIHq6t#$>Wx;SZAqJJ^!R7n}^H z_b@YW0rR%IurS}@ozAj_Nq2JYE&1tx${2aJFA!pOkOSK=J>fBv&U67oW*3Ns5r)hS F?f^%r$u$4~ diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index ea88cb424..f3c6d3512 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -761,7 +761,8 @@ void SecretChatActor::do_close_chat_impl(unique_ptr context_->secret_chat_db()->erase_value(config_state_); context_->secret_chat_db()->erase_value(pfs_state_); context_->secret_chat_db()->erase_value(seq_no_state_); - auto query = create_net_query(QueryType::DiscardEncryption, telegram_api::messages_discardEncryption(auth_state_.id)); + int32 flags = 0; + auto query = create_net_query(QueryType::DiscardEncryption, telegram_api::messages_discardEncryption(flags, false /*ignored*/, auth_state_.id)); send_update_secret_chat(); From 65bf9ad604584ff4a89668f0dcb225e1f195bb32 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 17 Jan 2021 23:07:17 +0300 Subject: [PATCH 046/232] Improve field names and documentation. --- td/generate/scheme/td_api.tl | 45 ++++++++++++++++---------------- td/generate/scheme/td_api.tlo | Bin 193748 -> 193788 bytes td/telegram/ContactsManager.cpp | 25 +++++++++--------- td/telegram/ContactsManager.h | 4 +-- td/telegram/Td.cpp | 9 ++++--- td/telegram/Td.h | 2 +- td/telegram/cli.cpp | 18 ++++++------- 7 files changed, 52 insertions(+), 51 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 618e03545..0036d8c9c 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -237,7 +237,7 @@ maskPosition point:MaskPoint x_shift:double y_shift:double scale:double = MaskPo closedVectorPath commands:vector = ClosedVectorPath; -//@description Describes one answer option of a poll @text Option text, 1-100 characters @voter_count Number of voters for this option, available only for closed or voted polls @vote_percentage The percentage of votes for this option, 0-100 +//@description Describes one answer option of a poll @text Option text; 1-100 characters @voter_count Number of voters for this option, available only for closed or voted polls @vote_percentage The percentage of votes for this option; 0-100 //@is_chosen True, if the option was chosen by the user @is_being_chosen True, if the option is being chosen by a pending setPollAnswer request pollOption text:string voter_count:int32 vote_percentage:int32 is_chosen:Bool is_being_chosen:Bool = PollOption; @@ -249,7 +249,7 @@ pollTypeRegular allow_multiple_answers:Bool = PollType; //@description A poll in quiz mode, which has exactly one correct answer option and can be answered only once //@correct_option_id 0-based identifier of the correct answer option; -1 for a yet unanswered poll -//@explanation Text that is shown when the user chooses an incorrect answer or taps on the lamp icon, 0-200 characters with at most 2 line feeds; empty for a yet unanswered poll +//@explanation Text that is shown when the user chooses an incorrect answer or taps on the lamp icon; 0-200 characters with at most 2 line feeds; empty for a yet unanswered poll pollTypeQuiz correct_option_id:int32 explanation:formattedText = PollType; @@ -308,7 +308,7 @@ venue location:location title:string address:string provider:string id:string ty //@param_description Game description @photo Game photo @animation Game animation; may be null game id:int64 short_name:string title:string text:formattedText description:string photo:photo animation:animation = Game; -//@description Describes a poll @id Unique poll identifier @question Poll question, 1-300 characters @options List of poll answer options +//@description Describes a poll @id Unique poll identifier @question Poll question; 1-300 characters @options List of poll answer options //@total_voter_count Total number of voters, participating in the poll @recent_voter_user_ids User identifiers of recent voters, if the poll is non-anonymous //@is_anonymous True, if the poll is anonymous @type Type of the poll //@open_period Amount of time the poll will be active after creation, in seconds @close_date Point in time (Unix timestamp) when the poll will be automatically closed @is_closed True, if the poll is closed @@ -547,14 +547,21 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@description Contains a chat invite link @invite_link Chat invite link @administrator_user_id User identifier of an administrator created the link //@date Point in time (Unix timestamp) when the link was created @expire_date Point in time (Unix timestamp) when the link will expire; 0 if never -//@usage_limit Maximum number of times the link can be used; 0 if not limited @usage_count Number of times the link has already been used +//@member_limit Maximum number of members, which can join the chat using the link simultaneously; 0 if not limited +//@member_count Number of chat members, which joined the chat using the link //@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator at any time //@is_expired True, if the link is already expired @is_revoked True, if the link was revoked -chatInviteLink invite_link:string administrator_user_id:int32 date:int32 expire_date:int32 usage_limit:int32 usage_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; +chatInviteLink invite_link:string administrator_user_id:int32 date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; +//@description Describes a chat member joined a chat by an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat +chatInviteLinkMember user_id:int32 joined_chat_date:int32 = ChatInviteLinkMember; + +//@description Contains a list of chat members joined a chat by an invite link @total_count Approximate total count of chat members found @members List of chat members, joined a chat by an invite link +chatInviteLinkMembers total_count:int32 members:vector = ChatInviteLinkMembers; + //@description Contains information about a chat invite link //@chat_id Chat identifier of the invite link; 0 if the user has no access to the chat before joining //@accessible_for If non-zero, the amount of time for which read access to the chat will remain available, in seconds @@ -566,12 +573,6 @@ chatInviteLinks total_count:int32 invite_links:vector = ChatInvi //@is_public True, if the chat is a public supergroup or channel, i.e. it has a username or it is a location-based supergroup chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string photo:chatPhotoInfo member_count:int32 member_user_ids:vector is_public:Bool = ChatInviteLinkInfo; -//@description Describes a user joined a chat by an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat -chatInviteLinkUser user_id:int32 joined_chat_date:int32 = ChatInviteLinkUser; - -//@description Contains a list of users joined a chat by an invite link @total_count Approximate total count of users found @users List of users, joined a chat by an invite link -chatInviteLinkUsers total_count:int32 users:vector = ChatInviteLinkUsers; - //@description Represents a basic group of 0-200 users (must be upgraded to a supergroup to accommodate more than 200 users) //@id Group identifier @@ -1373,7 +1374,7 @@ passportElementTypePhoneNumber = PassportElementType; passportElementTypeEmailAddress = PassportElementType; -//@description Represents a date according to the Gregorian calendar @day Day of the month, 1-31 @month Month, 1-12 @year Year, 1-9999 +//@description Represents a date according to the Gregorian calendar @day Day of the month; 1-31 @month Month; 1-12 @year Year; 1-9999 date day:int32 month:int32 year:int32 = Date; //@description Contains the user's personal details @@ -1864,7 +1865,7 @@ inputMessageGame bot_user_id:int32 game_short_name:string = InputMessageContent; //@payload The invoice payload @provider_token Payment provider token @provider_data JSON-encoded data about the invoice, which will be shared with the payment provider @start_parameter Unique invoice bot start_parameter for the generation of this invoice inputMessageInvoice invoice:invoice title:string description:string photo_url:string photo_size:int32 photo_width:int32 photo_height:int32 payload:bytes provider_token:string provider_data:string start_parameter:string = InputMessageContent; -//@description A message with a poll. Polls can't be sent to secret chats. Polls can be sent only to a private chat with a bot @question Poll question, 1-255 characters (up to 300 characters for bots) @options List of poll answer options, 2-10 strings 1-100 characters each +//@description A message with a poll. Polls can't be sent to secret chats. Polls can be sent only to a private chat with a bot @question Poll question; 1-255 characters (up to 300 characters for bots) @options List of poll answer options, 2-10 strings 1-100 characters each //@is_anonymous True, if the poll voters are anonymous. Non-anonymous polls can't be sent or forwarded to channels @type Type of the poll //@open_period Amount of time the poll will be active after creation, in seconds; for bots only //@close_date Point in time (Unix timestamp) when the poll will be automatically closed; for bots only @@ -2537,7 +2538,7 @@ backgroundTypeWallpaper is_blurred:Bool is_moving:Bool = BackgroundType; //@description A PNG or TGV (gzipped subset of SVG with MIME type "application/x-tgwallpattern") pattern to be combined with the background fill chosen by the user //@fill Description of the background fill -//@intensity Intensity of the pattern when it is shown above the filled background, 0-100 +//@intensity Intensity of the pattern when it is shown above the filled background; 0-100 //@is_moving True, if the background needs to be slightly moved when device is tilted backgroundTypePattern fill:BackgroundFill intensity:int32 is_moving:Bool = BackgroundType; @@ -4365,25 +4366,25 @@ deleteFile file_id:int32 = Ok; //@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right //@chat_id Chat identifier //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never -//@usage_limit Maximum number of times the link can be used; pass 0 if not limited +//@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited //@is_permanent True, if new permanent chat link needs to be created instead of the previous one -createChatInviteLink chat_id:int53 expire_date:int32 usage_limit:int32 is_permanent:Bool = ChatInviteLink; +createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 is_permanent:Bool = ChatInviteLink; //@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right //@chat_id Chat identifier //@invite_link Invite link to be edited; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never -//@usage_limit Maximum number of times the link can be used; pass 0 if not limited +//@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited //@is_revoked True, if the link is revoked -editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 usage_limit:int32 is_revoked:Bool = ChatInviteLink; +editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 is_revoked:Bool = ChatInviteLink; //@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned //@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning @limit Maximum number of invite links to return getChatInviteLinks chat_id:int53 administrator_user_id:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; -//@description Returns users joined a chat by an invite link. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @invite_link Invite link for which to return users -//@offset_user A user from which to return next users; use null to get results from the beginning @limit Maximum number of users to return -getChatInviteLinkUsers chat_id:int53 invite_link:string offset_user:chatInviteLinkUser limit:int32 = ChatInviteLinkUsers; +//@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @invite_link Invite link for which to return chat members +//@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return +getChatInviteLinkMembers chat_id:int53 invite_link:string offset_member:chatInviteLinkMember limit:int32 = ChatInviteLinkMembers; //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; @@ -5061,7 +5062,7 @@ setLogTagVerbosityLevel tag:string new_verbosity_level:int32 = Ok; getLogTagVerbosityLevel tag:string = LogVerbosityLevel; //@description Adds a message to TDLib internal log. Can be called synchronously -//@verbosity_level The minimum verbosity level needed for the message to be logged, 0-1023 @text Text of a message to log +//@verbosity_level The minimum verbosity level needed for the message to be logged; 0-1023 @text Text of a message to log addLogMessage verbosity_level:int32 text:string = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index dd7540b5c21f1286098eca46fbe8bb8e8f1c0d6f..eb1d4c0055f16b411d680043d738ceb7f9278403 100644 GIT binary patch delta 544 zcmccelKam~?hQBCSvGur{cZ9c_6W(|zHF=>n~cER%C83m^tOsH|XA0J{a%0Ek-{7#Kiu(=U8s)S0}X+Q0zB0}6t{RXMI0 zusgBoEM{N;$xSY336TJ4VuWfca%R)vux&dCmdvPW0J~zkLl%=lGf(Yyo?1p04~US@ zQAX+Q`bQa6YZYFvomwuET$Gwvl8PE6Kr=!9ocI31X@dQ<){X z?Wjb0Dw-LP&;t4y>}imD0%Ume)6$AlOX73Eu?q1%$mieyRmfs8U;%}}_L@wlh;9HH C>fcNN delta 558 zcmezKlKaX_?hQBCS;Aj%f0=xTJwkNl)AzPQ&KZd%o_S@NC8<7{dD)@GlP9uEi}xDr zS}BY!lUg*HpGk7_9F7wXU^6ync(Ulrf=uH}Mlpl|BzE(dkPqkdgMm!i(|=?#8BBlR z&d4&|A&yaY`rAMzWghO*;>7gS_?*n#%#z6;j!3eC`IG&2%1y4QEPw z^FxbMix@!S(=U8v(wV%a+CT@yV}OFIa$GTB4`Avl28mB|XEb01+6VN{$8LEEkXA;h z)*@#%9S+;JgCJ#-8)_QB?wFpD#iY=zQoCKHmXXB+EVNzcD5FCy3yX4E_4EQKCJBf- zg)fXc)87O#si68!pi**jWu@}u4Xzvz%_h!FK72BnQG7pzQd2TZP{SD~M^2`aUNnITSOeD+Dn?B4K+<5L1nq!uwAgTgB2-(aZt`R51g{Gt> promise_; + Promise> promise_; DialogId dialog_id_; public: - explicit GetChatInviteImportersQuery(Promise> &&promise) + explicit GetChatInviteImportersQuery(Promise> &&promise) : promise_(std::move(promise)) { } @@ -1752,7 +1752,7 @@ class GetChatInviteImportersQuery : public Td::ResultHandler { LOG(ERROR) << "Receive wrong total count of invite link users " << total_count << " in " << dialog_id_; total_count = static_cast(result->importers_.size()); } - vector> invite_link_users; + vector> invite_link_members; for (auto &importer : result->importers_) { UserId user_id(importer->user_id_); if (!user_id.is_valid()) { @@ -1760,10 +1760,10 @@ class GetChatInviteImportersQuery : public Td::ResultHandler { total_count--; continue; } - invite_link_users.push_back(td_api::make_object( - td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkUser"), importer->date_)); + invite_link_members.push_back(td_api::make_object( + td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkMember"), importer->date_)); } - promise_.set_value(td_api::make_object(total_count, std::move(invite_link_users))); + promise_.set_value(td_api::make_object(total_count, std::move(invite_link_members))); } void on_error(uint64 id, Status status) override { @@ -7100,10 +7100,9 @@ void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId adminis ->send(dialog_id, administrator_user_id, offset_invite_link, limit); } -void ContactsManager::get_dialog_invite_link_users(DialogId dialog_id, const string &invite_link, - td_api::object_ptr offset_user, - int32 limit, - Promise> &&promise) { +void ContactsManager::get_dialog_invite_link_users( + DialogId dialog_id, const string &invite_link, td_api::object_ptr offset_member, + int32 limit, Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); if (limit <= 0) { @@ -7112,9 +7111,9 @@ void ContactsManager::get_dialog_invite_link_users(DialogId dialog_id, const str UserId offset_user_id; int32 offset_date = 0; - if (offset_user != nullptr) { - offset_user_id = UserId(offset_user->user_id_); - offset_date = offset_user->joined_chat_date_; + if (offset_member != nullptr) { + offset_user_id = UserId(offset_member->user_id_); + offset_date = offset_member->joined_chat_date_; } td_->create_handler(std::move(promise)) diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 7af4510d0..2f5be621c 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -402,8 +402,8 @@ class ContactsManager : public Actor { int32 limit, Promise> &&promise); void get_dialog_invite_link_users(DialogId dialog_id, const string &invite_link, - td_api::object_ptr offset_user, int32 limit, - Promise> &&promise); + td_api::object_ptr offset_member, int32 limit, + Promise> &&promise); void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 52e8eb44e..b4c319938 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6277,7 +6277,7 @@ void Td::on_request(uint64 id, td_api::getChatAdministrators &request) { void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { CREATE_REQUEST_PROMISE(); - contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.usage_limit_, + contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.member_limit_, request.is_permanent_, std::move(promise)); } @@ -6285,7 +6285,7 @@ void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.invite_link_); contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, request.expire_date_, - request.usage_limit_, request.is_revoked_, std::move(promise)); + request.member_limit_, request.is_revoked_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { @@ -6295,11 +6295,12 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { request.offset_invite_link_, request.limit_, std::move(promise)); } -void Td::on_request(uint64 id, td_api::getChatInviteLinkUsers &request) { +void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.invite_link_); contacts_manager_->get_dialog_invite_link_users(DialogId(request.chat_id_), request.invite_link_, - std::move(request.offset_user_), request.limit_, std::move(promise)); + std::move(request.offset_member_), request.limit_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 1f9e8f21d..724ce0055 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -792,7 +792,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatInviteLinks &request); - void on_request(uint64 id, td_api::getChatInviteLinkUsers &request); + void on_request(uint64 id, td_api::getChatInviteLinkMembers &request); void on_request(uint64 id, td_api::checkChatInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index c8c9a8e53..ac0ff3161 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2688,19 +2688,19 @@ class CliClient final : public Actor { } else if (op == "ccilp" || op == "ccilt") { string chat_id; int32 expire_date; - int32 usage_limit; - get_args(args, chat_id, expire_date, usage_limit); - send_request(td_api::make_object(as_chat_id(chat_id), expire_date, usage_limit, + int32 member_limit; + get_args(args, chat_id, expire_date, member_limit); + send_request(td_api::make_object(as_chat_id(chat_id), expire_date, member_limit, op == "ccilp")); } else if (op == "ecil") { string chat_id; string invite_link; int32 expire_date; - int32 usage_limit; + int32 member_limit; bool is_revoked; - get_args(args, chat_id, invite_link, expire_date, usage_limit, is_revoked); + get_args(args, chat_id, invite_link, expire_date, member_limit, is_revoked); send_request(td_api::make_object(as_chat_id(chat_id), invite_link, expire_date, - usage_limit, is_revoked)); + member_limit, is_revoked)); } else if (op == "gcil") { string chat_id; string administrator_user_id; @@ -2709,16 +2709,16 @@ class CliClient final : public Actor { get_args(args, chat_id, administrator_user_id, offset_invite_link, limit); send_request(td_api::make_object( as_chat_id(chat_id), as_user_id(administrator_user_id), offset_invite_link, as_limit(limit))); - } else if (op == "gcilu") { + } else if (op == "gcilm") { string chat_id; string invite_link; string offset_user_id; int32 offset_date; string limit; get_args(args, chat_id, invite_link, offset_user_id, offset_date, limit); - send_request(td_api::make_object( + send_request(td_api::make_object( as_chat_id(chat_id), invite_link, - td_api::make_object(as_user_id(offset_user_id), offset_date), as_limit(limit))); + td_api::make_object(as_user_id(offset_user_id), offset_date), as_limit(limit))); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From a2c1a695d2872805bcf2ceadb252b3a9e8b2339c Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 17 Jan 2021 23:24:48 +0300 Subject: [PATCH 047/232] Add edit date to information about chat invite links. --- td/generate/scheme/td_api.tl | 6 ++++-- td/generate/scheme/td_api.tlo | Bin 193788 -> 193824 bytes td/telegram/DialogInviteLink.cpp | 32 +++++++++++++++++++------------ td/telegram/DialogInviteLink.h | 11 +++++++++++ 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 0036d8c9c..c157fdafc 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -546,12 +546,14 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@description Contains a chat invite link @invite_link Chat invite link @administrator_user_id User identifier of an administrator created the link -//@date Point in time (Unix timestamp) when the link was created @expire_date Point in time (Unix timestamp) when the link will expire; 0 if never +//@date Point in time (Unix timestamp) when the link was created +//@edit_date Point in time (Unix timestamp) when the link was last edited; 0 if never or unknown +//@expire_date Point in time (Unix timestamp) when the link will expire; 0 if never //@member_limit Maximum number of members, which can join the chat using the link simultaneously; 0 if not limited //@member_count Number of chat members, which joined the chat using the link //@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator at any time //@is_expired True, if the link is already expired @is_revoked True, if the link was revoked -chatInviteLink invite_link:string administrator_user_id:int32 date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; +chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index eb1d4c0055f16b411d680043d738ceb7f9278403..8e102873f176c9743a320c0ff5e5a306b4408c2c 100644 GIT binary patch delta 211 zcmezKl6%1`?hQBCS^Oj)FWh{Gy~7d0%J5{-mj#LOC1)g-c;=O5mZbV*=4CU0#BLrF z^5L3(Fpx=m`j1Q|gXs_48Cj-Z@Me^qep7)_dHS0`CZEXxm2)5_eW(;*2D7GTWHBi; ztJH2+sbysG01Iu`Im#GQ3z1d$!l(n$T2ZMoeStJ1Co|Bh?IzAlDSQwW4?3B2rW<53 gsZ4e_#x{MQ6O-Fyh1o3AZGxCoAf}}RG0j*60NBq_761SM delta 181 zcmZ4Riu=z??hQBCSvGur{cZCd_6|n~E5nmTUlt_BmzLS(56LnU~D~61#a! z$cJ;f;Za8EX+cZ^(`B-l45okZW>lDd$BD^zvTr5(=?FxyBZz6nA^`Ng BSL*-( diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index 1512dcc2e..3eeddaf94 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -30,13 +30,13 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrdate_; if (date_ < 1000000000) { - LOG(ERROR) << "Receive wrong date " << date_ << " as creation date of a link " << invite_link_; + LOG(ERROR) << "Receive wrong date " << date_ << " as a creation date of a link " << invite_link_; date_ = 0; } if ((exported_invite->flags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { expire_date_ = exported_invite->expire_date_; - if (expire_date_ < 0) { - LOG(ERROR) << "Receive wrong date " << expire_date_ << " as expire date of a link " << invite_link_; + if (expire_date_ < 1000000000) { + LOG(ERROR) << "Receive wrong date " << expire_date_ << " as an expire date of a link " << invite_link_; expire_date_ = 0; } } @@ -54,13 +54,21 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrflags_ & telegram_api::chatInviteExported::START_DATE_MASK) != 0) { + edit_date_ = exported_invite->start_date_; + if (edit_date_ < 1000000000) { + LOG(ERROR) << "Receive wrong date " << edit_date_ << " as an edit date of a link " << invite_link_; + edit_date_ = 0; + } + } is_revoked_ = exported_invite->revoked_; is_permanent_ = exported_invite->permanent_; - if (is_permanent_ && (usage_limit_ > 0 || expire_date_ > 0)) { - LOG(ERROR) << "Receive wron permanent " << *this; + if (is_permanent_ && (usage_limit_ > 0 || expire_date_ > 0 || edit_date_ > 0)) { + LOG(ERROR) << "Receive wrong permanent " << *this; expire_date_ = 0; usage_limit_ = 0; + edit_date_ = 0; } } @@ -114,14 +122,14 @@ td_api::object_ptr DialogInviteLink::get_chat_invite_lin return td_api::make_object( invite_link_, contacts_manager->get_user_id_object(administrator_user_id_, "get_chat_invite_link_object"), date_, - expire_date_, usage_limit_, usage_count_, is_permanent_, is_expired(), is_revoked_); + edit_date_, expire_date_, usage_limit_, usage_count_, is_permanent_, is_expired(), is_revoked_); } bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { return lhs.invite_link_ == rhs.invite_link_ && lhs.administrator_user_id_ == rhs.administrator_user_id_ && - lhs.date_ == rhs.date_ && lhs.expire_date_ == rhs.expire_date_ && lhs.usage_limit_ == rhs.usage_limit_ && - lhs.usage_count_ == rhs.usage_count_ && lhs.is_permanent_ == rhs.is_permanent_ && - lhs.is_revoked_ == rhs.is_revoked_; + lhs.date_ == rhs.date_ && lhs.edit_date_ == rhs.edit_date_ && lhs.expire_date_ == rhs.expire_date_ && + lhs.usage_limit_ == rhs.usage_limit_ && lhs.usage_count_ == rhs.usage_count_ && + lhs.is_permanent_ == rhs.is_permanent_ && lhs.is_revoked_ == rhs.is_revoked_; } bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { @@ -130,9 +138,9 @@ bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link) { return string_builder << "ChatInviteLink[" << invite_link.invite_link_ << " by " << invite_link.administrator_user_id_ - << " created at " << invite_link.date_ << " expiring at " << invite_link.expire_date_ - << " used by " << invite_link.usage_count_ << " with usage limit " << invite_link.usage_limit_ - << "]"; + << " created at " << invite_link.date_ << " edited at " << invite_link.edit_date_ + << " expiring at " << invite_link.expire_date_ << " used by " << invite_link.usage_count_ + << " with usage limit " << invite_link.usage_limit_ << "]"; } } // namespace td diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index 1cd787eb1..4a7146330 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -23,6 +23,7 @@ class DialogInviteLink { string invite_link_; UserId administrator_user_id_; int32 date_ = 0; + int32 edit_date_ = 0; int32 expire_date_ = 0; int32 usage_limit_ = 0; int32 usage_count_ = 0; @@ -68,12 +69,14 @@ class DialogInviteLink { bool has_expire_date = expire_date_ != 0; bool has_usage_limit = usage_limit_ != 0; bool has_usage_count = usage_count_ != 0; + bool has_edit_date = edit_date_ != 0; BEGIN_STORE_FLAGS(); STORE_FLAG(is_revoked_); STORE_FLAG(is_permanent_); STORE_FLAG(has_expire_date); STORE_FLAG(has_usage_limit); STORE_FLAG(has_usage_count); + STORE_FLAG(has_edit_date); END_STORE_FLAGS(); store(invite_link_, storer); store(administrator_user_id_, storer); @@ -87,6 +90,9 @@ class DialogInviteLink { if (has_usage_count) { store(usage_count_, storer); } + if (has_edit_date) { + store(edit_date_, storer); + } } template @@ -95,12 +101,14 @@ class DialogInviteLink { bool has_expire_date; bool has_usage_limit; bool has_usage_count; + bool has_edit_date; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_revoked_); PARSE_FLAG(is_permanent_); PARSE_FLAG(has_expire_date); PARSE_FLAG(has_usage_limit); PARSE_FLAG(has_usage_count); + PARSE_FLAG(has_edit_date); END_PARSE_FLAGS(); parse(invite_link_, parser); parse(administrator_user_id_, parser); @@ -114,6 +122,9 @@ class DialogInviteLink { if (has_usage_count) { parse(usage_count_, parser); } + if (has_edit_date) { + parse(edit_date_, parser); + } } }; From cc8c0cbe64c48db41bf340e53313486e99d41784 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 15:04:31 +0300 Subject: [PATCH 048/232] Support delete_history flag in messages.discardEncryption. --- td/telegram/SecretChatActor.cpp | 38 ++++++++++++++------------ td/telegram/SecretChatActor.h | 7 +++-- td/telegram/SecretChatsManager.cpp | 7 +++-- td/telegram/SecretChatsManager.h | 5 ++-- td/telegram/Td.cpp | 2 +- td/telegram/UpdatesManager.cpp | 2 +- td/telegram/logevent/SecretChatEvent.h | 20 +++++++++----- 7 files changed, 46 insertions(+), 35 deletions(-) diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index f3c6d3512..13ffbf93f 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -140,7 +140,7 @@ void SecretChatActor::on_result_resendable(NetQueryPtr net_query, Promise event) { - do_close_chat_impl(std::move(event)); + do_close_chat_impl(event->delete_history, event->log_event_id()); } void SecretChatActor::replay_create_chat(unique_ptr event) { @@ -709,10 +709,10 @@ void SecretChatActor::check_status(Status status) { void SecretChatActor::on_fatal_error(Status status) { LOG(ERROR) << "Fatal error: " << status; - cancel_chat(Promise<>()); + cancel_chat(false, Promise<>()); } -void SecretChatActor::cancel_chat(Promise<> promise) { +void SecretChatActor::cancel_chat(bool delete_history, Promise<> promise) { if (close_flag_) { promise.set_value(Unit()); return; @@ -735,26 +735,26 @@ void SecretChatActor::cancel_chat(Promise<> promise) { auto event = make_unique(); event->chat_id = auth_state_.id; - event->set_log_event_id(binlog_add(context_->binlog(), LogEvent::HandlerType::SecretChats, create_storer(*event))); + auto log_event_id = binlog_add(context_->binlog(), LogEvent::HandlerType::SecretChats, create_storer(*event)); - auto on_sync = PromiseCreator::lambda( - [actor_id = actor_id(this), event = std::move(event), promise = std::move(promise)](Result result) mutable { - if (result.is_ok()) { - send_closure(actor_id, &SecretChatActor::do_close_chat_impl, std::move(event)); - promise.set_value(Unit()); - } else { - promise.set_error(result.error().clone()); - send_closure(actor_id, &SecretChatActor::on_promise_error, result.move_as_error(), "do_close_chat_impl"); - } - }); + auto on_sync = PromiseCreator::lambda([actor_id = actor_id(this), delete_history, log_event_id, + promise = std::move(promise)](Result result) mutable { + if (result.is_ok()) { + send_closure(actor_id, &SecretChatActor::do_close_chat_impl, delete_history, log_event_id); + promise.set_value(Unit()); + } else { + promise.set_error(result.error().clone()); + send_closure(actor_id, &SecretChatActor::on_promise_error, result.move_as_error(), "cancel_chat"); + } + }); context_->binlog()->force_sync(std::move(on_sync)); yield(); } -void SecretChatActor::do_close_chat_impl(unique_ptr event) { +void SecretChatActor::do_close_chat_impl(bool delete_history, uint64 log_event_id) { close_flag_ = true; - close_log_event_id_ = event->log_event_id(); + close_log_event_id_ = log_event_id; LOG(INFO) << "Send messages.discardEncryption"; auth_state_.state = State::Closed; context_->secret_chat_db()->set_value(auth_state_); @@ -762,7 +762,11 @@ void SecretChatActor::do_close_chat_impl(unique_ptr context_->secret_chat_db()->erase_value(pfs_state_); context_->secret_chat_db()->erase_value(seq_no_state_); int32 flags = 0; - auto query = create_net_query(QueryType::DiscardEncryption, telegram_api::messages_discardEncryption(flags, false /*ignored*/, auth_state_.id)); + if (delete_history) { + flags |= telegram_api::messages_discardEncryption::DELETE_HISTORY_MASK; + } + auto query = create_net_query(QueryType::DiscardEncryption, + telegram_api::messages_discardEncryption(flags, false /*ignored*/, auth_state_.id)); send_update_secret_chat(); diff --git a/td/telegram/SecretChatActor.h b/td/telegram/SecretChatActor.h index 6560ad85f..01dd491ff 100644 --- a/td/telegram/SecretChatActor.h +++ b/td/telegram/SecretChatActor.h @@ -112,10 +112,11 @@ class SecretChatActor : public NetQueryCallback { SecretChatActor(int32 id, unique_ptr context, bool can_be_empty); - // First query to new chat must be on of these two + // First query to new chat must be one of these two void update_chat(telegram_api::object_ptr chat); void create_chat(int32 user_id, int64 user_access_hash, int32 random_id, Promise promise); - void cancel_chat(Promise<> promise); + + void cancel_chat(bool delete_history, Promise<> promise); // Inbound messages // Logevent is created by SecretChatsManager, because it must contain qts @@ -637,7 +638,7 @@ class SecretChatActor : public NetQueryCallback { // DiscardEncryption void on_fatal_error(Status status); - void do_close_chat_impl(unique_ptr event); + void do_close_chat_impl(bool delete_history, uint64 log_event_id); void on_discard_encryption_result(NetQueryPtr result); // Other diff --git a/td/telegram/SecretChatsManager.cpp b/td/telegram/SecretChatsManager.cpp index e82b0b806..a4dd084e9 100644 --- a/td/telegram/SecretChatsManager.cpp +++ b/td/telegram/SecretChatsManager.cpp @@ -106,10 +106,10 @@ void SecretChatsManager::create_chat(int32 user_id, int64 user_access_hash, Prom send_closure(actor, &SecretChatActor::create_chat, user_id, user_access_hash, random_id, std::move(promise)); } -void SecretChatsManager::cancel_chat(SecretChatId secret_chat_id, Promise<> promise) { +void SecretChatsManager::cancel_chat(SecretChatId secret_chat_id, bool delete_history, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Unit()); - send_closure(actor, &SecretChatActor::cancel_chat, std::move(safe_promise)); + send_closure(actor, &SecretChatActor::cancel_chat, delete_history, std::move(safe_promise)); } void SecretChatsManager::send_message(SecretChatId secret_chat_id, tl_object_ptr message, @@ -235,8 +235,9 @@ void SecretChatsManager::replay_binlog_event(BinlogEvent &&binlog_event) { case log_event::SecretChatEvent::Type::CreateSecretChat: return replay_create_chat( unique_ptr(static_cast(message.release()))); + default: + LOG(FATAL) << "Unknown log event type " << tag("type", format::as_hex(static_cast(message->get_type()))); } - LOG(FATAL) << "Unknown log event type " << tag("type", format::as_hex(static_cast(message->get_type()))); } void SecretChatsManager::binlog_replay_finish() { diff --git a/td/telegram/SecretChatsManager.h b/td/telegram/SecretChatsManager.h index 655e9545d..04bf59870 100644 --- a/td/telegram/SecretChatsManager.h +++ b/td/telegram/SecretChatsManager.h @@ -30,13 +30,12 @@ class SecretChatsManager : public Actor { public: explicit SecretChatsManager(ActorShared<> parent); - // Proxy query to corrensponding SecretChatActor. - // Look for more info in SecretChatActor.h + // Proxy query to corrensponding SecretChatActor void on_update_chat(tl_object_ptr update); void on_new_message(tl_object_ptr &&message_ptr, Promise &&promise); void create_chat(int32 user_id, int64 user_access_hash, Promise promise); - void cancel_chat(SecretChatId, Promise<> promise); + void cancel_chat(SecretChatId secret_chat_id, bool delete_history, Promise<> promise); void send_message(SecretChatId secret_chat_id, tl_object_ptr message, tl_object_ptr file, Promise<> promise); void send_message_action(SecretChatId secret_chat_id, tl_object_ptr action); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index b4c319938..19cd4444b 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6676,7 +6676,7 @@ void Td::on_request(uint64 id, const td_api::deleteSupergroup &request) { void Td::on_request(uint64 id, td_api::closeSecretChat &request) { CREATE_OK_REQUEST_PROMISE(); - send_closure(secret_chats_manager_, &SecretChatsManager::cancel_chat, SecretChatId(request.secret_chat_id_), + send_closure(secret_chats_manager_, &SecretChatsManager::cancel_chat, SecretChatId(request.secret_chat_id_), false, std::move(promise)); } diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 47ca2e19b..f9ed0e73d 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1643,7 +1643,7 @@ void UpdatesManager::add_pending_qts_update(tl_object_ptr int32 old_qts = get_qts(); LOG(INFO) << "Process update with qts = " << qts << ", current qts = " << old_qts; - if (qts < old_qts - 1000001) { + if (qts < old_qts - 10001) { LOG(WARNING) << "Restore qts after qts overflow from " << old_qts << " to " << qts << " by " << oneline(to_string(update)); add_qts(qts - 1).set_value(Unit()); diff --git a/td/telegram/logevent/SecretChatEvent.h b/td/telegram/logevent/SecretChatEvent.h index c65b5d521..c69bdf46e 100644 --- a/td/telegram/logevent/SecretChatEvent.h +++ b/td/telegram/logevent/SecretChatEvent.h @@ -34,12 +34,8 @@ class SecretChatEvent : public LogEventBase { virtual Type get_type() const = 0; - static constexpr LogEvent::HandlerType get_handler_type() { - return LogEvent::HandlerType::SecretChats; - } - static constexpr int32 version() { - return 2; + return 3; } template @@ -354,7 +350,7 @@ class OutboundSecretMessage : public SecretChatLogEventBase(action); + bool has_action = action != nullptr; BEGIN_STORE_FLAGS(); STORE_FLAG(is_sent); STORE_FLAG(need_notify_user); @@ -414,21 +410,31 @@ class CloseSecretChat : public SecretChatLogEventBase { public: static constexpr Type type = SecretChatEvent::Type::CloseSecretChat; int32 chat_id = 0; + bool delete_history = false; template void store(StorerT &storer) const { using td::store; + BEGIN_STORE_FLAGS(); + STORE_FLAG(delete_history); + END_STORE_FLAGS(); store(chat_id, storer); } template void parse(ParserT &parser) { using td::parse; + if (parser.version() >= 3) { + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(delete_history); + END_PARSE_FLAGS(); + } parse(chat_id, parser); } StringBuilder &print(StringBuilder &sb) const override { - return sb << "[Logevent CloseSecretChat " << tag("id", log_event_id()) << tag("chat_id", chat_id) << "]"; + return sb << "[Logevent CloseSecretChat " << tag("id", log_event_id()) << tag("chat_id", chat_id) + << tag("delete_history", delete_history) << "]"; } }; From 091d08b6a9627645bedfa557530f25bbbb3c56b7 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 19:02:24 +0300 Subject: [PATCH 049/232] Improve encryptedChatDiscarded handling. --- td/telegram/SecretChatActor.cpp | 76 ++++++++++++++++---------- td/telegram/SecretChatActor.h | 8 +-- td/telegram/SecretChatsManager.cpp | 2 +- td/telegram/logevent/SecretChatEvent.h | 5 +- 4 files changed, 57 insertions(+), 34 deletions(-) diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index 13ffbf93f..3e1b32ab1 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -115,7 +115,7 @@ void SecretChatActor::on_result_resendable(NetQueryPtr net_query, Promiseid()); if (close_flag_) { if (key == static_cast(QueryType::DiscardEncryption)) { - on_discard_encryption_result(std::move(net_query)); + discard_encryption_promise_.set_value(Unit()); } return; } @@ -140,7 +140,7 @@ void SecretChatActor::on_result_resendable(NetQueryPtr net_query, Promise event) { - do_close_chat_impl(event->delete_history, event->log_event_id()); + do_close_chat_impl(event->delete_history, event->is_already_discarded, event->log_event_id(), Promise()); } void SecretChatActor::replay_create_chat(unique_ptr event) { @@ -709,10 +709,10 @@ void SecretChatActor::check_status(Status status) { void SecretChatActor::on_fatal_error(Status status) { LOG(ERROR) << "Fatal error: " << status; - cancel_chat(false, Promise<>()); + cancel_chat(false, false, Promise<>()); } -void SecretChatActor::cancel_chat(bool delete_history, Promise<> promise) { +void SecretChatActor::cancel_chat(bool delete_history, bool is_already_discarded, Promise<> promise) { if (close_flag_) { promise.set_value(Unit()); return; @@ -737,11 +737,11 @@ void SecretChatActor::cancel_chat(bool delete_history, Promise<> promise) { event->chat_id = auth_state_.id; auto log_event_id = binlog_add(context_->binlog(), LogEvent::HandlerType::SecretChats, create_storer(*event)); - auto on_sync = PromiseCreator::lambda([actor_id = actor_id(this), delete_history, log_event_id, + auto on_sync = PromiseCreator::lambda([actor_id = actor_id(this), delete_history, is_already_discarded, log_event_id, promise = std::move(promise)](Result result) mutable { if (result.is_ok()) { - send_closure(actor_id, &SecretChatActor::do_close_chat_impl, delete_history, log_event_id); - promise.set_value(Unit()); + send_closure(actor_id, &SecretChatActor::do_close_chat_impl, delete_history, is_already_discarded, log_event_id, + std::move(promise)); } else { promise.set_error(result.error().clone()); send_closure(actor_id, &SecretChatActor::on_promise_error, result.move_as_error(), "cancel_chat"); @@ -752,25 +752,56 @@ void SecretChatActor::cancel_chat(bool delete_history, Promise<> promise) { yield(); } -void SecretChatActor::do_close_chat_impl(bool delete_history, uint64 log_event_id) { +void SecretChatActor::do_close_chat_impl(bool delete_history, bool is_already_discarded, uint64 log_event_id, + Promise &&promise) { close_flag_ = true; - close_log_event_id_ = log_event_id; - LOG(INFO) << "Send messages.discardEncryption"; auth_state_.state = State::Closed; context_->secret_chat_db()->set_value(auth_state_); context_->secret_chat_db()->erase_value(config_state_); context_->secret_chat_db()->erase_value(pfs_state_); context_->secret_chat_db()->erase_value(seq_no_state_); - int32 flags = 0; + + MultiPromiseActorSafe mpas{"DeleteMessagesFromServerMultiPromiseActor"}; + mpas.add_promise( + PromiseCreator::lambda([actor_id = actor_id(this), log_event_id, promise = std::move(promise)](Unit) mutable { + send_closure(actor_id, &SecretChatActor::on_closed, log_event_id, std::move(promise)); + })); + + auto lock = mpas.get_promise(); + if (delete_history) { - flags |= telegram_api::messages_discardEncryption::DELETE_HISTORY_MASK; + context_->on_flush_history(MessageId::max(), mpas.get_promise()); } - auto query = create_net_query(QueryType::DiscardEncryption, - telegram_api::messages_discardEncryption(flags, false /*ignored*/, auth_state_.id)); send_update_secret_chat(); - context_->send_net_query(std::move(query), actor_shared(this), true); + if (!is_already_discarded) { + int32 flags = 0; + if (delete_history) { + flags |= telegram_api::messages_discardEncryption::DELETE_HISTORY_MASK; + } + auto query = create_net_query(QueryType::DiscardEncryption, + telegram_api::messages_discardEncryption(flags, false /*ignored*/, auth_state_.id)); + query->total_timeout_limit_ = 60 * 60 * 24 * 365; + context_->send_net_query(std::move(query), actor_shared(this), true); + discard_encryption_promise_ = mpas.get_promise(); + } + + lock.set_value(Unit()); +} + +void SecretChatActor::on_closed(uint64 log_event_id, Promise &&promise) { + CHECK(close_flag_); + if (context_->close_flag()) { + return; + } + + LOG(INFO) << "Finish closing"; + context_->secret_chat_db()->erase_value(auth_state_); + binlog_erase(context_->binlog(), log_event_id); + promise.set_value(Unit()); + // skip flush + stop(); } void SecretChatActor::do_create_chat_impl(unique_ptr event) { @@ -793,18 +824,6 @@ void SecretChatActor::do_create_chat_impl(unique_ptrclose_flag()) { - return; - } - LOG(INFO) << "Got result for messages.discardEncryption"; - context_->secret_chat_db()->erase_value(auth_state_); - binlog_erase(context_->binlog(), close_log_event_id_); - // skip flush - stop(); -} telegram_api::object_ptr SecretChatActor::get_input_user() { return telegram_api::make_object(auth_state_.user_id, auth_state_.user_access_hash); @@ -1904,7 +1923,8 @@ Status SecretChatActor::on_update_chat(telegram_api::encryptedChat &update) { return Status::OK(); } Status SecretChatActor::on_update_chat(telegram_api::encryptedChatDiscarded &update) { - return Status::Error("Chat discarded"); + cancel_chat(update.history_deleted_, true, Promise()); + return Status::OK(); } Status SecretChatActor::on_update_chat(NetQueryPtr query) { diff --git a/td/telegram/SecretChatActor.h b/td/telegram/SecretChatActor.h index 01dd491ff..f2ff16a62 100644 --- a/td/telegram/SecretChatActor.h +++ b/td/telegram/SecretChatActor.h @@ -116,7 +116,7 @@ class SecretChatActor : public NetQueryCallback { void update_chat(telegram_api::object_ptr chat); void create_chat(int32 user_id, int64 user_access_hash, int32 random_id, Promise promise); - void cancel_chat(bool delete_history, Promise<> promise); + void cancel_chat(bool delete_history, bool is_already_discarded, Promise<> promise); // Inbound messages // Logevent is created by SecretChatsManager, because it must contain qts @@ -462,7 +462,7 @@ class SecretChatActor : public NetQueryCallback { bool binlog_replay_finish_flag_ = false; bool close_flag_ = false; - LogEvent::Id close_log_event_id_ = 0; + Promise discard_encryption_promise_; LogEvent::Id create_log_event_id_ = 0; @@ -638,8 +638,8 @@ class SecretChatActor : public NetQueryCallback { // DiscardEncryption void on_fatal_error(Status status); - void do_close_chat_impl(bool delete_history, uint64 log_event_id); - void on_discard_encryption_result(NetQueryPtr result); + void do_close_chat_impl(bool delete_history, bool is_already_discarded, uint64 log_event_id, Promise &&promise); + void on_closed(uint64 log_event_id, Promise &&promise); // Other template diff --git a/td/telegram/SecretChatsManager.cpp b/td/telegram/SecretChatsManager.cpp index a4dd084e9..6176b3586 100644 --- a/td/telegram/SecretChatsManager.cpp +++ b/td/telegram/SecretChatsManager.cpp @@ -109,7 +109,7 @@ void SecretChatsManager::create_chat(int32 user_id, int64 user_access_hash, Prom void SecretChatsManager::cancel_chat(SecretChatId secret_chat_id, bool delete_history, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Unit()); - send_closure(actor, &SecretChatActor::cancel_chat, delete_history, std::move(safe_promise)); + send_closure(actor, &SecretChatActor::cancel_chat, delete_history, false, std::move(safe_promise)); } void SecretChatsManager::send_message(SecretChatId secret_chat_id, tl_object_ptr message, diff --git a/td/telegram/logevent/SecretChatEvent.h b/td/telegram/logevent/SecretChatEvent.h index c69bdf46e..2c8f241f2 100644 --- a/td/telegram/logevent/SecretChatEvent.h +++ b/td/telegram/logevent/SecretChatEvent.h @@ -411,12 +411,14 @@ class CloseSecretChat : public SecretChatLogEventBase { static constexpr Type type = SecretChatEvent::Type::CloseSecretChat; int32 chat_id = 0; bool delete_history = false; + bool is_already_discarded = false; template void store(StorerT &storer) const { using td::store; BEGIN_STORE_FLAGS(); STORE_FLAG(delete_history); + STORE_FLAG(is_already_discarded); END_STORE_FLAGS(); store(chat_id, storer); } @@ -427,6 +429,7 @@ class CloseSecretChat : public SecretChatLogEventBase { if (parser.version() >= 3) { BEGIN_PARSE_FLAGS(); PARSE_FLAG(delete_history); + PARSE_FLAG(is_already_discarded); END_PARSE_FLAGS(); } parse(chat_id, parser); @@ -434,7 +437,7 @@ class CloseSecretChat : public SecretChatLogEventBase { StringBuilder &print(StringBuilder &sb) const override { return sb << "[Logevent CloseSecretChat " << tag("id", log_event_id()) << tag("chat_id", chat_id) - << tag("delete_history", delete_history) << "]"; + << tag("delete_history", delete_history) << tag("is_already_discarded", is_already_discarded) << "]"; } }; From d52c676912d0c78210a33e454542990c9b482851 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 19:32:53 +0300 Subject: [PATCH 050/232] Add getChatInviteLinks.is_revoked. --- td/generate/scheme/td_api.tl | 3 ++- td/generate/scheme/td_api.tlo | Bin 193824 -> 193860 bytes td/telegram/ContactsManager.cpp | 10 +++++++--- td/telegram/ContactsManager.h | 5 +++-- td/telegram/Td.cpp | 3 ++- td/telegram/cli.cpp | 4 ++-- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c157fdafc..5c04fa661 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4381,8 +4381,9 @@ createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 is_perma editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 is_revoked:Bool = ChatInviteLink; //@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned +//@is_revoked Pass true if revoked links needs to be returned instead of active or expired //@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning @limit Maximum number of invite links to return -getChatInviteLinks chat_id:int53 administrator_user_id:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; +getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool offset_invite_link:string limit:int32 = ChatInviteLinks; //@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @invite_link Invite link for which to return chat members //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 8e102873f176c9743a320c0ff5e5a306b4408c2c..28caa3a8af07be39b84fce0f54170853adde0052 100644 GIT binary patch delta 48 zcmV-00MGxR>I=l`3xI?Hv;x#=1V@&?o0r&W0vwklMgb_79BKkEmt0Z;Ad{eU1-DIV G0@I4@Qxeet delta 39 vcmX@|ihIE;?uHh|ElgK3S-d?T22H<_$s{v9messages_manager_->get_input_peer(dialog_id, AccessRights::Read); if (input_peer == nullptr) { @@ -1672,6 +1673,9 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { if (!offset_invite_link.empty()) { flags |= telegram_api::messages_getExportedChatInvites::OFFSET_LINK_MASK; } + if (is_revoked) { + flags |= telegram_api::messages_getExportedChatInvites::REVOKED_MASK; + } send_query(G()->net_query_creator().create(telegram_api::messages_getExportedChatInvites( flags, false /*ignored*/, std::move(input_peer), std::move(input_user), offset_invite_link, limit))); } @@ -7083,7 +7087,7 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & ->send(dialog_id, invite_link, expire_date, usage_limit, is_revoked); } -void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, +void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, const string &offset_invite_link, int32 limit, Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); @@ -7097,7 +7101,7 @@ void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId adminis } td_->create_handler(std::move(promise)) - ->send(dialog_id, administrator_user_id, offset_invite_link, limit); + ->send(dialog_id, administrator_user_id, is_revoked, offset_invite_link, limit); } void ContactsManager::get_dialog_invite_link_users( diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 2f5be621c..079b42c4c 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -398,8 +398,9 @@ class ContactsManager : public Actor { void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise); - void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, const string &offset_invite_link, - int32 limit, Promise> &&promise); + void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, + const string &offset_invite_link, int32 limit, + Promise> &&promise); void get_dialog_invite_link_users(DialogId dialog_id, const string &invite_link, td_api::object_ptr offset_member, int32 limit, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 19cd4444b..7cf30dfb4 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6292,7 +6292,8 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.offset_invite_link_); contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.administrator_user_id_), - request.offset_invite_link_, request.limit_, std::move(promise)); + request.is_revoked_, request.offset_invite_link_, request.limit_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index ac0ff3161..4d8e040f1 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2701,14 +2701,14 @@ class CliClient final : public Actor { get_args(args, chat_id, invite_link, expire_date, member_limit, is_revoked); send_request(td_api::make_object(as_chat_id(chat_id), invite_link, expire_date, member_limit, is_revoked)); - } else if (op == "gcil") { + } else if (op == "gcil" || op == "gcilr") { string chat_id; string administrator_user_id; string offset_invite_link; string limit; get_args(args, chat_id, administrator_user_id, offset_invite_link, limit); send_request(td_api::make_object( - as_chat_id(chat_id), as_user_id(administrator_user_id), offset_invite_link, as_limit(limit))); + as_chat_id(chat_id), as_user_id(administrator_user_id), op == "gcilr", offset_invite_link, as_limit(limit))); } else if (op == "gcilm") { string chat_id; string invite_link; From 93645ca29dca52d51761a46ed8e0e8d7736af6d7 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 19:51:46 +0300 Subject: [PATCH 051/232] Add td_api::deleteRevokedChatInviteLink. --- td/generate/scheme/td_api.tl | 3 +++ td/generate/scheme/td_api.tlo | Bin 193860 -> 193996 bytes td/telegram/ContactsManager.cpp | 41 ++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 2 ++ td/telegram/Td.cpp | 7 ++++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 5 ++++ 7 files changed, 60 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 5c04fa661..bbe70509f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4389,6 +4389,9 @@ getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool off //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return getChatInviteLinkMembers chat_id:int53 invite_link:string offset_member:chatInviteLinkMember limit:int32 = ChatInviteLinkMembers; +//@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @invite_link Invite link to revoke +deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; + //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 28caa3a8af07be39b84fce0f54170853adde0052..74ceaea203bc221f9db645efda2858b9a6be0426 100644 GIT binary patch delta 75 zcmV-R0JQ(a>I=;43xI?Hv;zDn0hPD?C;|&4Ab$)eksD-XY-Mz1Qe}2;Yh`3ZXkm0o hZgy#OWlU*qYnLF<0x_5EQ34;A&?y2Kw+KxFJg9UL9Ebn_ delta 27 jcmX@}n)}Er?uHh|Elhtj7^iIitHH#rvVDRtlg$bMvK$L{ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 5c8cce35a..ef24281ee 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1776,6 +1776,40 @@ class GetChatInviteImportersQuery : public Td::ResultHandler { } }; +class DeleteExportedChatInviteQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit DeleteExportedChatInviteQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create( + telegram_api::messages_deleteExportedChatInvite(std::move(input_peer), invite_link))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "DeleteExportedChatInviteQuery"); + promise_.set_error(std::move(status)); + } +}; + class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -7124,6 +7158,13 @@ void ContactsManager::get_dialog_invite_link_users( ->send(dialog_id, invite_link, offset_date, offset_user_id, limit); } +void ContactsManager::delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); +} + void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { if (invite_link_infos_.count(invite_link) > 0) { return promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 079b42c4c..1c5becaba 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -406,6 +406,8 @@ class ContactsManager : public Actor { td_api::object_ptr offset_member, int32 limit, Promise> &&promise); + void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); + void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 7cf30dfb4..350d46a30 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6304,6 +6304,13 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { std::move(promise)); } +void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { + CREATE_OK_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.invite_link_); + contacts_manager_->delete_revoked_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, + std::move(promise)); +} + void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 724ce0055..47f2b8fb4 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -794,6 +794,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatInviteLinkMembers &request); + void on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request); + void on_request(uint64 id, td_api::checkChatInviteLink &request); void on_request(uint64 id, td_api::joinChatByInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 4d8e040f1..3eb1c1527 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2719,6 +2719,11 @@ class CliClient final : public Actor { send_request(td_api::make_object( as_chat_id(chat_id), invite_link, td_api::make_object(as_user_id(offset_user_id), offset_date), as_limit(limit))); + } else if (op == "drcil") { + string chat_id; + string invite_link; + get_args(args, chat_id, invite_link); + send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From 3f91df5a4757297271ce6d89149b1f0e0e4ca75e Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 19:59:21 +0300 Subject: [PATCH 052/232] Add td_api::deleteRevokedChatInviteLink. --- td/generate/scheme/td_api.tl | 13 +++++++---- td/generate/scheme/td_api.tlo | Bin 193996 -> 194100 bytes td/telegram/ContactsManager.cpp | 40 ++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 2 ++ td/telegram/Td.cpp | 5 ++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 3 +++ 7 files changed, 60 insertions(+), 5 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index bbe70509f..dc263f80a 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4365,14 +4365,14 @@ readFilePart file_id:int32 offset:int32 count:int32 = FilePart; deleteFile file_id:int32 = Ok; -//@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right +//@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited //@is_permanent True, if new permanent chat link needs to be created instead of the previous one createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 is_permanent:Bool = ChatInviteLink; -//@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right +//@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier //@invite_link Invite link to be edited; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never @@ -4380,18 +4380,21 @@ createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 is_perma //@is_revoked True, if the link is revoked editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 is_revoked:Bool = ChatInviteLink; -//@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned +//@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned //@is_revoked Pass true if revoked links needs to be returned instead of active or expired //@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning @limit Maximum number of invite links to return getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool offset_invite_link:string limit:int32 = ChatInviteLinks; -//@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @invite_link Invite link for which to return chat members +//@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @invite_link Invite link for which to return chat members //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return getChatInviteLinkMembers chat_id:int53 invite_link:string offset_member:chatInviteLinkMember limit:int32 = ChatInviteLinkMembers; -//@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right @chat_id Chat identifier @invite_link Invite link to revoke +//@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @invite_link Invite link to revoke deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; +//@description Deletes all revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier +deleteAllRevokedChatInviteLinks chat_id:int53 = Ok; + //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 74ceaea203bc221f9db645efda2858b9a6be0426..c979f0e39e4a3517929aa85d3f8c65d0d6628831 100644 GIT binary patch delta 69 zcmV-L0J{Io>kG8(3xI?Hv;zDn0hYJ@C<4D9BxfOx$sc58Y-Mz1L2PVNWp;0CWn@EW bVRT7uc4>5FOlfXwbC=MX0z|jGNCFV83>6&D delta 27 jcmdn;hWpHG?uHh|Elhtj7^iOktHHEie!Ge%lfYU4vycmd diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index ef24281ee..e2d4e12be 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1810,6 +1810,40 @@ class DeleteExportedChatInviteQuery : public Td::ResultHandler { } }; +class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit DeleteRevokedExportedChatInvitesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create( + telegram_api::messages_deleteRevokedExportedChatInvites(std::move(input_peer)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "DeleteRevokedExportedChatInvitesQuery"); + promise_.set_error(std::move(status)); + } +}; + class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -7165,6 +7199,12 @@ void ContactsManager::delete_revoked_dialog_invite_link(DialogId dialog_id, cons td_->create_handler(std::move(promise))->send(dialog_id, invite_link); } +void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, Promise &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + td_->create_handler(std::move(promise))->send(dialog_id); +} + void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { if (invite_link_infos_.count(invite_link) > 0) { return promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 1c5becaba..66b3dc850 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -408,6 +408,8 @@ class ContactsManager : public Actor { void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); + void delete_all_revoked_dialog_invite_links(DialogId dialog_id, Promise &&promise); + void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 350d46a30..4e62aca29 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6311,6 +6311,11 @@ void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { std::move(promise)); } +void Td::on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request) { + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), std::move(promise)); +} + void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 47f2b8fb4..39a19d146 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -796,6 +796,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request); + void on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request); + void on_request(uint64 id, td_api::checkChatInviteLink &request); void on_request(uint64 id, td_api::joinChatByInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 3eb1c1527..3e1f56e22 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2724,6 +2724,9 @@ class CliClient final : public Actor { string invite_link; get_args(args, chat_id, invite_link); send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); + } else if (op == "darcil") { + string chat_id = args; + send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From 840c1ba8f95c7bd8bf69bb0661fb60833877525d Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 20:29:02 +0300 Subject: [PATCH 053/232] Ignore *FullInfo without invite link. --- td/telegram/ContactsManager.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index e2d4e12be..878848d72 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -9259,6 +9259,13 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val Chat *c = get_chat(chat_id); CHECK(c != nullptr); + + // ignore ChatFull without invite link + if (c->is_active && c->status.is_administrator() && !chat_full->invite_link.is_valid()) { + chats_full_.erase(chat_id); + return; + } + if (td_->file_manager_->get_file_view(c->photo.small_file_id).get_unique_file_id() != td_->file_manager_->get_file_view(as_fake_dialog_photo(chat_full->photo).small_file_id).get_unique_file_id()) { chat_full->photo = Photo(); @@ -9347,6 +9354,13 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s Channel *c = get_channel(channel_id); CHECK(c != nullptr); + + // ignore ChannelFull without invite link + if (c->status.is_administrator() && c->status.can_invite_users() && !channel_full->invite_link.is_valid()) { + channels_full_.erase(channel_id); + return; + } + if (td_->file_manager_->get_file_view(c->photo.small_file_id).get_unique_file_id() != td_->file_manager_->get_file_view(as_fake_dialog_photo(channel_full->photo).small_file_id).get_unique_file_id()) { channel_full->photo = Photo(); @@ -13447,7 +13461,7 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha } } - if (c->status.is_creator() && !chat_full->invite_link.is_valid()) { + if (c->is_active && c->status.is_administrator() && !chat_full->invite_link.is_valid()) { LOG(INFO) << "Have outdated invite link in " << chat_id; return true; } From 936dbbb254a9de2a5e32905bfa4d932c8e68b82e Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 22:35:28 +0300 Subject: [PATCH 054/232] Add separate method for revoking chat invite links. --- td/generate/scheme/td_api.tl | 10 +++++++--- td/generate/scheme/td_api.tlo | Bin 194100 -> 194196 bytes td/telegram/ContactsManager.cpp | 9 ++------- td/telegram/Td.cpp | 9 ++++++++- td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 12 ++++++++---- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index dc263f80a..a8dd28e8f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4374,11 +4374,15 @@ createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 is_perma //@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier -//@invite_link Invite link to be edited; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" +//@invite_link Invite link to be edited //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited -//@is_revoked True, if the link is revoked -editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 is_revoked:Bool = ChatInviteLink; +editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; + +//@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat +//@chat_id Chat identifier +//@invite_link Invite link to be edited +revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLink; //@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned //@is_revoked Pass true if revoked links needs to be returned instead of active or expired diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index c979f0e39e4a3517929aa85d3f8c65d0d6628831..cf89e826fef9a3e65ef73f5b605e40cf39921133 100644 GIT binary patch delta 93 zcmdn;hI`6e?uHh|Elhtj7^iRltHCr~ljZQQclW2y2x5|%p7NPdbNbyNrtIkkGZ{Iz xmj*L!j^p~M5y31{lv 0) { - flags |= telegram_api::messages_editExportedChatInvite::EXPIRE_DATE_MASK; - } - if (usage_limit > 0) { - flags |= telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK; - } + int32 flags = telegram_api::messages_editExportedChatInvite::EXPIRE_DATE_MASK | + telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK; if (is_revoked) { flags |= telegram_api::messages_editExportedChatInvite::REVOKED_MASK; } diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 4e62aca29..aca996fd7 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6285,7 +6285,14 @@ void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.invite_link_); contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, request.expire_date_, - request.member_limit_, request.is_revoked_, std::move(promise)); + request.member_limit_, false, std::move(promise)); +} + +void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { + CREATE_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.invite_link_); + contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, 0, 0, true, + std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 39a19d146..d824810a0 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -790,6 +790,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::editChatInviteLink &request); + void on_request(uint64 id, td_api::revokeChatInviteLink &request); + void on_request(uint64 id, td_api::getChatInviteLinks &request); void on_request(uint64 id, td_api::getChatInviteLinkMembers &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 3e1f56e22..a28bf8feb 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2697,10 +2697,14 @@ class CliClient final : public Actor { string invite_link; int32 expire_date; int32 member_limit; - bool is_revoked; - get_args(args, chat_id, invite_link, expire_date, member_limit, is_revoked); - send_request(td_api::make_object(as_chat_id(chat_id), invite_link, expire_date, - member_limit, is_revoked)); + get_args(args, chat_id, invite_link, expire_date, member_limit); + send_request( + td_api::make_object(as_chat_id(chat_id), invite_link, expire_date, member_limit)); + } else if (op == "rcil") { + string chat_id; + string invite_link; + get_args(args, chat_id, invite_link); + send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); } else if (op == "gcil" || op == "gcilr") { string chat_id; string administrator_user_id; From 3ff68546ab068f55e0c4a50e008f0c7414fd4b42 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 18 Jan 2021 23:04:56 +0300 Subject: [PATCH 055/232] Add separate td_api::replacePermanentChatInviteLink method. --- td/generate/scheme/td_api.tl | 22 ++++++++++++---------- td/generate/scheme/td_api.tlo | Bin 194196 -> 194260 bytes td/telegram/Td.cpp | 7 ++++++- td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 8 +++++--- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index a8dd28e8f..ee49afec9 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -551,7 +551,7 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@expire_date Point in time (Unix timestamp) when the link will expire; 0 if never //@member_limit Maximum number of members, which can join the chat using the link simultaneously; 0 if not limited //@member_count Number of chat members, which joined the chat using the link -//@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator at any time +//@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator with can_invite_users right at a given time //@is_expired True, if the link is already expired @is_revoked True, if the link was revoked chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; @@ -627,7 +627,7 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co //@is_all_history_available True, if new chat members will have access to old messages. In public or discussion groups and both public and private channels, old messages are always available, so this option affects only private supergroups without a linked chat. The value of this field is only available for chat administrators //@sticker_set_id Identifier of the supergroup sticker set; 0 if none //@location Location to which the supergroup is connected; may be null -//@invite_link Permanent invite link for this chat; for chat administrators only +//@invite_link Permanent invite link for this chat; for chat administrators with can_invite_users right only //@upgraded_from_basic_group_id Identifier of the basic group from which supergroup was upgraded; 0 if none //@upgraded_from_max_message_id Identifier of the last message in the basic group from which supergroup was upgraded; 0 if none supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_get_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:chatInviteLink upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo; @@ -4365,25 +4365,22 @@ readFilePart file_id:int32 offset:int32 count:int32 = FilePart; deleteFile file_id:int32 = Ok; +//@description Replaces current permanent invite link for a chat with a new permanent invite link. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right @chat_id Chat identifier +replacePermanentChatInviteLink chat_id:int53 = ChatInviteLink; + //@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited -//@is_permanent True, if new permanent chat link needs to be created instead of the previous one -createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 is_permanent:Bool = ChatInviteLink; +createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatInviteLink; -//@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat +//@description Edits a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier //@invite_link Invite link to be edited //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; -//@description Edits an invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat -//@chat_id Chat identifier -//@invite_link Invite link to be edited -revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLink; - //@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned //@is_revoked Pass true if revoked links needs to be returned instead of active or expired //@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning @limit Maximum number of invite links to return @@ -4393,6 +4390,11 @@ getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool off //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return getChatInviteLinkMembers chat_id:int53 invite_link:string offset_member:chatInviteLinkMember limit:int32 = ChatInviteLinkMembers; +//@description Revokes a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat +//@chat_id Chat identifier +//@invite_link Invite link to be revoked +revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLink; + //@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @invite_link Invite link to revoke deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index cf89e826fef9a3e65ef73f5b605e40cf39921133..762ffb03685c6920593772f43bdf77f1b35f58fb 100644 GIT binary patch delta 105 zcmV-v0G9uh?F-cH3xI?Hv;zDn0hqV_C<2oi3jQsFFBD^PWnpxe4zdComlR9_DVNexport_dialog_invite_link(DialogId(request.chat_id_), 0, 0, true, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.member_limit_, - request.is_permanent_, std::move(promise)); + false, std::move(promise)); } void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index d824810a0..969797730 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -786,6 +786,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatAdministrators &request); + void on_request(uint64 id, const td_api::replacePermanentChatInviteLink &request); + void on_request(uint64 id, const td_api::createChatInviteLink &request); void on_request(uint64 id, td_api::editChatInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index a28bf8feb..d91b801ee 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2685,13 +2685,15 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_group_call_id(args))); } else if (op == "dgc") { send_request(td_api::make_object(as_group_call_id(args))); - } else if (op == "ccilp" || op == "ccilt") { + } else if (op == "rpcil") { + string chat_id = args; + send_request(td_api::make_object(as_chat_id(chat_id))); + } else if (op == "ccilt") { string chat_id; int32 expire_date; int32 member_limit; get_args(args, chat_id, expire_date, member_limit); - send_request(td_api::make_object(as_chat_id(chat_id), expire_date, member_limit, - op == "ccilp")); + send_request(td_api::make_object(as_chat_id(chat_id), expire_date, member_limit)); } else if (op == "ecil") { string chat_id; string invite_link; From 4fa29ab1b8d9a247d0174e657e502fb3c2e14b60 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 19 Jan 2021 13:21:50 +0300 Subject: [PATCH 056/232] Add deleteAllCallMessages method. --- td/generate/scheme/td_api.tl | 3 + td/generate/scheme/td_api.tlo | Bin 194260 -> 194356 bytes td/telegram/MessagesManager.cpp | 100 ++++++++++++++++++++++++++++++++ td/telegram/MessagesManager.h | 7 +++ td/telegram/Td.cpp | 6 ++ td/telegram/Td.h | 2 + td/telegram/TdDb.cpp | 1 + td/telegram/cli.cpp | 3 + td/telegram/logevent/LogEvent.h | 1 + 9 files changed, 123 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index ee49afec9..4029464f6 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3859,6 +3859,9 @@ searchSecretMessages chat_id:int53 query:string offset:string limit:int32 filter //@limit The maximum number of messages to be returned; up to 100. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached @only_missed If true, returns only messages with missed calls searchCallMessages from_message_id:int53 limit:int32 only_missed:Bool = Messages; +//@description Deletes all call messages @revoke Pass true to delete the messages for all users +deleteAllCallMessages revoke:Bool = Ok; + //@description Returns information about the recent locations of chat members that were sent to the chat. Returns up to 1 location message per user @chat_id Chat identifier @limit The maximum number of messages to be returned searchChatRecentLocationMessages chat_id:int53 limit:int32 = Messages; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 762ffb03685c6920593772f43bdf77f1b35f58fb..017b3e174c3622c2ba18399ff18eb86f671716ef 100644 GIT binary patch delta 56 zcmV-80LTB-?F+Q-3xI?Hv;zDn0hzb`C<2!s67$iQs1;;oY-Mz1L2PV8VQg%diar7w Om)J=HG`EyU0zj_MiWj8- delta 27 jcmdn;j{C}6?uHh|Elhtj7-ww%tHCr~e)|^>CWmzZxCRVq diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 5555c69a7..8762d5fa5 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -2303,6 +2303,59 @@ class DeleteChannelHistoryQuery : public Td::ResultHandler { } }; +class DeletePhoneCallHistoryQuery : public Td::ResultHandler { + Promise promise_; + bool revoke_; + + void send_request() { + int32 flags = 0; + if (revoke_) { + flags |= telegram_api::messages_deletePhoneCallHistory::REVOKE_MASK; + } + send_query( + G()->net_query_creator().create(telegram_api::messages_deletePhoneCallHistory(flags, false /*ignored*/))); + } + + public: + explicit DeletePhoneCallHistoryQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(bool revoke) { + revoke_ = revoke; + + send_request(); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto affected_history = result_ptr.move_as_ok(); + CHECK(affected_history->get_id() == telegram_api::messages_affectedHistory::ID); + + if (affected_history->pts_count_ > 0) { + affected_history->pts_count_ = 0; // force receiving real updates from the server + auto promise = affected_history->offset_ > 0 ? Promise() : std::move(promise_); + td->updates_manager_->add_pending_pts_update(make_tl_object(), affected_history->pts_, + affected_history->pts_count_, std::move(promise), + "delete phone call history query"); + } else if (affected_history->offset_ <= 0) { + promise_.set_value(Unit()); + } + + if (affected_history->offset_ > 0) { + send_request(); + return; + } + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class BlockFromRepliesQuery : public Td::ResultHandler { Promise promise_; @@ -10075,6 +10128,46 @@ void MessagesManager::delete_dialog_history_from_server(DialogId dialog_id, Mess } } +void MessagesManager::delete_all_call_messages(bool revoke, Promise &&promise) { + delete_all_call_messages_from_server(revoke, 0, std::move(promise)); +} + +class MessagesManager::DeleteAllCallMessagesFromServerLogEvent { + public: + bool revoke_; + + template + void store(StorerT &storer) const { + BEGIN_STORE_FLAGS(); + STORE_FLAG(revoke_); + END_STORE_FLAGS(); + } + + template + void parse(ParserT &parser) { + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(revoke_); + END_PARSE_FLAGS(); + } +}; + +uint64 MessagesManager::save_delete_all_call_messages_from_server_log_event(bool revoke) { + DeleteAllCallMessagesFromServerLogEvent log_event{revoke}; + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteAllCallMessagesFromServer, + get_log_event_storer(log_event)); +} + +void MessagesManager::delete_all_call_messages_from_server(bool revoke, uint64 log_event_id, Promise &&promise) { + if (log_event_id == 0) { + log_event_id = save_delete_all_call_messages_from_server_log_event(revoke); + } + + auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise)); + promise = std::move(new_promise); // to prevent self-move + + td_->create_handler(std::move(promise))->send(revoke); +} + void MessagesManager::find_messages(const Message *m, vector &message_ids, const std::function &condition) { if (m == nullptr) { @@ -35568,6 +35661,13 @@ void MessagesManager::on_binlog_events(vector &&events) { log_event.revoke_, true, event.id_, Auto()); break; } + case LogEvent::HandlerType::DeleteAllCallMessagesFromServer: { + DeleteAllCallMessagesFromServerLogEvent log_event; + log_event_parse(log_event, event.data_).ensure(); + + delete_all_call_messages_from_server(log_event.revoke_, event.id_, Auto()); + break; + } case LogEvent::HandlerType::BlockMessageSenderFromRepliesOnServer: { BlockMessageSenderFromRepliesOnServerLogEvent log_event; log_event_parse(log_event, event.data_).ensure(); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 4b4c06c81..ff79e01bb 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -338,6 +338,8 @@ class MessagesManager : public Actor { void delete_dialog_history(DialogId dialog_id, bool remove_from_dialog_list, bool revoke, Promise &&promise); + void delete_all_call_messages(bool revoke, Promise &&promise); + void delete_dialog_messages_from_user(DialogId dialog_id, UserId user_id, Promise &&promise); void delete_dialog(DialogId dialog_id); @@ -1591,6 +1593,7 @@ class MessagesManager : public Actor { class ChangeDialogReportSpamStateOnServerLogEvent; class DeleteAllChannelMessagesFromUserOnServerLogEvent; class DeleteDialogHistoryFromServerLogEvent; + class DeleteAllCallMessagesFromServerLogEvent; class DeleteMessageLogEvent; class DeleteMessagesFromServerLogEvent; class DeleteScheduledMessagesFromServerLogEvent; @@ -1888,6 +1891,8 @@ class MessagesManager : public Actor { void delete_dialog_history_from_server(DialogId dialog_id, MessageId max_message_id, bool remove_from_dialog_list, bool revoke, bool allow_error, uint64 log_event_id, Promise &&promise); + void delete_all_call_messages_from_server(bool revoke, uint64 log_event_id, Promise &&promise); + void block_message_sender_from_replies_on_server(MessageId message_id, bool delete_message, bool delete_all_messages, bool report_spam, uint64 log_event_id, Promise &&promise); @@ -2884,6 +2889,8 @@ class MessagesManager : public Actor { uint64 save_delete_dialog_history_from_server_log_event(DialogId dialog_id, MessageId max_message_id, bool remove_from_dialog_list, bool revoke); + uint64 save_delete_all_call_messages_from_server_log_event(bool revoke); + uint64 save_block_message_sender_from_replies_on_server_log_event(MessageId message_id, bool delete_message, bool delete_all_messages, bool report_spam); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index bce983c5a..ab419916e 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5511,6 +5511,12 @@ void Td::on_request(uint64 id, td_api::searchCallMessages &request) { CREATE_REQUEST(SearchCallMessagesRequest, request.from_message_id_, request.limit_, request.only_missed_); } +void Td::on_request(uint64 id, const td_api::deleteAllCallMessages &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->delete_all_call_messages(request.revoke_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::searchChatRecentLocationMessages &request) { CHECK_IS_USER(); CREATE_REQUEST(SearchChatRecentLocationMessagesRequest, request.chat_id_, request.limit_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 969797730..7e690f5b6 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -588,6 +588,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::searchCallMessages &request); + void on_request(uint64 id, const td_api::deleteAllCallMessages &request); + void on_request(uint64 id, const td_api::searchChatRecentLocationMessages &request); void on_request(uint64 id, const td_api::getActiveLiveLocationMessages &request); diff --git a/td/telegram/TdDb.cpp b/td/telegram/TdDb.cpp index 5092dfdd8..febb18026 100644 --- a/td/telegram/TdDb.cpp +++ b/td/telegram/TdDb.cpp @@ -116,6 +116,7 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue &binlog_p case LogEvent::HandlerType::ReadMessageThreadHistoryOnServer: case LogEvent::HandlerType::BlockMessageSenderFromRepliesOnServer: case LogEvent::HandlerType::UnpinAllDialogMessagesOnServer: + case LogEvent::HandlerType::DeleteAllCallMessagesFromServer: events.to_messages_manager.push_back(event.clone()); break; case LogEvent::HandlerType::AddMessagePushNotification: diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index d91b801ee..b04bc96bb 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2013,6 +2013,9 @@ class CliClient final : public Actor { get_args(args, limit, offset_message_id, only_missed); send_request(td_api::make_object(as_message_id(offset_message_id), as_limit(limit), only_missed)); + } else if (op == "DeleteAllCallMessages") { + bool revoke = as_bool(args); + send_request(td_api::make_object(revoke)); } else if (op == "SCRLM") { string chat_id; string limit; diff --git a/td/telegram/logevent/LogEvent.h b/td/telegram/logevent/LogEvent.h index 4e381f0a9..33aee717b 100644 --- a/td/telegram/logevent/LogEvent.h +++ b/td/telegram/logevent/LogEvent.h @@ -100,6 +100,7 @@ class LogEvent { ReadMessageThreadHistoryOnServer = 0x119, BlockMessageSenderFromRepliesOnServer = 0x120, UnpinAllDialogMessagesOnServer = 0x121, + DeleteAllCallMessagesFromServer = 0x122, GetChannelDifference = 0x140, AddMessagePushNotification = 0x200, EditMessagePushNotification = 0x201, From c7e4abb0a599437c3494b445ff168b1644049f6f Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 19 Jan 2021 14:10:25 +0300 Subject: [PATCH 057/232] Add deleteChat method support. --- td/telegram/ContactsManager.cpp | 47 +++++++++++++++++++++++++++++++-- td/telegram/ContactsManager.h | 2 ++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index a9d3dab14..7f3fc2055 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1441,6 +1441,34 @@ class ReportChannelSpamQuery : public Td::ResultHandler { } }; +class DeleteChatQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit DeleteChatQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChatId chat_id) { + send_query(G()->net_query_creator().create(telegram_api::messages_deleteChat(chat_id.get()))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + LOG(INFO) << "Receive result for DeleteChatQuery: " << result_ptr.ok(); + td->updates_manager_->get_difference("DeleteChatQuery"); + td->updates_manager_->on_get_updates(make_tl_object(Auto(), Auto(), Auto(), 0, 0), + std::move(promise_)); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class DeleteChannelQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; @@ -6669,13 +6697,28 @@ void ContactsManager::report_channel_spam(ChannelId channel_id, UserId user_id, td_->create_handler(std::move(promise))->send(channel_id, user_id, server_message_ids); } +void ContactsManager::delete_chat(ChatId chat_id, Promise &&promise) { + auto c = get_chat(chat_id); + if (c == nullptr) { + return promise.set_error(Status::Error(400, "Chat info not found")); + } + if (!get_chat_status(c).is_creator()) { + return promise.set_error(Status::Error(400, "Not enough rights to delete the chat")); + } + if (!c->is_active) { + return promise.set_error(Status::Error(400, "Chat is already deactivated")); + } + + td_->create_handler(std::move(promise))->send(chat_id); +} + void ContactsManager::delete_channel(ChannelId channel_id, Promise &&promise) { auto c = get_channel(channel_id); if (c == nullptr) { - return promise.set_error(Status::Error(6, "Supergroup not found")); + return promise.set_error(Status::Error(400, "Chat info not found")); } if (!get_channel_status(c).is_creator()) { - return promise.set_error(Status::Error(6, "Not enough rights to delete the supergroup")); + return promise.set_error(Status::Error(400, "Not enough rights to delete the chat")); } td_->create_handler(std::move(promise))->send(channel_id); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 66b3dc850..77894dfd8 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -379,6 +379,8 @@ class ContactsManager : public Actor { void add_channel_participants(ChannelId channel_id, const vector &user_ids, Promise &&promise); + void delete_chat(ChatId chat_id, Promise &&promise); + void change_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status, Promise &&promise); From dbf1253075433d314a8780f0e74304997dff9afa Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Jan 2021 14:49:18 +0300 Subject: [PATCH 058/232] Replace deleteSupergroup with universal deleteChat method. --- td/generate/scheme/td_api.tl | 6 +++--- td/generate/scheme/td_api.tlo | Bin 194356 -> 194340 bytes td/telegram/ContactsManager.cpp | 22 ++++++++++++++++++++++ td/telegram/ContactsManager.h | 8 +++++--- td/telegram/MessagesManager.cpp | 20 +++++++++++--------- td/telegram/MessagesManager.h | 7 +++++-- td/telegram/SecretChatActor.cpp | 5 +++-- td/telegram/SecretChatActor.h | 2 +- td/telegram/SecretChatsManager.cpp | 6 +++--- td/telegram/Td.cpp | 22 ++++++++++++++++------ td/telegram/Td.h | 4 ++-- td/telegram/cli.cpp | 4 ++-- test/secret.cpp | 4 ++-- 13 files changed, 75 insertions(+), 35 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 4029464f6..466cb779e 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3821,6 +3821,9 @@ getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 off //@chat_id Chat identifier @remove_from_chat_list Pass true if the chat should be removed from the chat list @revoke Pass true to try to delete chat history for all users deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok; +//@description Deletes a chat along with all messages in the corresponding chat for all chat members; requires owner privileges. For group chats this will release the username and remove all members. Chats with more than 1000 members can't be deleted using this method @chat_id Chat identifier +deleteChat chat_id:int53 = Ok; + //@description Searches for messages with given words in the chat. Returns the results in reverse chronological order, i.e. in order of decreasing message_id. Cannot be used in secret chats with a non-empty query //-(searchSecretMessages should be used instead), or without an enabled message database. For optimal performance the number of returned messages is chosen by the library //@chat_id Identifier of the chat in which to search messages @@ -4695,9 +4698,6 @@ reportSupergroupSpam supergroup_id:int32 user_id:int32 message_ids:vector //@filter The type of users to return. By default, supergroupMembersFilterRecent @offset Number of users to skip @limit The maximum number of users be returned; up to 200 getSupergroupMembers supergroup_id:int32 filter:SupergroupMembersFilter offset:int32 limit:int32 = ChatMembers; -//@description Deletes a supergroup or channel along with all messages in the corresponding chat. This will release the supergroup or channel username and remove all members; requires owner privileges in the supergroup or channel. Chats with more than 1000 members can't be deleted using this method @supergroup_id Identifier of the supergroup or channel -deleteSupergroup supergroup_id:int32 = Ok; - //@description Closes a secret chat, effectively transferring its state to secretChatStateClosed @secret_chat_id Secret chat identifier closeSecretChat secret_chat_id:int32 = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 017b3e174c3622c2ba18399ff18eb86f671716ef..25dce982dad80b63db6af35ac4d2d64b69858bbc 100644 GIT binary patch delta 47 zcmdn;j(f>F?uHh|Ele?<0&x#cedS6?%}Fgubvj=;CWRFM D;K33Q delta 62 zcmV-E0KxyH?hCZ;3xI?Hv;tg7x3Eb9R0S2Ip3I04WMyn+bY)X@aAk65a&L8Tmk}@m U6PMgF0W6o$ECLX>1Wy7Ws2wjCR{#J2 diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 7f3fc2055..259a00287 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -34,6 +34,7 @@ #include "td/telegram/Photo.hpp" #include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatActor.h" +#include "td/telegram/SecretChatsManager.h" #include "td/telegram/ServerMessageId.h" #include "td/telegram/StickerSetId.hpp" #include "td/telegram/StickersManager.h" @@ -6724,6 +6725,27 @@ void ContactsManager::delete_channel(ChannelId channel_id, Promise &&promi td_->create_handler(std::move(promise))->send(channel_id); } +void ContactsManager::delete_dialog(DialogId dialog_id, Promise &&promise) { + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return td_->messages_manager_->delete_dialog_history(dialog_id, true, true, std::move(promise)); + case DialogType::Chat: + return delete_chat(dialog_id.get_chat_id(), std::move(promise)); + case DialogType::Channel: + return delete_channel(dialog_id.get_channel_id(), std::move(promise)); + case DialogType::SecretChat: + send_closure(td_->secret_chats_manager_, &SecretChatsManager::cancel_chat, dialog_id.get_secret_chat_id(), true, + std::move(promise)); + return; + default: + UNREACHABLE(); + } +} + void ContactsManager::add_chat_participant(ChatId chat_id, UserId user_id, int32 forward_limit, Promise &&promise) { const Chat *c = get_chat(chat_id); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 77894dfd8..ad9b9f135 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -359,7 +359,7 @@ class ContactsManager : public Actor { void report_channel_spam(ChannelId channel_id, UserId user_id, const vector &message_ids, Promise &&promise); - void delete_channel(ChannelId channel_id, Promise &&promise); + void delete_dialog(DialogId dialog_id, Promise &&promise); void get_channel_statistics(DialogId dialog_id, bool is_dark, Promise> &&promise); @@ -379,8 +379,6 @@ class ContactsManager : public Actor { void add_channel_participants(ChannelId channel_id, const vector &user_ids, Promise &&promise); - void delete_chat(ChatId chat_id, Promise &&promise); - void change_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status, Promise &&promise); @@ -1132,6 +1130,10 @@ class ContactsManager : public Actor { tl_object_ptr input_check_password, Promise &&promise); + void delete_chat(ChatId chat_id, Promise &&promise); + + void delete_channel(ChannelId channel_id, Promise &&promise); + void get_channel_statistics_dc_id(DialogId dialog_id, bool for_full_statistics, Promise &&promise); void get_channel_statistics_dc_id_impl(ChannelId channel_id, bool for_full_statistics, Promise &&promise); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 8762d5fa5..77f289987 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -12255,9 +12255,9 @@ void MessagesManager::finish_delete_secret_messages(DialogId dialog_id, std::vec delete_dialog_messages_from_updates(dialog_id, to_delete_message_ids, false); } -void MessagesManager::delete_secret_chat_history(SecretChatId secret_chat_id, MessageId last_message_id, - Promise<> promise) { - LOG(DEBUG) << "On delete history in " << secret_chat_id << " up to " << last_message_id; +void MessagesManager::delete_secret_chat_history(SecretChatId secret_chat_id, bool remove_from_dialog_list, + MessageId last_message_id, Promise<> promise) { + LOG(DEBUG) << "Delete history in " << secret_chat_id << " up to " << last_message_id; CHECK(secret_chat_id.is_valid()); CHECK(!last_message_id.is_scheduled()); @@ -12273,19 +12273,20 @@ void MessagesManager::delete_secret_chat_history(SecretChatId secret_chat_id, Me pending_secret_message->type = PendingSecretMessage::Type::DeleteHistory; pending_secret_message->dialog_id = dialog_id; pending_secret_message->last_message_id = last_message_id; + pending_secret_message->remove_from_dialog_list = remove_from_dialog_list; add_secret_message(std::move(pending_secret_message)); } -void MessagesManager::finish_delete_secret_chat_history(DialogId dialog_id, MessageId last_message_id, - Promise<> promise) { +void MessagesManager::finish_delete_secret_chat_history(DialogId dialog_id, bool remove_from_dialog_list, + MessageId last_message_id, Promise<> promise) { LOG(DEBUG) << "Delete history in " << dialog_id << " up to " << last_message_id; - promise.set_value(Unit()); // TODO: set after event is saved Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); // TODO: probably last_message_id is not needed - delete_all_dialog_messages(d, false, true); + delete_all_dialog_messages(d, remove_from_dialog_list, true); + promise.set_value(Unit()); // TODO: set after event is saved } void MessagesManager::read_secret_chat_outbox(SecretChatId secret_chat_id, int32 up_to_date, int32 read_date) { @@ -12560,8 +12561,9 @@ void MessagesManager::finish_add_secret_message(unique_ptr std::move(pending_secret_message->success_promise)); } if (pending_secret_message->type == PendingSecretMessage::Type::DeleteHistory) { - return finish_delete_secret_chat_history(pending_secret_message->dialog_id, pending_secret_message->last_message_id, - std::move(pending_secret_message->success_promise)); + return finish_delete_secret_chat_history( + pending_secret_message->dialog_id, pending_secret_message->remove_from_dialog_list, + pending_secret_message->last_message_id, std::move(pending_secret_message->success_promise)); } auto d = get_dialog(pending_secret_message->message_info.dialog_id); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index ff79e01bb..18dcaf4b4 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -234,7 +234,8 @@ class MessagesManager : public Actor { void delete_secret_messages(SecretChatId secret_chat_id, std::vector random_ids, Promise<> promise); - void delete_secret_chat_history(SecretChatId secret_chat_id, MessageId last_message_id, Promise<> promise); + void delete_secret_chat_history(SecretChatId secret_chat_id, bool remove_from_dialog_list, MessageId last_message_id, + Promise<> promise); void read_secret_chat_outbox(SecretChatId secret_chat_id, int32 up_to_date, int32 read_date); @@ -1574,6 +1575,7 @@ class MessagesManager : public Actor { DialogId dialog_id; vector random_ids; MessageId last_message_id; + bool remove_from_dialog_list = false; Promise<> success_promise; }; @@ -1709,7 +1711,8 @@ class MessagesManager : public Actor { void finish_delete_secret_messages(DialogId dialog_id, std::vector random_ids, Promise<> promise); - void finish_delete_secret_chat_history(DialogId dialog_id, MessageId last_message_id, Promise<> promise); + void finish_delete_secret_chat_history(DialogId dialog_id, bool remove_from_dialog_list, MessageId last_message_id, + Promise<> promise); MessageInfo parse_telegram_api_message(tl_object_ptr message_ptr, bool is_scheduled, const char *source) const; diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index 3e1b32ab1..571472767 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -770,7 +770,7 @@ void SecretChatActor::do_close_chat_impl(bool delete_history, bool is_already_di auto lock = mpas.get_promise(); if (delete_history) { - context_->on_flush_history(MessageId::max(), mpas.get_promise()); + context_->on_flush_history(true, MessageId::max(), mpas.get_promise()); } send_update_secret_chat(); @@ -1349,7 +1349,8 @@ Status SecretChatActor::do_inbound_message_decrypted(unique_ptron_flush_history(MessageId(ServerMessageId(message->message_id)), std::move(save_message_finish)); + context_->on_flush_history(false, MessageId(ServerMessageId(message->message_id)), + std::move(save_message_finish)); break; case secret_api::decryptedMessageActionReadMessages::ID: { const auto &random_ids = diff --git a/td/telegram/SecretChatActor.h b/td/telegram/SecretChatActor.h index f2ff16a62..682aa7044 100644 --- a/td/telegram/SecretChatActor.h +++ b/td/telegram/SecretChatActor.h @@ -96,7 +96,7 @@ class SecretChatActor : public NetQueryCallback { tl_object_ptr file, tl_object_ptr message, Promise<> promise) = 0; virtual void on_delete_messages(std::vector random_id, Promise<> promise) = 0; - virtual void on_flush_history(MessageId message_id, Promise<> promise) = 0; + virtual void on_flush_history(bool remove_from_dialog_list, MessageId message_id, Promise<> promise) = 0; virtual void on_read_message(int64 random_id, Promise<> promise) = 0; virtual void on_screenshot_taken(UserId user_id, MessageId message_id, int32 date, int64 random_id, Promise<> promise) = 0; diff --git a/td/telegram/SecretChatsManager.cpp b/td/telegram/SecretChatsManager.cpp index 6176b3586..db6d0a28e 100644 --- a/td/telegram/SecretChatsManager.cpp +++ b/td/telegram/SecretChatsManager.cpp @@ -373,9 +373,9 @@ unique_ptr SecretChatsManager::make_secret_chat_contex send_closure(G()->messages_manager(), &MessagesManager::delete_secret_messages, secret_chat_id_, std::move(random_ids), std::move(promise)); } - void on_flush_history(MessageId message_id, Promise<> promise) override { - send_closure(G()->messages_manager(), &MessagesManager::delete_secret_chat_history, secret_chat_id_, message_id, - std::move(promise)); + void on_flush_history(bool remove_from_dialog_list, MessageId message_id, Promise<> promise) override { + send_closure(G()->messages_manager(), &MessagesManager::delete_secret_chat_history, secret_chat_id_, + remove_from_dialog_list, message_id, std::move(promise)); } void on_read_message(int64 random_id, Promise<> promise) override { send_closure(G()->messages_manager(), &MessagesManager::open_secret_message, secret_chat_id_, random_id, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index ab419916e..24dfc126c 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5472,6 +5472,22 @@ void Td::on_request(uint64 id, const td_api::deleteChatHistory &request) { std::move(promise)); } +void Td::on_request(uint64 id, const td_api::deleteChat &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + DialogId dialog_id(request.chat_id_); + auto query_promise = [actor_id = messages_manager_actor_.get(), dialog_id, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &MessagesManager::delete_dialog, dialog_id); + promise.set_value(Unit()); + } + }; + contacts_manager_->delete_dialog(dialog_id, std::move(query_promise)); +} + void Td::on_request(uint64 id, const td_api::getMessageThreadHistory &request) { CHECK_IS_USER(); CREATE_REQUEST(GetMessageThreadHistoryRequest, request.chat_id_, request.message_id_, request.from_message_id_, @@ -6699,12 +6715,6 @@ void Td::on_request(uint64 id, td_api::getSupergroupMembers &request) { request.offset_, request.limit_, -1, false, std::move(query_promise)); } -void Td::on_request(uint64 id, const td_api::deleteSupergroup &request) { - CHECK_IS_USER(); - CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->delete_channel(ChannelId(request.supergroup_id_), std::move(promise)); -} - void Td::on_request(uint64 id, td_api::closeSecretChat &request) { CREATE_OK_REQUEST_PROMISE(); send_closure(secret_chats_manager_, &SecretChatsManager::cancel_chat, SecretChatId(request.secret_chat_id_), false, diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 7e690f5b6..7efb1f85c 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -578,6 +578,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::deleteChatHistory &request); + void on_request(uint64 id, const td_api::deleteChat &request); + void on_request(uint64 id, const td_api::getMessageThreadHistory &request); void on_request(uint64 id, td_api::searchChatMessages &request); @@ -884,8 +886,6 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getSupergroupMembers &request); - void on_request(uint64 id, const td_api::deleteSupergroup &request); - void on_request(uint64 id, td_api::closeSecretChat &request); void on_request(uint64 id, td_api::getStickers &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index b04bc96bb..55900c391 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3417,8 +3417,8 @@ class CliClient final : public Actor { td_api::make_object(as_location("40.0", "60.0"), "address"))); } else if (op == "UpgradeBasicGroupChatToSupergroupChat") { send_request(td_api::make_object(as_chat_id(args))); - } else if (op == "DeleteSupergroup") { - send_request(td_api::make_object(as_supergroup_id(args))); + } else if (op == "DeleteChat") { + send_request(td_api::make_object(as_chat_id(args))); } else if (op == "gcpc") { send_request(td_api::make_object()); } else if (op == "gcpcl") { diff --git a/test/secret.cpp b/test/secret.cpp index 6353b2151..b9d91108b 100644 --- a/test/secret.cpp +++ b/test/secret.cpp @@ -545,7 +545,7 @@ class FakeSecretChatContext : public SecretChatActor::Context { void on_send_message_ok(int64 random_id, MessageId message_id, int32 date, tl_object_ptr file, Promise<>) override; void on_delete_messages(std::vector random_id, Promise<>) override; - void on_flush_history(MessageId, Promise<>) override; + void on_flush_history(bool, MessageId, Promise<>) override; void on_read_message(int64, Promise<>) override; void on_screenshot_taken(UserId user_id, MessageId message_id, int32 date, int64 random_id, @@ -992,7 +992,7 @@ void FakeSecretChatContext::on_send_message_ok(int64 random_id, MessageId messag void FakeSecretChatContext::on_delete_messages(std::vector random_id, Promise<> promise) { promise.set_value(Unit()); } -void FakeSecretChatContext::on_flush_history(MessageId, Promise<> promise) { +void FakeSecretChatContext::on_flush_history(bool, MessageId, Promise<> promise) { promise.set_error(Status::Error("Unsupported")); } void FakeSecretChatContext::on_read_message(int64, Promise<> promise) { From 613caeb4443e768c0232b86156b5f82bc50839bb Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Jan 2021 15:02:45 +0300 Subject: [PATCH 059/232] Add source to send_get_dialog_query. --- td/telegram/MessagesManager.cpp | 25 +++++++++++++------------ td/telegram/MessagesManager.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 77f289987..df78115b4 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -10754,7 +10754,7 @@ void MessagesManager::repair_server_unread_count(DialogId dialog_id, int32 unrea create_actor("RepairServerUnreadCountSleepActor", 0.2, PromiseCreator::lambda([actor_id = actor_id(this), dialog_id](Result result) { send_closure(actor_id, &MessagesManager::send_get_dialog_query, dialog_id, Promise(), - 0); + 0, "repair_server_unread_count"); })) .release(); } @@ -18566,7 +18566,7 @@ void MessagesManager::create_dialog(DialogId dialog_id, bool force, Promise()); + send_get_dialog_query(dialog_id, Promise(), 0, "on_update_dialog_draft_message"); } return; } @@ -29226,7 +29226,7 @@ DialogId MessagesManager::search_public_dialog(const string &username_to_search, } else { const Dialog *d = get_dialog_force(dialog_id); if (!is_dialog_inited(d)) { - send_get_dialog_query(dialog_id, std::move(promise)); + send_get_dialog_query(dialog_id, std::move(promise), 0, "search_public_dialog"); return DialogId(); } } @@ -29316,7 +29316,8 @@ uint64 MessagesManager::save_get_dialog_from_server_log_event(DialogId dialog_id get_log_event_storer(log_event)); } -void MessagesManager::send_get_dialog_query(DialogId dialog_id, Promise &&promise, uint64 log_event_id) { +void MessagesManager::send_get_dialog_query(DialogId dialog_id, Promise &&promise, uint64 log_event_id, + const char *source) { if (td_->auth_manager_->is_bot() || dialog_id.get_type() == DialogType::SecretChat) { if (log_event_id != 0) { binlog_erase(G()->td_db()->get_binlog(), log_event_id); @@ -29334,7 +29335,7 @@ void MessagesManager::send_get_dialog_query(DialogId dialog_id, Promise && promises.push_back(std::move(promise)); if (promises.size() != 1) { if (log_event_id != 0) { - LOG(INFO) << "Duplicate getDialog query for " << dialog_id; + LOG(INFO) << "Duplicate getDialog query for " << dialog_id << " from " << source; binlog_erase(G()->td_db()->get_binlog(), log_event_id); } // query has already been sent, just wait for the result @@ -29353,7 +29354,7 @@ void MessagesManager::send_get_dialog_query(DialogId dialog_id, Promise && return; } - LOG(INFO) << "Send get " << dialog_id << " query"; + LOG(INFO) << "Send get " << dialog_id << " query from " << source; td_->create_handler()->send(dialog_id); } @@ -29447,7 +29448,7 @@ void MessagesManager::drop_username(const string &username) { auto dialog_id = it->second.dialog_id; if (have_input_peer(dialog_id, AccessRights::Read)) { CHECK(dialog_id.get_type() != DialogType::SecretChat); - send_get_dialog_query(dialog_id, Auto()); + send_get_dialog_query(dialog_id, Auto(), 0, "drop_username"); } resolved_usernames_.erase(it); @@ -33167,7 +33168,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab if (being_added_dialog_id_ != dialog_id && !td_->auth_manager_->is_bot() && !is_dialog_inited(d) && dialog_type != DialogType::SecretChat && have_input_peer(dialog_id, AccessRights::Read)) { // asynchronously get dialog from the server - send_get_dialog_query(dialog_id, Auto()); + send_get_dialog_query(dialog_id, Auto(), 0, "fix_new_dialog 20"); } if (being_added_dialog_id_ != dialog_id && !d->is_is_blocked_inited && !td_->auth_manager_->is_bot()) { @@ -34119,7 +34120,7 @@ unique_ptr MessagesManager::parse_dialog(DialogId dialo have_dialog_info_force(dialog_id); if (have_input_peer(dialog_id, AccessRights::Read)) { if (dialog_id.get_type() != DialogType::SecretChat) { - send_get_dialog_query(dialog_id, Auto()); + send_get_dialog_query(dialog_id, Auto(), 0, "parse_dialog"); } } else { LOG(ERROR) << "Have no info about " << dialog_id << " to repair it"; @@ -36019,7 +36020,7 @@ void MessagesManager::on_binlog_events(vector &&events) { break; } - send_get_dialog_query(dialog_id, Promise(), event.id_); + send_get_dialog_query(dialog_id, Promise(), event.id_, "GetDialogFromServerLogEvent"); break; } case LogEvent::HandlerType::UnpinAllDialogMessagesOnServer: { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 18dcaf4b4..b14409c58 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -2409,7 +2409,7 @@ class MessagesManager : public Actor { void on_get_dialogs_from_database(FolderId folder_id, int32 limit, DialogDbGetDialogsResult &&dialogs, Promise &&promise); - void send_get_dialog_query(DialogId dialog_id, Promise &&promise, uint64 log_event_id = 0); + void send_get_dialog_query(DialogId dialog_id, Promise &&promise, uint64 log_event_id, const char *source); void send_search_public_dialogs_query(const string &query, Promise &&promise); From 1b7c6a9c314b3f0b388e38bd811187690dea077c Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Jan 2021 16:12:03 +0300 Subject: [PATCH 060/232] Update layer 123. --- td/generate/scheme/telegram_api.tl | 24 +++++++++++++++++------- td/generate/scheme/telegram_api.tlo | Bin 227544 -> 229016 bytes td/telegram/ContactsManager.cpp | 9 +++++---- td/telegram/MessagesManager.cpp | 4 ++-- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 10d1c9af9..403a55779 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -96,7 +96,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; userEmpty#200250ba id:int = User; -user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; +user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#69d3ab26 flags:# has_video:flags.0?true photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto; @@ -111,7 +111,7 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; +channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; chatFull#f3474af6 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; @@ -216,6 +216,7 @@ inputReportReasonChildAbuse#adf44ee3 = ReportReason; inputReportReasonOther#e1746d0a text:string = ReportReason; inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; +inputReportReasonFake#f5ddd6e7 = ReportReason; userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull; @@ -442,6 +443,7 @@ sendMessageGamePlayAction#dd6a8f48 = SendMessageAction; sendMessageRecordRoundAction#88f27fbc = SendMessageAction; sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction; speakingInGroupCallAction#d92c2285 = SendMessageAction; +sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction; contacts.found#b3134d9d my_results:Vector results:Vector chats:Vector users:Vector = contacts.Found; @@ -522,7 +524,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteExported#6e24fc9d flags:# revoked:flags.0?true expired:flags.4?true permanent:flags.5?true link:string admin_id:int date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; +chatInviteExported#6e24fc9d flags:# revoked:flags.0?true permanent:flags.5?true link:string admin_id:int date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; @@ -647,7 +649,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink; -messageFwdHeader#5f777dce flags:# from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader; +messageFwdHeader#5f777dce flags:# imported:flags.7?true from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader; auth.codeTypeSms#72a3158c = auth.CodeType; auth.codeTypeCall#741cd3e3 = auth.CodeType; @@ -1201,6 +1203,10 @@ messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector users:Vector = messages.ChatInviteImporters; +messages.historyImport#1662af0b id:long = messages.HistoryImport; + +messages.historyImportParsed#8d94ab42 flags:# pm:flags.0?true group:flags.1?true title:flags.1?string = messages.HistoryImportParsed; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1343,7 +1349,7 @@ messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; messages.editChatTitle#dc452855 chat_id:int title:string = Updates; messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates; messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates; -messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates; +messages.deleteChatUser#c534459a flags:# revoke_history:flags.0?true chat_id:int user_id:InputUser = Updates; messages.createChat#9cb126e users:Vector title:string = Updates; messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; @@ -1450,13 +1456,17 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; -messages.getExportedChatInvites#6d9cae03 flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.getExportedChatInvites#6a72ac6c flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; messages.deleteRevokedExportedChatInvites#52041463 peer:InputPeer = Bool; messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.deleteChat#83247d11 chat_id:int = Bool; messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; +messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; +messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; +messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; +messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1508,7 +1518,7 @@ channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipant channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant; channels.getChannels#a7f6bbb id:Vector = messages.Chats; channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull; -channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates; +channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true for_import:flags.3?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates; channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates; channels.editTitle#566decd0 channel:InputChannel title:string = Updates; channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index dc0ef9c57981e86e05d17cf358df4454651e71d3..500f36e339b965e6d1eca44bff07c102d91d68b6 100644 GIT binary patch delta 1657 zcmaJ>T}%^M6z;uusFk)d6qM5PGbox$MyZAe1j2-fz`jb0zseu*LIJTZSYKa zP3wgdzgNr;cXIv!?--mk#s}8Ml;B`|`TAvR$Ur4vapztWj)`yLa4EL<^zr1r1Sp{P za6LS(Iaz|EjdXm%oooSx_{Klm;Z%C*R$ZrLI&<`sIO0 z&G`DoN_8qnVN8dti)))S3mXjI8M?f# zU=~4HZ5(^Y;$!0~Oux`8)gIpA;_&r|0W*y%N*aPdo6y*T0?FPAG8u-|=V$9tl|27Q z2TCw)(;J2}Dg=U@Kf4GC)gcDk-_y#4a{svF+ES7TLt035po0aghM1#unTxuw*X`pR z?djWetHefB?~k{KrX?jQe0j>GOpoQv#Y)n704d4gVOT}z`+&yD&l)mfMS8(zMWNcS DCBi^d delta 977 zcmbR7m-og;UfxHu^{p77zPjyT#$poq411oV*a1q-4#b^N=W69>Jmdb{V(vu&)(3w6{ zl1Y8CK!(QTih7aF9@#$_Cm+a=nf%})&*llaCmbg~DbShzK$KZvvP7~Zdul~NW>IR& z6xrjue+VohEX_L0_3!t$Ao-9?k#d=)8VjfI|ve+ZZe%w2e)j+bVdW* zvIp?$WtqXK!2-5p0zabxgq1P_pKU8<;IVDH!A!;kDG5*@qI(1+HhslrMh)DuFE#^> z#;Mm}3($dJQ$B2EG=Z?@0L5_Yc!EVoz*bCC_Fxgy*oM#K0=%+2wgCeWr$7GS)$6hy z(|sGZ<1uf0!Vbn5AxLl>IK?;z%-WuCnsJXIgsJeCF@_(QP%oa**}js8DVzzGIJXDz zGyMqJ-jd3+N1w59yW>rm Ji74g<6#%p$XgdG^ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 259a00287..fb8147661 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1701,7 +1701,7 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { flags |= telegram_api::messages_getExportedChatInvites::REVOKED_MASK; } send_query(G()->net_query_creator().create(telegram_api::messages_getExportedChatInvites( - flags, false /*ignored*/, std::move(input_peer), std::move(input_user), offset_invite_link, limit))); + flags, false /*ignored*/, std::move(input_peer), std::move(input_user), 0, offset_invite_link, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1951,8 +1951,9 @@ class DeleteChatUserQuery : public Td::ResultHandler { } void send(ChatId chat_id, tl_object_ptr &&input_user) { - send_query( - G()->net_query_creator().create(telegram_api::messages_deleteChatUser(chat_id.get(), std::move(input_user)))); + int32 flags = 0; + send_query(G()->net_query_creator().create( + telegram_api::messages_deleteChatUser(flags, false /*ignored*/, chat_id.get(), std::move(input_user)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -14494,7 +14495,7 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc on_update_chat_photo(c, chat_id, std::move(chat.photo_)); on_update_chat_active(c, chat_id, is_active); on_update_chat_migrated_to_channel_id(c, chat_id, migrated_to_channel_id); - LOG_IF(INFO, !is_active && !migrated_to_channel_id.is_valid()) << chat_id << " is deactivated in " << debug_str; + LOG_IF(INFO, !is_active && !migrated_to_channel_id.is_valid()) << chat_id << " is deactivated" << debug_str; if (c->cache_version != Chat::CACHE_VERSION) { c->cache_version = Chat::CACHE_VERSION; c->need_save_to_database = true; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index df78115b4..03a307ede 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -988,8 +988,8 @@ class CreateChannelQuery : public Td::ResultHandler { random_id_ = random_id; send_query(G()->net_query_creator().create( - telegram_api::channels_createChannel(flags, false /*ignored*/, false /*ignored*/, title, about, - location.get_input_geo_point(), location.get_address()))); + telegram_api::channels_createChannel(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, title, + about, location.get_input_geo_point(), location.get_address()))); } void on_result(uint64 id, BufferSlice packet) override { From 7a45faff24dd36f7e7dcfdddfcab36adf6ca760e Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Jan 2021 17:30:26 +0300 Subject: [PATCH 061/232] Add offset_date support in getChatInviteLinks. --- td/generate/scheme/td_api.tl | 10 +++++++--- td/generate/scheme/td_api.tlo | Bin 194340 -> 194376 bytes td/telegram/ContactsManager.cpp | 16 +++++++++------- td/telegram/ContactsManager.h | 2 +- td/telegram/Td.cpp | 4 ++-- td/telegram/cli.cpp | 8 +++++--- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 466cb779e..6862dc462 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4387,10 +4387,14 @@ createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatIn //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; -//@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @administrator_user_id If not 0, only invite links created by the specified administrator will be returned +//@description Returns exported invite links for a chat. Requires administrator privileges and can_invite_users right in the chat +//@chat_id Chat identifier +//@administrator_user_id If not 0, only invite links created by the specified administrator will be returned //@is_revoked Pass true if revoked links needs to be returned instead of active or expired -//@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning @limit Maximum number of invite links to return -getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool offset_invite_link:string limit:int32 = ChatInviteLinks; +//@offset_date Creation date of an invite link starting after which to return invite links; use 0 to get results from the beginning +//@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning +//@limit Maximum number of invite links to return +getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool offset_date:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; //@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @invite_link Invite link for which to return chat members //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 25dce982dad80b63db6af35ac4d2d64b69858bbc..9f84e287927038e49494ee19d6d6a7f6cbbdaa43 100644 GIT binary patch delta 44 zcmV+{0Mq}Z?hDB73xI?Hv;xR#1hWf(@t4eM0vwmbSOO@Q5NiTdmt?yFB)4R10>F#5 CY7$-m delta 39 xcmV+?0NDS??hB;u3xI?Hv;xR#1V@&?o0rUL0vwklMgb_75NiTdw}@*3z>8ON5BmTB diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index fb8147661..f1b952324 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1680,8 +1680,8 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, const string &offset_invite_link, - int32 limit) { + void send(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, int32 offset_date, + const string &offset_invite_link, int32 limit) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); if (input_peer == nullptr) { @@ -1694,14 +1694,16 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { if (input_user != nullptr) { flags |= telegram_api::messages_getExportedChatInvites::ADMIN_ID_MASK; } - if (!offset_invite_link.empty()) { + if (!offset_invite_link.empty() || offset_date != 0) { + flags |= telegram_api::messages_getExportedChatInvites::OFFSET_DATE_MASK; flags |= telegram_api::messages_getExportedChatInvites::OFFSET_LINK_MASK; } if (is_revoked) { flags |= telegram_api::messages_getExportedChatInvites::REVOKED_MASK; } - send_query(G()->net_query_creator().create(telegram_api::messages_getExportedChatInvites( - flags, false /*ignored*/, std::move(input_peer), std::move(input_user), 0, offset_invite_link, limit))); + send_query(G()->net_query_creator().create( + telegram_api::messages_getExportedChatInvites(flags, false /*ignored*/, std::move(input_peer), + std::move(input_user), offset_date, offset_invite_link, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -7217,7 +7219,7 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & } void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, - const string &offset_invite_link, int32 limit, + int32 offset_date, const string &offset_invite_link, int32 limit, Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); @@ -7230,7 +7232,7 @@ void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId adminis } td_->create_handler(std::move(promise)) - ->send(dialog_id, administrator_user_id, is_revoked, offset_invite_link, limit); + ->send(dialog_id, administrator_user_id, is_revoked, offset_date, offset_invite_link, limit); } void ContactsManager::get_dialog_invite_link_users( diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index ad9b9f135..09f5aade7 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -398,7 +398,7 @@ class ContactsManager : public Actor { void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise); - void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, + void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, int32 offset_date, const string &offset_invite_link, int32 limit, Promise> &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 24dfc126c..3be1c17c0 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6326,8 +6326,8 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.offset_invite_link_); contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.administrator_user_id_), - request.is_revoked_, request.offset_invite_link_, request.limit_, - std::move(promise)); + request.is_revoked_, request.offset_date_, request.offset_invite_link_, + request.limit_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 55900c391..9dc50a8c6 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2713,11 +2713,13 @@ class CliClient final : public Actor { } else if (op == "gcil" || op == "gcilr") { string chat_id; string administrator_user_id; + int32 offset_date; string offset_invite_link; string limit; - get_args(args, chat_id, administrator_user_id, offset_invite_link, limit); - send_request(td_api::make_object( - as_chat_id(chat_id), as_user_id(administrator_user_id), op == "gcilr", offset_invite_link, as_limit(limit))); + get_args(args, chat_id, administrator_user_id, offset_date, offset_invite_link, limit); + send_request(td_api::make_object(as_chat_id(chat_id), + as_user_id(administrator_user_id), op == "gcilr", + offset_date, offset_invite_link, as_limit(limit))); } else if (op == "gcilm") { string chat_id; string invite_link; From da4d2b7636504d875e9f194ff79d2eb7aae297cc Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Jan 2021 21:12:48 +0300 Subject: [PATCH 062/232] Add separate banChatMember method. --- td/generate/scheme/td_api.tl | 9 ++++++++- td/generate/scheme/td_api.tlo | Bin 194376 -> 194580 bytes td/telegram/ContactsManager.cpp | 13 +++++++++---- td/telegram/ContactsManager.h | 2 ++ td/telegram/MessagesManager.cpp | 26 +++++++++++++++++++++++--- td/telegram/MessagesManager.h | 3 +++ td/telegram/Td.cpp | 7 +++++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 8 ++++++++ 9 files changed, 62 insertions(+), 8 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 6862dc462..4bbf2baf2 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -479,7 +479,7 @@ chatMemberStatusRestricted is_member:Bool restricted_until_date:int32 permission chatMemberStatusLeft = ChatMemberStatus; //@description The user was banned (and hence is not a member of the chat). Implies the user can't return to the chat or view messages -//@banned_until_date Point in time (Unix timestamp) when the user will be unbanned; 0 if never. If the user is banned for more than 366 days or for less than 30 seconds from the current time, the user is considered to be banned forever +//@banned_until_date Point in time (Unix timestamp) when the user will be unbanned; 0 if never. If the user is banned for more than 366 days or for less than 30 seconds from the current time, the user is considered to be banned forever. Always 0 in basic groups chatMemberStatusBanned banned_until_date:int32 = ChatMemberStatus; @@ -4282,6 +4282,13 @@ addChatMembers chat_id:int53 user_ids:vector = Ok; //@chat_id Chat identifier @user_id User identifier @status The new status of the member in the chat setChatMemberStatus chat_id:int53 user_id:int32 status:ChatMemberStatus = Ok; +//@description Bans a member in a chat. Members can't be banned in private or secret chats. In supergroups and channels, the user will not be able to return to the group on their own using invite links, etc., unless [unbanned](#unbanchatmember) first +//@chat_id Chat identifier +//@user_id Identifier of the user +//@banned_until_date Point in time (Unix timestamp) when the user will be unbanned; 0 if never. If the user is banned for more than 366 days or for less than 30 seconds from the current time, the user is considered to be banned forever. Ignored in basic groups +//@revoke_messages Pass true to delete all messages in the chat for the user. Always true for supergroups and channels +banChatMember chat_id:int53 user_id:int32 banned_until_date:int32 revoke_messages:Bool = Ok; + //@description Checks whether the current session can be used to transfer a chat ownership to another user canTransferOwnership = CanTransferOwnershipResult; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 9f84e287927038e49494ee19d6d6a7f6cbbdaa43..c46745ef72ec62a52f9404d667a4730cdd539dc3 100644 GIT binary patch delta 75 zcmV-R0JQ(e?hBOg3xI?Hv;zDn0h+h{C<22F2nzQ@dJSS>ZkO;W0u+~I^8qNA5K96a hldzW!laYfX1`l#&c5iEwu7Mtx5H|uCw}3SQ$ia0N9OVE2 delta 27 jcmbR8f&0We?uHh|Elhtj7-w$(tHIRFv)#vx>BK<*uuKd4 diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index f1b952324..1b1a358a9 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1952,8 +1952,11 @@ class DeleteChatUserQuery : public Td::ResultHandler { explicit DeleteChatUserQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(ChatId chat_id, tl_object_ptr &&input_user) { + void send(ChatId chat_id, tl_object_ptr &&input_user, bool revoke_messages) { int32 flags = 0; + if (revoke_messages) { + flags |= telegram_api::messages_deleteChatUser::REVOKE_HISTORY_MASK; + } send_query(G()->net_query_creator().create( telegram_api::messages_deleteChatUser(flags, false /*ignored*/, chat_id.get(), std::move(input_user)))); } @@ -7001,7 +7004,7 @@ void ContactsManager::promote_channel_participant(ChannelId channel_id, UserId u void ContactsManager::change_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status, Promise &&promise) { if (!status.is_member()) { - return delete_chat_participant(chat_id, user_id, std::move(promise)); + return delete_chat_participant(chat_id, user_id, false, std::move(promise)); } auto c = get_chat(chat_id); @@ -7288,7 +7291,8 @@ void ContactsManager::import_dialog_invite_link(const string &invite_link, Promi td_->create_handler(std::move(promise))->send(invite_link); } -void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, Promise &&promise) { +void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, + Promise &&promise) { const Chat *c = get_chat(chat_id); if (c == nullptr) { return promise.set_error(Status::Error(3, "Chat info not found")); @@ -7299,6 +7303,7 @@ void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, Pr auto my_id = get_my_id(); if (c->status.is_left()) { if (user_id == my_id) { + td_->messages_manager_->delete_dialog(DialogId(chat_id)); return promise.set_value(Unit()); } else { return promise.set_error(Status::Error(3, "Not in the chat")); @@ -7337,7 +7342,7 @@ void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, Pr } // TODO invoke after - td_->create_handler(std::move(promise))->send(chat_id, std::move(input_user)); + td_->create_handler(std::move(promise))->send(chat_id, std::move(input_user), revoke_messages); } void ContactsManager::restrict_channel_participant(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 09f5aade7..67cf25121 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -385,6 +385,8 @@ class ContactsManager : public Actor { void change_channel_participant_status(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, Promise &&promise); + void delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, Promise &&promise); + void can_transfer_ownership(Promise &&promise); static td_api::object_ptr get_can_transfer_ownership_result_object( diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 03a307ede..bd70da913 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -30234,7 +30234,6 @@ void MessagesManager::unpin_all_dialog_messages_on_server(DialogId dialog_id, ui void MessagesManager::add_dialog_participant(DialogId dialog_id, UserId user_id, int32 forward_limit, Promise &&promise) { - LOG(INFO) << "Receive AddChatParticipant request to add " << user_id << " to " << dialog_id; if (!have_dialog_force(dialog_id)) { return promise.set_error(Status::Error(3, "Chat not found")); } @@ -30257,7 +30256,6 @@ void MessagesManager::add_dialog_participant(DialogId dialog_id, UserId user_id, void MessagesManager::add_dialog_participants(DialogId dialog_id, const vector &user_ids, Promise &&promise) { - LOG(INFO) << "Receive AddChatParticipants request to add " << format::as_array(user_ids) << " to " << dialog_id; if (td_->auth_manager_->is_bot()) { return promise.set_error(Status::Error(3, "Method is not available for bots")); } @@ -30285,7 +30283,6 @@ void MessagesManager::set_dialog_participant_status(DialogId dialog_id, UserId u const tl_object_ptr &chat_member_status, Promise &&promise) { auto status = get_dialog_participant_status(chat_member_status); - LOG(INFO) << "Receive setChatMemberStatus request with " << user_id << " and " << dialog_id << " to " << status; if (!have_dialog_force(dialog_id)) { return promise.set_error(Status::Error(3, "Chat not found")); } @@ -30307,6 +30304,29 @@ void MessagesManager::set_dialog_participant_status(DialogId dialog_id, UserId u } } +void MessagesManager::ban_dialog_participant(DialogId dialog_id, UserId user_id, int32 banned_until_date, + bool revoke_messages, Promise &&promise) { + if (!have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return promise.set_error(Status::Error(3, "Can't ban members in a private chat")); + case DialogType::Chat: + return td_->contacts_manager_->delete_chat_participant(dialog_id.get_chat_id(), user_id, revoke_messages, + std::move(promise)); + case DialogType::Channel: + return td_->contacts_manager_->change_channel_participant_status( + dialog_id.get_channel_id(), user_id, DialogParticipantStatus::Banned(banned_until_date), std::move(promise)); + case DialogType::SecretChat: + return promise.set_error(Status::Error(3, "Can't ban members in a secret chat")); + case DialogType::None: + default: + UNREACHABLE(); + } +} + DialogParticipant MessagesManager::get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, Promise &&promise) { LOG(INFO) << "Receive GetChatMember request to get " << user_id << " in " << dialog_id << " with random_id " diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index b14409c58..9e5ca5aea 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -477,6 +477,9 @@ class MessagesManager : public Actor { const tl_object_ptr &chat_member_status, Promise &&promise); + void ban_dialog_participant(DialogId dialog_id, UserId user_id, int32 banned_until_date, bool revoke_messages, + Promise &&promise); + DialogParticipant get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 3be1c17c0..97a6e6244 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6251,6 +6251,13 @@ void Td::on_request(uint64 id, td_api::setChatMemberStatus &request) { request.status_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::banChatMember &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->ban_dialog_participant(DialogId(request.chat_id_), UserId(request.user_id_), + request.banned_until_date_, request.revoke_messages_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::canTransferOwnership &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 7efb1f85c..7aadd9fc7 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -780,6 +780,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::setChatMemberStatus &request); + void on_request(uint64 id, const td_api::banChatMember &request); + void on_request(uint64 id, const td_api::canTransferOwnership &request); void on_request(uint64 id, td_api::transferChatOwnership &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 9dc50a8c6..32204b328 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3536,6 +3536,14 @@ class CliClient final : public Actor { string user_ids; get_args(args, chat_id, user_ids); send_request(td_api::make_object(as_chat_id(chat_id), as_user_ids(user_ids))); + } else if (op == "bcm") { + string chat_id; + string user_id; + int32 banned_until_date; + bool revoke_messages; + get_args(args, chat_id, user_id, banned_until_date, revoke_messages); + send_request(td_api::make_object(as_chat_id(chat_id), as_user_id(user_id), + banned_until_date, revoke_messages)); } else if (op == "spolla") { string chat_id; string message_id; From 1d5de77b1fe4fe431f9797611fc640058fdf6664 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 20 Jan 2021 21:21:39 +0300 Subject: [PATCH 063/232] Remove superflous documentation. --- td/generate/scheme/td_api.tl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 4bbf2baf2..3a422a0a4 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4270,15 +4270,15 @@ joinChat chat_id:int53 = Ok; //@description Removes the current user from chat members. Private and secret chats can't be left using this method @chat_id Chat identifier leaveChat chat_id:int53 = Ok; -//@description Adds a new member to a chat. Members can't be added to private or secret chats. Members will not be added until the chat state has been synchronized with the server +//@description Adds a new member to a chat. Members can't be added to private or secret chats //@chat_id Chat identifier @user_id Identifier of the user @forward_limit The number of earlier messages from the chat to be forwarded to the new member; up to 100. Ignored for supergroups and channels addChatMember chat_id:int53 user_id:int32 forward_limit:int32 = Ok; -//@description Adds multiple new members to a chat. Currently this method is only available for supergroups and channels. This method can't be used to join a chat. Members can't be added to a channel if it has more than 200 members. Members will not be added until the chat state has been synchronized with the server +//@description Adds multiple new members to a chat. Currently this method is only available for supergroups and channels. This method can't be used to join a chat. Members can't be added to a channel if it has more than 200 members //@chat_id Chat identifier @user_ids Identifiers of the users to be added to the chat. The maximum number of added users is 20 for supergroups and 100 for channels addChatMembers chat_id:int53 user_ids:vector = Ok; -//@description Changes the status of a chat member, needs appropriate privileges. This function is currently not suitable for adding new members to the chat and transferring chat ownership; instead, use addChatMember or transferChatOwnership. The chat member status will not be changed until it has been synchronized with the server +//@description Changes the status of a chat member, needs appropriate privileges. This function is currently not suitable for adding new members to the chat and transferring chat ownership; instead, use addChatMember or transferChatOwnership //@chat_id Chat identifier @user_id User identifier @status The new status of the member in the chat setChatMemberStatus chat_id:int53 user_id:int32 status:ChatMemberStatus = Ok; @@ -4421,7 +4421,7 @@ deleteAllRevokedChatInviteLinks chat_id:int53 = Ok; //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; -//@description Uses an invite link to add the current user to the chat if possible. The new member will not be added until the chat state has been synchronized with the server +//@description Uses an invite link to add the current user to the chat if possible //@invite_link Invite link to import; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" joinChatByInviteLink invite_link:string = Chat; From b54554c79eb9418124bf98309da83adec1ecbc5e Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 21 Jan 2021 01:50:21 +0300 Subject: [PATCH 064/232] Add chatActionImportingMessages. --- td/generate/scheme/td_api.tl | 2 ++ td/generate/scheme/td_api.tlo | Bin 194580 -> 194684 bytes td/telegram/DialogAction.cpp | 16 ++++++++++++++++ td/telegram/DialogAction.h | 3 ++- td/telegram/cli.cpp | 3 +++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 3a422a0a4..43945ff4b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1963,6 +1963,8 @@ chatActionStartPlayingGame = ChatAction; chatActionRecordingVideoNote = ChatAction; //@description The user is uploading a video note @progress Upload progress, as a percentage chatActionUploadingVideoNote progress:int32 = ChatAction; +//@description The user is importing message history. This action is sent automatically and can't be explicitly sent @progress Import progress, as a percentage +chatActionImportingMessages progress:int32 = ChatAction; //@description The user has cancelled the previous action chatActionCancel = ChatAction; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index c46745ef72ec62a52f9404d667a4730cdd539dc3..6544a6a0d5fe07f1e438eb97c6dd57c355b2af33 100644 GIT binary patch delta 454 zcmbR8f&0$~?hPtzf}tnx3vfASB$hZPmt^MWG4M^E$RxhmhwX|kW7=j#6J;$4kcykf zgnSr)pvaj`hr_n*AV_R-Oq>pG$pvvbEMQd^;!Ge6j(7vy$^+sJ(3J0qM>jwv0bQgg z0gnqV;FA wPE1cNMhy|3Bo&+?((IA6-6M&yWfC}8wqN+dm~jrwoPI!uNnv};VWu4>0RK6u$p8QV delta 404 zcmezKfqTja?hPtzg6!2!)47~85=$JDOEUBG7qXv+7*qZ^=-fG*OL zfX4+F@JWg!;?WtAh{vT1646~+lB9yuW6c|qwr@ycY?;Ig3JiwnjgJ`>wnu(tbUz0b PFw|m_-+tsU(+(2=!0v-& diff --git a/td/telegram/DialogAction.cpp b/td/telegram/DialogAction.cpp index d4b9f8a64..e6091b81a 100644 --- a/td/telegram/DialogAction.cpp +++ b/td/telegram/DialogAction.cpp @@ -79,6 +79,9 @@ DialogAction::DialogAction(tl_object_ptr &&action) { init(Type::UploadingVideoNote, uploading_action->progress_); break; } + case td_api::chatActionImportingMessages::ID: + init(Type::Cancel); // it can't be sent explicitly + break; default: UNREACHABLE(); break; @@ -139,6 +142,11 @@ DialogAction::DialogAction(tl_object_ptr &&acti case telegram_api::speakingInGroupCallAction::ID: init(Type::SpeakingInVoiceChat); break; + case telegram_api::sendMessageHistoryImportAction::ID: { + auto history_import_action = move_tl_object_as(action); + init(Type::ImportingMessages, history_import_action->progress_); + break; + } default: UNREACHABLE(); break; @@ -175,6 +183,8 @@ tl_object_ptr DialogAction::get_input_send_mess return make_tl_object(progress_); case Type::SpeakingInVoiceChat: return make_tl_object(); + case Type::ImportingMessages: + return make_tl_object(progress_); default: UNREACHABLE(); return nullptr; @@ -211,6 +221,8 @@ tl_object_ptr DialogAction::get_secret_input_send return make_tl_object(); case Type::SpeakingInVoiceChat: return make_tl_object(); + case Type::ImportingMessages: + return make_tl_object(); default: UNREACHABLE(); return nullptr; @@ -245,6 +257,8 @@ tl_object_ptr DialogAction::get_chat_action_object() const { return td_api::make_object(); case Type::UploadingVideoNote: return td_api::make_object(progress_); + case Type::ImportingMessages: + return td_api::make_object(progress_); case Type::SpeakingInVoiceChat: default: UNREACHABLE(); @@ -381,6 +395,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogAction &act return "UploadingVideoNote"; case DialogAction::Type::SpeakingInVoiceChat: return "SpeakingInVoiceChat"; + case DialogAction::Type::ImportingMessages: + return "ImportingMessages"; default: UNREACHABLE(); return "Cancel"; diff --git a/td/telegram/DialogAction.h b/td/telegram/DialogAction.h index 0d457a7d7..09826b47b 100644 --- a/td/telegram/DialogAction.h +++ b/td/telegram/DialogAction.h @@ -31,7 +31,8 @@ class DialogAction { StartPlayingGame, RecordingVideoNote, UploadingVideoNote, - SpeakingInVoiceChat + SpeakingInVoiceChat, + ImportingMessages }; Type type_ = Type::Cancel; int32 progress_ = 0; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 32204b328..ec9a5abe0 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1310,6 +1310,9 @@ class CliClient final : public Actor { if (action == "uvn" || action == "upload_video_note") { return td_api::make_object(50); } + if (action == "im" || action == "import_messages") { + return td_api::make_object(50); + } return td_api::make_object(); } From ebb453c9528c63076bf0e70c083ee064080bc8be Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 21 Jan 2021 12:49:05 +0300 Subject: [PATCH 065/232] Add messageForwardOriginMessageImport. --- td/generate/scheme/td_api.tl | 3 +++ td/telegram/MessagesManager.cpp | 24 +++++++++++++++++++----- td/telegram/MessagesManager.h | 20 ++++++++++++-------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 43945ff4b..6cd057850 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -689,6 +689,9 @@ messageForwardOriginHiddenUser sender_name:string = MessageForwardOrigin; //@author_signature Original post author signature messageForwardOriginChannel chat_id:int53 message_id:int53 author_signature:string = MessageForwardOrigin; +//@description The message was imported from an exported message history @sender_name Name of the sender +messageForwardOriginMessageImport sender_name:string = MessageForwardOrigin; + //@description Contains information about a forwarded message //@origin Origin of a forwarded message diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index bd70da913..7031c9b93 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -4616,6 +4616,7 @@ void MessagesManager::Message::store(StorerT &storer) const { bool has_linked_top_thread_message_id = linked_top_thread_message_id.is_valid(); bool has_interaction_info_update_date = interaction_info_update_date != 0; bool has_send_emoji = !send_emoji.empty(); + bool is_imported = is_forwarded && forward_info->is_imported; BEGIN_STORE_FLAGS(); STORE_FLAG(is_channel_post); STORE_FLAG(is_outgoing); @@ -4675,6 +4676,7 @@ void MessagesManager::Message::store(StorerT &storer) const { STORE_FLAG(is_pinned); STORE_FLAG(has_interaction_info_update_date); STORE_FLAG(has_send_emoji); + STORE_FLAG(is_imported); END_STORE_FLAGS(); } @@ -4826,6 +4828,7 @@ void MessagesManager::Message::parse(ParserT &parser) { bool has_linked_top_thread_message_id = false; bool has_interaction_info_update_date = false; bool has_send_emoji = false; + bool is_imported = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_channel_post); PARSE_FLAG(is_outgoing); @@ -4885,6 +4888,7 @@ void MessagesManager::Message::parse(ParserT &parser) { PARSE_FLAG(is_pinned); PARSE_FLAG(has_interaction_info_update_date); PARSE_FLAG(has_send_emoji); + PARSE_FLAG(is_imported); END_PARSE_FLAGS(); } @@ -4926,6 +4930,7 @@ void MessagesManager::Message::parse(ParserT &parser) { if (has_forward_psa_type) { parse(forward_info->psa_type, parser); } + forward_info->is_imported = is_imported; } if (has_real_forward_from) { parse(real_forward_from_dialog_id, parser); @@ -24197,7 +24202,7 @@ int32 MessagesManager::get_message_schedule_date(const Message *m) { DialogId MessagesManager::get_message_original_sender(const Message *m) { if (m->forward_info != nullptr) { auto forward_info = m->forward_info.get(); - if (is_forward_info_sender_hidden(forward_info)) { + if (forward_info->is_imported || is_forward_info_sender_hidden(forward_info)) { return DialogId(); } if (forward_info->message_id.is_valid() || forward_info->sender_dialog_id.is_valid()) { @@ -25148,10 +25153,14 @@ tl_object_ptr MessagesManager::get_game_high_scores_obje } bool MessagesManager::is_forward_info_sender_hidden(const MessageForwardInfo *forward_info) { + CHECK(forward_info != nullptr); + if (forward_info->is_imported) { + return false; + } if (!forward_info->sender_name.empty()) { return true; } - DialogId hidden_sender_dialog_id(static_cast(G()->is_test_dc() ? -1000010460537ll : -1001228946795ll)); + DialogId hidden_sender_dialog_id(ChannelId(G()->is_test_dc() ? 10460537 : 1228946795)); return forward_info->sender_dialog_id == hidden_sender_dialog_id && !forward_info->author_signature.empty() && !forward_info->message_id.is_valid(); } @@ -25174,6 +25183,7 @@ unique_ptr MessagesManager::get_message_for DialogId from_dialog_id; MessageId from_message_id; string sender_name = std::move(forward_header->from_name_); + bool is_imported = forward_header->imported_; if (forward_header->from_id_ != nullptr) { sender_dialog_id = DialogId(forward_header->from_id_); if (!sender_dialog_id.is_valid()) { @@ -25231,7 +25241,7 @@ unique_ptr MessagesManager::get_message_for return td::make_unique(sender_user_id, forward_header->date_, sender_dialog_id, message_id, std::move(author_signature), std::move(sender_name), from_dialog_id, - from_message_id, std::move(forward_header->psa_type_)); + from_message_id, std::move(forward_header->psa_type_), is_imported); } td_api::object_ptr MessagesManager::get_message_forward_info_object( @@ -25241,6 +25251,9 @@ td_api::object_ptr MessagesManager::get_message_forw } auto origin = [&]() -> td_api::object_ptr { + if (forward_info->is_imported) { + return td_api::make_object(forward_info->sender_name); + } if (is_forward_info_sender_hidden(forward_info.get())) { return td_api::make_object( forward_info->sender_name.empty() ? forward_info->author_signature : forward_info->sender_name); @@ -25577,14 +25590,15 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i : forwarded_message->author_signature; forward_info = td::make_unique( UserId(), forwarded_message->date, from_dialog_id, forwarded_message->message_id, - std::move(author_signature), "", saved_from_dialog_id, saved_from_message_id, ""); + std::move(author_signature), "", saved_from_dialog_id, saved_from_message_id, "", false); } else { LOG(ERROR) << "Don't know how to forward a channel post not from a channel"; } } else if (forwarded_message->sender_user_id.is_valid() || forwarded_message->sender_dialog_id.is_valid()) { forward_info = td::make_unique( forwarded_message->sender_user_id, forwarded_message->date, forwarded_message->sender_dialog_id, - MessageId(), "", forwarded_message->author_signature, saved_from_dialog_id, saved_from_message_id, ""); + MessageId(), "", forwarded_message->author_signature, saved_from_dialog_id, saved_from_message_id, "", + false); } else { LOG(ERROR) << "Don't know how to forward a non-channel post message without forward info and sender"; } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 9e5ca5aea..cc3be9d85 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -963,12 +963,13 @@ class MessagesManager : public Actor { DialogId from_dialog_id; MessageId from_message_id; string psa_type; + bool is_imported = false; MessageForwardInfo() = default; MessageForwardInfo(UserId sender_user_id, int32 date, DialogId sender_dialog_id, MessageId message_id, string author_signature, string sender_name, DialogId from_dialog_id, MessageId from_message_id, - string psa_type) + string psa_type, bool is_imported) : sender_user_id(sender_user_id) , date(date) , sender_dialog_id(sender_dialog_id) @@ -977,14 +978,15 @@ class MessagesManager : public Actor { , sender_name(std::move(sender_name)) , from_dialog_id(from_dialog_id) , from_message_id(from_message_id) - , psa_type(psa_type) { + , psa_type(psa_type) + , is_imported(is_imported) { } bool operator==(const MessageForwardInfo &rhs) const { return sender_user_id == rhs.sender_user_id && date == rhs.date && sender_dialog_id == rhs.sender_dialog_id && message_id == rhs.message_id && author_signature == rhs.author_signature && sender_name == rhs.sender_name && from_dialog_id == rhs.from_dialog_id && - from_message_id == rhs.from_message_id && psa_type == rhs.psa_type; + from_message_id == rhs.from_message_id && psa_type == rhs.psa_type && is_imported == rhs.is_imported; } bool operator!=(const MessageForwardInfo &rhs) const { @@ -992,11 +994,13 @@ class MessagesManager : public Actor { } friend StringBuilder &operator<<(StringBuilder &string_builder, const MessageForwardInfo &forward_info) { - return string_builder << "MessageForwardInfo[sender " << forward_info.sender_user_id << "(" - << forward_info.author_signature << "/" << forward_info.sender_name << "), psa_type " - << forward_info.psa_type << ", source " << forward_info.sender_dialog_id << ", source " - << forward_info.message_id << ", from " << forward_info.from_dialog_id << ", from " - << forward_info.from_message_id << " at " << forward_info.date << "]"; + return string_builder << "MessageForwardInfo[" << (forward_info.is_imported ? "imported " : "") << "sender " + << forward_info.sender_user_id << "(" << forward_info.author_signature << "/" + << forward_info.sender_name << "), psa_type " << forward_info.psa_type << ", source " + << forward_info.sender_dialog_id << ", source " << forward_info.message_id << ", from " + << forward_info.from_dialog_id << ", from " << forward_info.from_message_id << " at " + << forward_info.date << " " + << "]"; } }; From 01f613f7bf7a1114479bbaff257ec07088e8617c Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 21 Jan 2021 13:40:22 +0300 Subject: [PATCH 066/232] Add user/supergroup.is_fake. --- td/generate/scheme/td_api.tl | 6 +++-- td/generate/scheme/td_api.tlo | Bin 194684 -> 194860 bytes td/telegram/ContactsManager.cpp | 40 +++++++++++++++++++--------- td/telegram/ContactsManager.h | 2 ++ td/telegram/NotificationManager.cpp | 11 ++++---- 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 6cd057850..808e89c3f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -403,10 +403,11 @@ inputChatPhotoAnimation animation:InputFile main_frame_timestamp:double = InputC //@is_support True, if the user is Telegram support account //@restriction_reason If non-empty, it contains a human-readable description of the reason why access to this user must be restricted //@is_scam True, if many users reported this user as a scam +//@is_fake True, if the user is fake as opposed to verified //@have_access If false, the user is inaccessible, and the only information known about the user is inside this class. It can't be passed to any method except GetUser //@type Type of the user //@language_code IETF language tag of the user's language; only available to bots -user id:int32 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_support:Bool restriction_reason:string is_scam:Bool have_access:Bool type:UserType language_code:string = User; +user id:int32 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string = User; //@description Contains full information about a user //@photo User profile photo; may be null @@ -607,7 +608,8 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 memb //@is_verified True, if the supergroup or channel is verified //@restriction_reason If non-empty, contains a human-readable description of the reason why access to this supergroup or channel must be restricted //@is_scam True, if many users reported this supergroup as a scam -supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_verified:Bool restriction_reason:string is_scam:Bool = Supergroup; +//@is_fake True, if this supergroup or channel is fake as opposed to verified +supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup; //@description Contains full information about a supergroup or channel //@photo Chat photo; may be null diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 6544a6a0d5fe07f1e438eb97c6dd57c355b2af33..96ff5dba67d5a3bc9908663a8b211b1d2c1c862e 100644 GIT binary patch delta 455 zcmezKfqTs-?hP|!SVU#4>?Y5VkzizieOYn(K^sO9Vc*o^;>7e+&)kCiq7nuMkjm+BbGBcx zV?5F*334r0acMznQF>8+X#oR>fAg4-58w8U2N<6-PB+kG;$%+DPMv%pL#0D8DIC-PH@?@RG9@`HH zFjX*4{@}#Ik_I$-x`Gf>2iU;vYlN6Y@+DvzK)xz+X4B!YZ951O+kT^wDMuJ$*M_M~ j77+KeOl1mS#I5JeG^RC-5a&KP2@FlJK^JB)t%(N!n;)fp delta 383 zcmZ4UiTlq7?hP|!SWJvAzMVWrMq;vxn8fBYG9m&jzPbPYZ+@av!om{%=i0f=F@{H& z7}J^+O|~nVFrH=yYncAQn(+;U^}vSF0>YB9Wz>PNd~6w0AgnF6j0O-Ek3FLagr#5y zQx{;z*Z>yW?qbh)q)`%NGgonGL26NYQGRIw1Bid~n2-{usYMJ73?MehB!S5r-IXWnEcVzQ zAjnj~IQ@kXQvukV?J>ekBKZ<9bwDi)Mb2zG9JXx-L1NoGnwWBgAyy online_member_dialogs; // id -> time - static constexpr uint32 CACHE_VERSION = 3; + static constexpr uint32 CACHE_VERSION = 4; uint32 cache_version = 0; bool is_min_access_hash = true; @@ -3164,6 +3164,7 @@ struct ContactsManager::User { bool is_inline_bot = false; bool need_location_bot = false; bool is_scam = false; + bool is_fake = false; bool is_contact = false; bool is_mutual_contact = false; bool need_apply_min_photo = false; @@ -3325,7 +3326,7 @@ struct ContactsManager::Channel { int32 date = 0; int32 participant_count = 0; - static constexpr uint32 CACHE_VERSION = 6; + static constexpr uint32 CACHE_VERSION = 7; uint32 cache_version = 0; bool has_linked_channel = false; @@ -3336,6 +3337,7 @@ struct ContactsManager::Channel { bool is_megagroup = false; bool is_verified = false; bool is_scam = false; + bool is_fake = false; bool is_title_changed = true; bool is_username_changed = true; @@ -3750,6 +3752,7 @@ void ContactsManager::User::store(StorerT &storer) const { STORE_FLAG(is_mutual_contact); STORE_FLAG(has_restriction_reasons); STORE_FLAG(need_apply_min_photo); + STORE_FLAG(is_fake); END_STORE_FLAGS(); store(first_name, storer); if (has_last_name) { @@ -3819,6 +3822,7 @@ void ContactsManager::User::parse(ParserT &parser) { PARSE_FLAG(is_mutual_contact); PARSE_FLAG(has_restriction_reasons); PARSE_FLAG(need_apply_min_photo); + PARSE_FLAG(is_fake); END_PARSE_FLAGS(); parse(first_name, parser); if (has_last_name) { @@ -4143,6 +4147,7 @@ void ContactsManager::Channel::store(StorerT &storer) const { STORE_FLAG(is_slow_mode_enabled); STORE_FLAG(has_restriction_reasons); STORE_FLAG(legacy_has_active_group_call); + STORE_FLAG(is_fake); END_STORE_FLAGS(); store(status, storer); @@ -4210,6 +4215,7 @@ void ContactsManager::Channel::parse(ParserT &parser) { PARSE_FLAG(is_slow_mode_enabled); PARSE_FLAG(has_restriction_reasons); PARSE_FLAG(legacy_has_active_group_call); + PARSE_FLAG(is_fake); END_PARSE_FLAGS(); if (use_new_rights) { @@ -7950,6 +7956,7 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, bool need_location_bot = (flags & USER_FLAG_NEED_LOCATION_BOT) != 0; bool has_bot_info_version = (flags & USER_FLAG_HAS_BOT_INFO_VERSION) != 0; bool need_apply_min_photo = (flags & USER_FLAG_NEED_APPLY_MIN_PHOTO) != 0; + bool is_fake = (flags & USER_FLAG_IS_FAKE) != 0; LOG_IF(ERROR, !is_support && expect_support) << "Receive non-support " << user_id << ", but expected a support user"; LOG_IF(ERROR, !can_join_groups && !is_bot) @@ -7980,8 +7987,9 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, int32 bot_info_version = has_bot_info_version ? user->bot_info_version_ : -1; if (is_verified != u->is_verified || is_support != u->is_support || is_bot != u->is_bot || can_join_groups != u->can_join_groups || can_read_all_group_messages != u->can_read_all_group_messages || - restriction_reasons != u->restriction_reasons || is_scam != u->is_scam || is_inline_bot != u->is_inline_bot || - inline_query_placeholder != u->inline_query_placeholder || need_location_bot != u->need_location_bot) { + restriction_reasons != u->restriction_reasons || is_scam != u->is_scam || is_fake != u->is_fake || + is_inline_bot != u->is_inline_bot || inline_query_placeholder != u->inline_query_placeholder || + need_location_bot != u->need_location_bot) { LOG_IF(ERROR, is_bot != u->is_bot && !is_deleted && !u->is_deleted && u->is_received) << "User.is_bot has changed for " << user_id << "/" << u->username << " from " << source << " from " << u->is_bot << " to " << is_bot; @@ -7992,6 +8000,7 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, u->can_read_all_group_messages = can_read_all_group_messages; u->restriction_reasons = std::move(restriction_reasons); u->is_scam = is_scam; + u->is_fake = is_fake; u->is_inline_bot = is_inline_bot; u->inline_query_placeholder = std::move(inline_query_placeholder); u->need_location_bot = need_location_bot; @@ -8335,9 +8344,9 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) { auto user = telegram_api::make_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, user_id.get(), 1, first_name, - string(), username, phone_number, std::move(profile_photo), nullptr, bot_info_version, Auto(), string(), - string()); + false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, user_id.get(), 1, + first_name, string(), username, phone_number, std::move(profile_photo), nullptr, bot_info_version, Auto(), + string(), string()); on_get_user(std::move(user), "get_user_force"); u = get_user(user_id); CHECK(u != nullptr && u->is_received); @@ -14576,6 +14585,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char bool is_verified = (channel.flags_ & CHANNEL_FLAG_IS_VERIFIED) != 0; auto restriction_reasons = get_restriction_reasons(std::move(channel.restriction_reason_)); bool is_scam = (channel.flags_ & CHANNEL_FLAG_IS_SCAM) != 0; + bool is_fake = (channel.flags_ & CHANNEL_FLAG_IS_FAKE) != 0; bool have_participant_count = (channel.flags_ & CHANNEL_FLAG_HAS_PARTICIPANT_COUNT) != 0; int32 participant_count = have_participant_count ? channel.participants_count_ : 0; @@ -14683,13 +14693,14 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char bool need_invalidate_channel_full = false; if (c->has_linked_channel != has_linked_channel || c->has_location != has_location || c->is_slow_mode_enabled != is_slow_mode_enabled || c->is_megagroup != is_megagroup || - c->restriction_reasons != restriction_reasons || c->is_scam != is_scam) { + c->restriction_reasons != restriction_reasons || c->is_scam != is_scam || c->is_fake != is_fake) { c->has_linked_channel = has_linked_channel; c->has_location = has_location; c->is_slow_mode_enabled = is_slow_mode_enabled; c->is_megagroup = is_megagroup; c->restriction_reasons = std::move(restriction_reasons); c->is_scam = is_scam; + c->is_fake = is_fake; c->is_changed = true; need_invalidate_channel_full = true; @@ -14770,6 +14781,7 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co bool is_megagroup = (channel.flags_ & CHANNEL_FLAG_IS_MEGAGROUP) != 0; bool is_verified = false; bool is_scam = false; + bool is_fake = false; { bool is_broadcast = (channel.flags_ & CHANNEL_FLAG_IS_BROADCAST) != 0; @@ -14784,13 +14796,14 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co bool need_invalidate_channel_full = false; if (c->is_slow_mode_enabled != is_slow_mode_enabled || c->is_megagroup != is_megagroup || - !c->restriction_reasons.empty() || c->is_scam != is_scam) { + !c->restriction_reasons.empty() || c->is_scam != is_scam || c->is_fake != is_fake) { // c->has_linked_channel = has_linked_channel; // c->has_location = has_location; c->is_slow_mode_enabled = is_slow_mode_enabled; c->is_megagroup = is_megagroup; c->restriction_reasons.clear(); c->is_scam = is_scam; + c->is_fake = is_fake; c->is_changed = true; need_invalidate_channel_full = true; @@ -14913,7 +14926,7 @@ td_api::object_ptr ContactsManager::get_user_status_object(U td_api::object_ptr ContactsManager::get_update_unknown_user_object(UserId user_id) { return td_api::make_object(td_api::make_object( user_id.get(), "", "", "", "", td_api::make_object(), nullptr, false, false, false, - false, "", false, false, td_api::make_object(), "")); + false, "", false, false, false, td_api::make_object(), "")); } int32 ContactsManager::get_user_id_object(UserId user_id, const char *source) const { @@ -14946,7 +14959,7 @@ tl_object_ptr ContactsManager::get_user_object(UserId user_id, con return make_tl_object( user_id.get(), u->first_name, u->last_name, u->username, u->phone_number, get_user_status_object(user_id, u), get_profile_photo_object(td_->file_manager_.get(), u->photo), u->is_contact, u->is_mutual_contact, u->is_verified, - u->is_support, get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_received, + u->is_support, get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_fake, u->is_received, std::move(type), u->language_code); } @@ -15031,7 +15044,7 @@ td_api::object_ptr ContactsManager::get_update_unknown ChannelId channel_id) { return td_api::make_object(td_api::make_object( channel_id.get(), string(), 0, DialogParticipantStatus::Banned(0).get_chat_member_status_object(), 0, false, - false, false, false, true, false, "", false)); + false, false, false, true, false, "", false, false)); } int32 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char *source) const { @@ -15054,7 +15067,8 @@ tl_object_ptr ContactsManager::get_supergroup_object(Channel return td_api::make_object( channel_id.get(), c->username, c->date, get_channel_status(c).get_chat_member_status_object(), c->participant_count, c->has_linked_channel, c->has_location, c->sign_messages, c->is_slow_mode_enabled, - !c->is_megagroup, c->is_verified, get_restriction_reason_description(c->restriction_reasons), c->is_scam); + !c->is_megagroup, c->is_verified, get_restriction_reason_description(c->restriction_reasons), c->is_scam, + c->is_fake); } tl_object_ptr ContactsManager::get_supergroup_full_info_object(ChannelId channel_id) const { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 67cf25121..4d7b62ee1 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -656,6 +656,7 @@ class ContactsManager : public Actor { static constexpr int32 USER_FLAG_IS_SUPPORT = 1 << 23; static constexpr int32 USER_FLAG_IS_SCAM = 1 << 24; static constexpr int32 USER_FLAG_NEED_APPLY_MIN_PHOTO = 1 << 25; + static constexpr int32 USER_FLAG_IS_FAKE = 1 << 26; static constexpr int32 USER_FULL_FLAG_IS_BLOCKED = 1 << 0; static constexpr int32 USER_FULL_FLAG_HAS_ABOUT = 1 << 1; @@ -702,6 +703,7 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FLAG_IS_SLOW_MODE_ENABLED = 1 << 22; static constexpr int32 CHANNEL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 23; static constexpr int32 CHANNEL_FLAG_IS_GROUP_CALL_NON_EMPTY = 1 << 24; + static constexpr int32 CHANNEL_FLAG_IS_FAKE = 1 << 25; static constexpr int32 CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 0; static constexpr int32 CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT = 1 << 1; diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index 26f143dce..f61734e2b 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -3269,9 +3269,9 @@ Status NotificationManager::process_push_notification_payload(string payload, bo auto user = telegram_api::make_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), - sender_access_hash, user_name, string(), string(), string(), std::move(sender_photo), nullptr, 0, Auto(), - string(), string()); + false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, + sender_user_id.get(), sender_access_hash, user_name, string(), string(), string(), std::move(sender_photo), + nullptr, 0, Auto(), string(), string()); td_->contacts_manager_->on_get_user(std::move(user), "process_push_notification_payload"); } @@ -3606,8 +3606,9 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess auto user = telegram_api::make_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), 0, user_name, - string(), string(), string(), nullptr, nullptr, 0, Auto(), string(), string()); + false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, + sender_user_id.get(), 0, user_name, string(), string(), string(), nullptr, nullptr, 0, Auto(), string(), + string()); td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification"); } From 516f9cb9ce189dd636c18a0490d91a79f7803e64 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 21 Jan 2021 22:00:08 +0300 Subject: [PATCH 067/232] Add importMessages method. --- td/generate/scheme/td_api.tl | 7 + td/generate/scheme/td_api.tlo | Bin 194860 -> 195048 bytes td/telegram/MessagesManager.cpp | 248 ++++++++++++++++++++++++++++++++ td/telegram/MessagesManager.h | 32 ++++- td/telegram/Td.cpp | 7 + td/telegram/Td.h | 2 + td/telegram/cli.cpp | 15 +- 7 files changed, 307 insertions(+), 4 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 808e89c3f..01bdf2329 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4385,6 +4385,13 @@ readFilePart file_id:int32 offset:int32 count:int32 = FilePart; deleteFile file_id:int32 = Ok; +//@description Imports messages exported from another app +//@chat_id Identifier of a chat to which the messages will be imported. It must be an identifier of a private chat with a mutual contact or an identifier of a created supergroup chat +//@message_file File with messages to import. Only inputFileLocal and inputFileGenerated are supported. The file must not be previously uploaded +//@attached_files Files used in the imported messages. Only inputFileLocal and inputFileGenerated are supported. The files must not be previously uploaded +importMessages chat_id:int53 message_file:InputFile attached_files:vector = Ok; + + //@description Replaces current permanent invite link for a chat with a new permanent invite link. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right @chat_id Chat identifier replacePermanentChatInviteLink chat_id:int53 = ChatInviteLink; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 96ff5dba67d5a3bc9908663a8b211b1d2c1c862e..8539abddbbd20ca0bb5766bc02a2e049e53eacc7 100644 GIT binary patch delta 98 zcmZ4UiTlN8?uHh|EleU>jI+0kYBALm30-|WfiE++Ait=@H?_DpF+H_-I=>H-ArDV3 xgd3lhnKRkvwu}H@Vo6D2az<(jNF=qGVRC?*%;XKXMW%nyViMRsVGh%r7ywk5CA|Ou delta 27 jcmaFynS0GA?uHh|EleU>jI*|jYBALmZTFqaG&=?WqHPMK diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 7031c9b93..837a8f4c4 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1009,6 +1009,92 @@ class CreateChannelQuery : public Td::ResultHandler { } }; +class InitHistoryImportQuery : public Td::ResultHandler { + Promise promise_; + FileId file_id_; + DialogId dialog_id_; + vector attached_file_ids_; + + public: + explicit InitHistoryImportQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, FileId file_id, tl_object_ptr &&input_file, + vector attached_file_ids) { + CHECK(input_file != nullptr); + file_id_ = file_id; + dialog_id_ = dialog_id; + attached_file_ids_ = std::move(attached_file_ids); + attached_file_ids_.clear(); + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + CHECK(input_peer != nullptr); + send_query(G()->net_query_creator().create(telegram_api::messages_initHistoryImport( + std::move(input_peer), std::move(input_file), narrow_cast(attached_file_ids_.size())))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + td->file_manager_->delete_partial_remote_location(file_id_); + + auto ptr = result_ptr.move_as_ok(); + td->messages_manager_->start_import_messages(dialog_id_, ptr->id_, std::move(attached_file_ids_), + std::move(promise_)); + } + + void on_error(uint64 id, Status status) override { + td->file_manager_->delete_partial_remote_location(file_id_); + if (!td->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) { + LOG(ERROR) << "Receive file reference error " << status; + } + + // TODO support FILE_PART_*_MISSING + + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "InitHistoryImportQuery"); + promise_.set_error(std::move(status)); + } +}; + +class StartImportHistoryQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit StartImportHistoryQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, int64 import_id) { + dialog_id_ = dialog_id; + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + CHECK(input_peer != nullptr); + + send_query( + G()->net_query_creator().create(telegram_api::messages_startHistoryImport(std::move(input_peer), import_id))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + if (!result_ptr.ok()) { + return on_error(id, Status::Error(500, "Import history returned false")); + } + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "StartImportHistoryQuery"); + promise_.set_error(std::move(status)); + } +}; + class EditDialogPhotoQuery : public Td::ResultHandler { Promise promise_; FileId file_id_; @@ -4579,6 +4665,24 @@ class MessagesManager::UploadDialogPhotoCallback : public FileManager::UploadCal } }; +class MessagesManager::UploadImportedMessagesCallback : public FileManager::UploadCallback { + public: + void on_upload_ok(FileId file_id, tl_object_ptr input_file) override { + send_closure_later(G()->messages_manager(), &MessagesManager::on_upload_imported_messages, file_id, + std::move(input_file)); + } + void on_upload_encrypted_ok(FileId file_id, tl_object_ptr input_file) override { + UNREACHABLE(); + } + void on_upload_secure_ok(FileId file_id, tl_object_ptr input_file) override { + UNREACHABLE(); + } + void on_upload_error(FileId file_id, Status error) override { + send_closure_later(G()->messages_manager(), &MessagesManager::on_upload_imported_messages_error, file_id, + std::move(error)); + } +}; + template void MessagesManager::Message::store(StorerT &storer) const { using td::store; @@ -5513,6 +5617,7 @@ MessagesManager::MessagesManager(Td *td, ActorShared<> parent) : td_(td), parent upload_media_callback_ = std::make_shared(); upload_thumbnail_callback_ = std::make_shared(); upload_dialog_photo_callback_ = std::make_shared(); + upload_imported_messages_callback_ = std::make_shared(); channel_get_difference_timeout_.set_callback(on_channel_get_difference_timeout_callback); channel_get_difference_timeout_.set_callback_data(static_cast(this)); @@ -8555,6 +8660,70 @@ void MessagesManager::on_upload_dialog_photo_error(FileId file_id, Status status promise.set_error(std::move(status)); } +void MessagesManager::on_upload_imported_messages(FileId file_id, tl_object_ptr input_file) { + LOG(INFO) << "File " << file_id << " has been uploaded"; + + auto it = being_uploaded_imported_messages_.find(file_id); + if (it == being_uploaded_imported_messages_.end()) { + // just in case, as in on_upload_media + return; + } + + CHECK(it->second != nullptr); + DialogId dialog_id = it->second->dialog_id; + vector attached_file_ids = std::move(it->second->attached_file_ids); + bool is_reupload = it->second->is_reupload; + Promise promise = std::move(it->second->promise); + + being_uploaded_imported_messages_.erase(it); + + TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); + + FileView file_view = td_->file_manager_->get_file_view(file_id); + CHECK(!file_view.is_encrypted()); + if (input_file == nullptr && file_view.has_remote_location()) { + if (file_view.main_remote_location().is_web()) { + return promise.set_error(Status::Error(400, "Can't use web photo")); + } + if (is_reupload) { + return promise.set_error(Status::Error(400, "Failed to reupload the file")); + } + + CHECK(file_view.get_type() == FileType::Document); + // delete file reference and forcely reupload the file + auto file_reference = FileManager::extract_file_reference(file_view.main_remote_location().as_input_document()); + td_->file_manager_->delete_file_reference(file_id, file_reference); + upload_imported_messages(dialog_id, file_id, std::move(attached_file_ids), true, std::move(promise), {-1}); + return; + } + CHECK(input_file != nullptr); + + td_->create_handler(std::move(promise)) + ->send(dialog_id, file_id, std::move(input_file), std::move(attached_file_ids)); +} + +void MessagesManager::on_upload_imported_messages_error(FileId file_id, Status status) { + if (G()->close_flag()) { + // do not fail upload if closing + return; + } + + LOG(INFO) << "File " << file_id << " has upload error " << status; + CHECK(status.is_error()); + + auto it = being_uploaded_imported_messages_.find(file_id); + if (it == being_uploaded_imported_messages_.end()) { + // just in case, as in on_upload_media_error + return; + } + + Promise promise = std::move(it->second->promise); + + being_uploaded_imported_messages_.erase(it); + + promise.set_error(std::move(status)); +} + void MessagesManager::before_get_difference() { running_get_difference_ = true; @@ -26094,6 +26263,85 @@ Result MessagesManager::add_local_message( return message_id; } +void MessagesManager::import_messages(DialogId dialog_id, const td_api::object_ptr &message_file, + const vector> &attached_files, + Promise &&promise) { + if (!have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); + + switch (dialog_id.get_type()) { + case DialogType::User: + if (!td_->contacts_manager_->is_user_contact(dialog_id.get_user_id(), true)) { + return promise.set_error(Status::Error(400, "User must be a mutual contact")); + } + break; + case DialogType::Chat: + return promise.set_error(Status::Error(400, "Basic groups must be updagraded to supergroups first")); + case DialogType::Channel: + if (is_broadcast_channel(dialog_id)) { + return promise.set_error(Status::Error(400, "Can't import messages to channels")); + } + if (!td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).is_creator()) { + return promise.set_error(Status::Error(400, "Not enough rights to import messages")); + } + break; + case DialogType::SecretChat: + return promise.set_error(Status::Error(400, "Can't import messages to secret chats")); + case DialogType::None: + default: + UNREACHABLE(); + } + + auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Document, message_file, dialog_id, false, false); + if (r_file_id.is_error()) { + // TODO TRY_RESULT_PROMISE(promise, ...); + return promise.set_error(Status::Error(400, r_file_id.error().message())); + } + FileId file_id = r_file_id.ok(); + + vector attached_file_ids; + attached_file_ids.reserve(attached_files.size()); + for (auto &attached_file : attached_files) { + auto r_attached_file_id = + td_->file_manager_->get_input_file_id(FileType::Document, attached_file, dialog_id, false, false); + if (r_attached_file_id.is_error()) { + // TODO TRY_RESULT_PROMISE(promise, ...); + return promise.set_error(Status::Error(400, r_attached_file_id.error().message())); + } + attached_file_ids.push_back(r_attached_file_id.ok()); + } + + upload_imported_messages(dialog_id, td_->file_manager_->dup_file_id(file_id), std::move(attached_file_ids), false, + std::move(promise)); +} + +void MessagesManager::upload_imported_messages(DialogId dialog_id, FileId file_id, vector attached_file_ids, + bool is_reupload, Promise &&promise, vector bad_parts) { + CHECK(file_id.is_valid()); + LOG(INFO) << "Ask to upload imported messages file " << file_id; + CHECK(being_uploaded_imported_messages_.find(file_id) == being_uploaded_imported_messages_.end()); + being_uploaded_imported_messages_.emplace( + file_id, td::make_unique(dialog_id, std::move(attached_file_ids), is_reupload, + std::move(promise))); + // TODO use force_reupload if is_reupload + td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_imported_messages_callback_, 1, 0); +} + +void MessagesManager::start_import_messages(DialogId dialog_id, int64 import_id, vector &&attached_file_ids, + Promise &&promise) { + CHECK(attached_file_ids.empty()); + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); + + td_->create_handler(std::move(promise))->send(dialog_id, import_id); +} + bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_message_id, const string &source) { if (!new_message_id.is_valid() || !new_message_id.is_server()) { LOG(ERROR) << "Receive " << new_message_id << " in updateMessageId with random_id " << random_id << " from " diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index cc3be9d85..16b94d513 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -394,6 +394,12 @@ class MessagesManager : public Actor { tl_object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; + void import_messages(DialogId dialog_id, const td_api::object_ptr &message_file, + const vector> &attached_files, Promise &&promise); + + void start_import_messages(DialogId dialog_id, int64 import_id, vector &&attached_file_ids, + Promise &&promise); + void edit_message_text(FullMessageId full_message_id, tl_object_ptr &&reply_markup, tl_object_ptr &&input_message_content, Promise &&promise); @@ -890,6 +896,9 @@ class MessagesManager : public Actor { void upload_dialog_photo(DialogId dialog_id, FileId file_id, bool is_animation, double main_frame_timestamp, bool is_reupload, Promise &&promise, vector bad_parts = {}); + void upload_imported_messages(DialogId dialog_id, FileId file_id, vector attached_file_ids, bool is_reupload, + Promise &&promise, vector bad_parts = {}); + void on_binlog_events(vector &&events); void set_poll_answer(FullMessageId full_message_id, vector &&option_ids, Promise &&promise); @@ -2824,6 +2833,9 @@ class MessagesManager : public Actor { tl_object_ptr &&input_chat_photo, Promise &&promise); + void on_upload_imported_messages(FileId file_id, tl_object_ptr input_file); + void on_upload_imported_messages_error(FileId file_id, Status status); + void add_sponsored_dialog(const Dialog *d, DialogSource source); void save_sponsored_dialog(); @@ -2956,10 +2968,12 @@ class MessagesManager : public Actor { class UploadMediaCallback; class UploadThumbnailCallback; class UploadDialogPhotoCallback; + class UploadImportedMessagesCallback; std::shared_ptr upload_media_callback_; std::shared_ptr upload_thumbnail_callback_; std::shared_ptr upload_dialog_photo_callback_; + std::shared_ptr upload_imported_messages_callback_; double last_channel_pts_jump_warning_time_ = 0; @@ -3040,7 +3054,23 @@ class MessagesManager : public Actor { , promise(std::move(promise)) { } }; - std::unordered_map being_uploaded_dialog_photos_; // file_id -> ... + std::unordered_map being_uploaded_dialog_photos_; + + struct UploadedImportedMessagesInfo { + DialogId dialog_id; + vector attached_file_ids; + bool is_reupload; + Promise promise; + + UploadedImportedMessagesInfo(DialogId dialog_id, vector &&attached_file_ids, bool is_reupload, + Promise &&promise) + : dialog_id(dialog_id) + , attached_file_ids(std::move(attached_file_ids)) + , is_reupload(is_reupload) + , promise(std::move(promise)) { + } + }; + std::unordered_map, FileIdHash> being_uploaded_imported_messages_; struct PendingMessageGroupSend { DialogId dialog_id; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 97a6e6244..eee7d2cbc 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6535,6 +6535,13 @@ void Td::on_request(uint64 id, const td_api::deleteFile &request) { "td_api::deleteFile"); } +void Td::on_request(uint64 id, const td_api::importMessages &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->import_messages(DialogId(request.chat_id_), request.message_file_, request.attached_files_, + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::blockMessageSenderFromReplies &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 7aadd9fc7..4607683de 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -836,6 +836,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::deleteFile &request); + void on_request(uint64 id, const td_api::importMessages &request); + void on_request(uint64 id, const td_api::blockMessageSenderFromReplies &request); void on_request(uint64 id, const td_api::getBlockedMessageSenders &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index ec9a5abe0..ad955338e 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2968,6 +2968,15 @@ class CliClient final : public Actor { as_input_file(document), nullptr, true, as_caption("")); return content; }))); + } else if (op == "im") { + string chat_id; + string message_file; + vector attached_files; + get_args(args, chat_id, message_file, args); + attached_files = full_split(args); + send_request(td_api::make_object( + as_chat_id(chat_id), as_input_file(message_file), + transform(attached_files, [](const string &attached_file) { return as_input_file(attached_file); }))); } else if (op == "em") { string chat_id; string message_id; @@ -3412,11 +3421,11 @@ class CliClient final : public Actor { string title; get_args(args, user_ids_string, title); send_request(td_api::make_object(as_user_ids(user_ids_string), title)); - } else if (op == "cnch") { + } else if (op == "cnchc") { send_request(td_api::make_object(args, true, "Description", nullptr)); - } else if (op == "cnsg") { + } else if (op == "cnsgc") { send_request(td_api::make_object(args, false, "Description", nullptr)); - } else if (op == "cngc") { + } else if (op == "cnsgcloc") { send_request(td_api::make_object( args, false, "Description", td_api::make_object(as_location("40.0", "60.0"), "address"))); From b8d8f18b915eb2dd80a7514ff84fbbb4fd8186af Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 00:55:42 +0300 Subject: [PATCH 068/232] Add chatReportReasonFake. --- td/generate/scheme/td_api.tl | 9 ++++++--- td/generate/scheme/td_api.tlo | Bin 195048 -> 195112 bytes td/telegram/MessagesManager.cpp | 3 +++ td/telegram/cli.cpp | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 01bdf2329..c607f9ae5 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -403,7 +403,7 @@ inputChatPhotoAnimation animation:InputFile main_frame_timestamp:double = InputC //@is_support True, if the user is Telegram support account //@restriction_reason If non-empty, it contains a human-readable description of the reason why access to this user must be restricted //@is_scam True, if many users reported this user as a scam -//@is_fake True, if the user is fake as opposed to verified +//@is_fake True, if many users reported this user as a fake account //@have_access If false, the user is inaccessible, and the only information known about the user is inside this class. It can't be passed to any method except GetUser //@type Type of the user //@language_code IETF language tag of the user's language; only available to bots @@ -607,8 +607,8 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 memb //@is_channel True, if the supergroup is a channel //@is_verified True, if the supergroup or channel is verified //@restriction_reason If non-empty, contains a human-readable description of the reason why access to this supergroup or channel must be restricted -//@is_scam True, if many users reported this supergroup as a scam -//@is_fake True, if this supergroup or channel is fake as opposed to verified +//@is_scam True, if many users reported this supergroup or channel as a scam +//@is_fake True, if many users reported this supergroup or channel as a fake account supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup; //@description Contains full information about a supergroup or channel @@ -2894,6 +2894,9 @@ chatReportReasonCopyright = ChatReportReason; //@description The location-based chat is unrelated to its stated location chatReportReasonUnrelatedLocation = ChatReportReason; +//@description The chat represents a fake account +chatReportReasonFake = ChatReportReason; + //@description A custom reason provided by the user @text Report text chatReportReasonCustom text:string = ChatReportReason; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 8539abddbbd20ca0bb5766bc02a2e049e53eacc7..c8d19851c02934d9b35b8ea76cdce006f2276195 100644 GIT binary patch delta 339 zcmaFynR~?-?hP8;lIyQ$SqeC3B$foF7UUNJ$;9IPJO%~^j?IqT_ca(ZHY=JOj*i zW{M=EdCe^`I~6F8OaJ7E2^uUQ$>{+L8D%Cfm>>e?HSd_vzGDL8_8k+La^zV-@(j}# T`Z3v0S6Id*vAtmfQ-&u1q?JB$foF7UUNJ$;9IPJO%~^_RWsm_ca*PH!GSPj*@y^ZAAb8 diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 837a8f4c4..eada18967 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8046,6 +8046,9 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr(); break; + case td_api::chatReportReasonFake::ID: + report_reason = make_tl_object(); + break; case td_api::chatReportReasonUnrelatedLocation::ID: report_reason = make_tl_object(); if (dialog_id.get_type() == DialogType::Channel) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index ad955338e..cf3798460 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1336,6 +1336,9 @@ class CliClient final : public Actor { if (reason == "geo" || reason == "location") { return td_api::make_object(); } + if (reason == "fake") { + return td_api::make_object(); + } return td_api::make_object(reason.str()); } From 6f38576467f8998ac2c9c3dcab240574b42c2dc6 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 15:17:56 +0300 Subject: [PATCH 069/232] Add FileManager::guess_file_type. --- td/telegram/MessagesManager.cpp | 10 ++++- td/telegram/files/FileManager.cpp | 64 +++++++++++++++++++++++++++++++ td/telegram/files/FileManager.h | 2 + 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index eada18967..89624e62b 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -26308,8 +26308,14 @@ void MessagesManager::import_messages(DialogId dialog_id, const td_api::object_p vector attached_file_ids; attached_file_ids.reserve(attached_files.size()); for (auto &attached_file : attached_files) { - auto r_attached_file_id = - td_->file_manager_->get_input_file_id(FileType::Document, attached_file, dialog_id, false, false); + auto file_type = td_->file_manager_->guess_file_type(attached_file); + if (file_type != FileType::Animation && file_type != FileType::Audio && file_type != FileType::Document && + file_type != FileType::Photo && file_type != FileType::Sticker && file_type != FileType::Video && + file_type != FileType::VoiceNote) { + LOG(INFO) << "Skip attached file of type " << file_type; + continue; + } + auto r_attached_file_id = td_->file_manager_->get_input_file_id(file_type, attached_file, dialog_id, false, false); if (r_attached_file_id.is_error()) { // TODO TRY_RESULT_PROMISE(promise, ...); return promise.set_error(Status::Error(400, r_attached_file_id.error().message())); diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index 2fb439fbb..ae6f01371 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -3162,6 +3162,70 @@ Result FileManager::get_map_thumbnail_file_id(Location location, int32 z FileLocationSource::FromUser, string(), std::move(conversion), owner_dialog_id, 0); } +FileType FileManager::guess_file_type(const tl_object_ptr &file) { + if (file == nullptr) { + return FileType::Temp; + } + + auto guess_file_type_by_path = [](const string &file_path) { + PathView path_view(file_path); + auto file_name = path_view.file_name(); + auto extension = path_view.extension(); + if (extension == "jpg" || extension == "jpeg") { + return FileType::Photo; + } + if (extension == "ogg" || extension == "oga" || extension == "opus") { + return FileType::VoiceNote; + } + if (extension == "3gp" || extension == "mov") { + return FileType::Video; + } + if (extension == "mp3" || extension == "mpeg3" || extension == "m4a") { + return FileType::Audio; + } + if (extension == "webp" || extension == "tgs" || extension == "opus") { + return FileType::Sticker; + } + if (extension == "gif") { + return FileType::Animation; + } + if (extension == "mp4" || extension == "mpeg4") { + return to_lower(file_name).find("-gif-") != string::npos ? FileType::Animation : FileType::Video; + } + return FileType::Document; + }; + + switch (file->get_id()) { + case td_api::inputFileLocal::ID: + return guess_file_type_by_path(static_cast(file.get())->path_); + case td_api::inputFileId::ID: { + FileId file_id(static_cast(file.get())->id_, 0); + auto file_view = get_file_view(file_id); + if (file_view.empty()) { + return FileType::Temp; + } + return file_view.get_type(); + } + case td_api::inputFileRemote::ID: { + const string &file_persistent_id = static_cast(file.get())->id_; + Result r_file_id = from_persistent_id(file_persistent_id, FileType::Temp); + if (r_file_id.is_error()) { + return FileType::Temp; + } + auto file_view = get_file_view(r_file_id.ok()); + if (file_view.empty()) { + return FileType::Temp; + } + return file_view.get_type(); + } + case td_api::inputFileGenerated::ID: + return guess_file_type_by_path(static_cast(file.get())->original_path_); + default: + UNREACHABLE(); + return FileType::Temp; + } +} + vector> FileManager::get_input_documents(const vector &file_ids) { vector> result; result.reserve(file_ids.size()); diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index 8fd2b5654..60332ac1c 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -474,6 +474,8 @@ class FileManager : public FileLoadManager::Callback { Result get_map_thumbnail_file_id(Location location, int32 zoom, int32 width, int32 height, int32 scale, DialogId owner_dialog_id) TD_WARN_UNUSED_RESULT; + FileType guess_file_type(const tl_object_ptr &file); + vector> get_input_documents(const vector &file_ids); static bool extract_was_uploaded(const tl_object_ptr &input_media); From f307200ddc8dd6afb981d76cf4fd433d09665eda Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 17:09:08 +0300 Subject: [PATCH 070/232] Support attachment upload in importMessages. --- td/telegram/AnimationsManager.cpp | 2 +- td/telegram/MessageContent.cpp | 34 +++++ td/telegram/MessageContent.h | 3 + td/telegram/MessagesManager.cpp | 216 +++++++++++++++++++++++++++++- td/telegram/MessagesManager.h | 38 +++++- 5 files changed, 283 insertions(+), 10 deletions(-) diff --git a/td/telegram/AnimationsManager.cpp b/td/telegram/AnimationsManager.cpp index 32401f987..b2d20b24d 100644 --- a/td/telegram/AnimationsManager.cpp +++ b/td/telegram/AnimationsManager.cpp @@ -391,7 +391,7 @@ tl_object_ptr AnimationsManager::get_input_media( } return make_tl_object( flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type, - std::move(attributes), vector>(), 0); + std::move(attributes), std::move(added_stickers), 0); } else { CHECK(!file_view.has_remote_location()); } diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index d10679aeb..a124dafba 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -2496,6 +2496,40 @@ tl_object_ptr get_input_media(const MessageContent *co return input_media; } +tl_object_ptr get_fake_input_media(Td *td, tl_object_ptr input_file, + FileId file_id) { + FileView file_view = td->file_manager_->get_file_view(file_id); + auto file_type = file_view.get_type(); + switch (file_type) { + case FileType::Animation: + case FileType::Audio: + case FileType::Document: + case FileType::Sticker: + case FileType::Video: + case FileType::VoiceNote: { + vector> attributes; + auto file_name = file_view.suggested_name(); + if (!file_name.empty()) { + attributes.push_back(make_tl_object(file_name)); + } + string mime_type = MimeType::from_extension(PathView(file_name).extension()); + int32 flags = 0; + if (file_type == FileType::Video) { + flags |= telegram_api::inputMediaUploadedDocument::NOSOUND_VIDEO_MASK; + } + return make_tl_object( + flags, false /*ignored*/, false /*ignored*/, std::move(input_file), nullptr, mime_type, std::move(attributes), + vector>(), 0); + } + case FileType::Photo: + return make_tl_object( + 0, std::move(input_file), vector>(), 0); + default: + UNREACHABLE(); + } + return nullptr; +} + void delete_message_content_thumbnail(MessageContent *content, Td *td) { switch (content->get_type()) { case MessageContentType::Animation: { diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h index c04038a6c..398d2d4c4 100644 --- a/td/telegram/MessageContent.h +++ b/td/telegram/MessageContent.h @@ -117,6 +117,9 @@ tl_object_ptr get_input_media(const MessageContent *co tl_object_ptr get_input_media(const MessageContent *content, Td *td, int32 ttl, const string &emoji, bool force); +tl_object_ptr get_fake_input_media(Td *td, tl_object_ptr input_file, + FileId file_id); + void delete_message_content_thumbnail(MessageContent *content, Td *td); bool can_forward_message_content(const MessageContent *content); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 89624e62b..f15649056 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1025,7 +1025,6 @@ class InitHistoryImportQuery : public Td::ResultHandler { file_id_ = file_id; dialog_id_ = dialog_id; attached_file_ids_ = std::move(attached_file_ids); - attached_file_ids_.clear(); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); CHECK(input_peer != nullptr); @@ -1047,18 +1046,73 @@ class InitHistoryImportQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - td->file_manager_->delete_partial_remote_location(file_id_); if (!td->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) { LOG(ERROR) << "Receive file reference error " << status; } + if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) { + // TODO support FILE_PART_*_MISSING + } - // TODO support FILE_PART_*_MISSING + td->file_manager_->delete_partial_remote_location(file_id_); td->messages_manager_->on_get_dialog_error(dialog_id_, status, "InitHistoryImportQuery"); promise_.set_error(std::move(status)); } }; +class UploadImportedMediaQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + int64 import_id_; + FileId file_id_; + + public: + explicit UploadImportedMediaQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, int64 import_id, const string &file_name, FileId file_id, + tl_object_ptr &&input_media) { + CHECK(input_media != nullptr); + dialog_id_ = dialog_id; + import_id_ = import_id; + file_id_ = file_id; + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Have no write access to the chat")); + } + + send_query(G()->net_query_creator().create(telegram_api::messages_uploadImportedMedia( + std::move(input_peer), import_id, file_name, std::move(input_media)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + td->file_manager_->delete_partial_remote_location(file_id_); + + // ignore response + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + if (FileReferenceManager::is_file_reference_error(status)) { + LOG(ERROR) << "Receive file reference error " << status; + } + if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) { + // TODO support FILE_PART_*_MISSING + } + + td->file_manager_->delete_partial_remote_location(file_id_); + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "UploadImportedMediaQuery"); + promise_.set_error(std::move(status)); + } +}; + class StartImportHistoryQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -4683,6 +4737,24 @@ class MessagesManager::UploadImportedMessagesCallback : public FileManager::Uplo } }; +class MessagesManager::UploadImportedMessageAttachmentCallback : public FileManager::UploadCallback { + public: + void on_upload_ok(FileId file_id, tl_object_ptr input_file) override { + send_closure_later(G()->messages_manager(), &MessagesManager::on_upload_imported_message_attachment, file_id, + std::move(input_file)); + } + void on_upload_encrypted_ok(FileId file_id, tl_object_ptr input_file) override { + UNREACHABLE(); + } + void on_upload_secure_ok(FileId file_id, tl_object_ptr input_file) override { + UNREACHABLE(); + } + void on_upload_error(FileId file_id, Status error) override { + send_closure_later(G()->messages_manager(), &MessagesManager::on_upload_imported_message_attachment_error, file_id, + std::move(error)); + } +}; + template void MessagesManager::Message::store(StorerT &storer) const { using td::store; @@ -5618,6 +5690,7 @@ MessagesManager::MessagesManager(Td *td, ActorShared<> parent) : td_(td), parent upload_thumbnail_callback_ = std::make_shared(); upload_dialog_photo_callback_ = std::make_shared(); upload_imported_messages_callback_ = std::make_shared(); + upload_imported_message_attachment_callback_ = std::make_shared(); channel_get_difference_timeout_.set_callback(on_channel_get_difference_timeout_callback); channel_get_difference_timeout_.set_callback_data(static_cast(this)); @@ -8686,7 +8759,7 @@ void MessagesManager::on_upload_imported_messages(FileId file_id, tl_object_ptr< CHECK(!file_view.is_encrypted()); if (input_file == nullptr && file_view.has_remote_location()) { if (file_view.main_remote_location().is_web()) { - return promise.set_error(Status::Error(400, "Can't use web photo")); + return promise.set_error(Status::Error(400, "Can't use web file")); } if (is_reupload) { return promise.set_error(Status::Error(400, "Failed to reupload the file")); @@ -8727,6 +8800,72 @@ void MessagesManager::on_upload_imported_messages_error(FileId file_id, Status s promise.set_error(std::move(status)); } +void MessagesManager::on_upload_imported_message_attachment(FileId file_id, + tl_object_ptr input_file) { + LOG(INFO) << "File " << file_id << " has been uploaded"; + + auto it = being_uploaded_imported_message_attachments_.find(file_id); + if (it == being_uploaded_imported_message_attachments_.end()) { + // just in case, as in on_upload_media + return; + } + + CHECK(it->second != nullptr); + DialogId dialog_id = it->second->dialog_id; + int64 import_id = it->second->import_id; + bool is_reupload = it->second->is_reupload; + Promise promise = std::move(it->second->promise); + + being_uploaded_imported_message_attachments_.erase(it); + + FileView file_view = td_->file_manager_->get_file_view(file_id); + CHECK(!file_view.is_encrypted()); + if (input_file == nullptr && file_view.has_remote_location()) { + if (file_view.main_remote_location().is_web()) { + return promise.set_error(Status::Error(400, "Can't use web file")); + } + if (is_reupload) { + return promise.set_error(Status::Error(400, "Failed to reupload the file")); + } + + // delete file reference and forcely reupload the file + auto file_reference = + file_view.get_type() == FileType::Photo + ? FileManager::extract_file_reference(file_view.main_remote_location().as_input_photo()) + : FileManager::extract_file_reference(file_view.main_remote_location().as_input_document()); + td_->file_manager_->delete_file_reference(file_id, file_reference); + upload_imported_message_attachment(dialog_id, import_id, file_id, true, std::move(promise), {-1}); + return; + } + CHECK(input_file != nullptr); + + td_->create_handler(std::move(promise)) + ->send(dialog_id, import_id, file_view.suggested_name(), file_id, + get_fake_input_media(td_, std::move(input_file), file_id)); +} + +void MessagesManager::on_upload_imported_message_attachment_error(FileId file_id, Status status) { + if (G()->close_flag()) { + // do not fail upload if closing + return; + } + + LOG(INFO) << "File " << file_id << " has upload error " << status; + CHECK(status.is_error()); + + auto it = being_uploaded_imported_message_attachments_.find(file_id); + if (it == being_uploaded_imported_message_attachments_.end()) { + // just in case, as in on_upload_media_error + return; + } + + Promise promise = std::move(it->second->promise); + + being_uploaded_imported_message_attachments_.erase(it); + + promise.set_error(std::move(status)); +} + void MessagesManager::before_get_difference() { running_get_difference_ = true; @@ -26341,14 +26480,79 @@ void MessagesManager::upload_imported_messages(DialogId dialog_id, FileId file_i void MessagesManager::start_import_messages(DialogId dialog_id, int64 import_id, vector &&attached_file_ids, Promise &&promise) { - CHECK(attached_file_ids.empty()); if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); } TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); - td_->create_handler(std::move(promise))->send(dialog_id, import_id); + auto pending_message_import = make_unique(); + pending_message_import->dialog_id = dialog_id; + pending_message_import->import_id = import_id; + pending_message_import->promise = std::move(promise); + + auto &multipromise = pending_message_import->upload_files_multipromise; + + int64 random_id; + do { + random_id = Random::secure_int64(); + } while (random_id == 0 || pending_message_imports_.find(random_id) != pending_message_imports_.end()); + pending_message_imports_[random_id] = std::move(pending_message_import); + + multipromise.add_promise(PromiseCreator::lambda([random_id](Result result) { + send_closure_later(G()->messages_manager(), &MessagesManager::on_imported_message_attachments_uploaded, random_id, + std::move(result)); + })); + auto lock_promise = multipromise.get_promise(); + + for (auto attached_file_id : attached_file_ids) { + upload_imported_message_attachment(dialog_id, import_id, td_->file_manager_->dup_file_id(attached_file_id), false, + multipromise.get_promise()); + } + + lock_promise.set_value(Unit()); +} + +void MessagesManager::upload_imported_message_attachment(DialogId dialog_id, int64 import_id, FileId file_id, + bool is_reupload, Promise &&promise, + vector bad_parts) { + CHECK(file_id.is_valid()); + LOG(INFO) << "Ask to upload improted message attached file " << file_id; + CHECK(being_uploaded_imported_message_attachments_.find(file_id) == + being_uploaded_imported_message_attachments_.end()); + being_uploaded_imported_message_attachments_.emplace( + file_id, + td::make_unique(dialog_id, import_id, is_reupload, std::move(promise))); + // TODO use force_reupload if is_reupload + td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_imported_message_attachment_callback_, 1, 0); +} + +void MessagesManager::on_imported_message_attachments_uploaded(int64 random_id, Result &&result) { + if (G()->close_flag()) { + result = Status::Error(500, "Request aborted"); + } + + auto it = pending_message_imports_.find(random_id); + CHECK(it != pending_message_imports_.end()); + + auto pending_message_import = std::move(it->second); + CHECK(pending_message_import != nullptr); + + pending_message_imports_.erase(it); + + if (result.is_error()) { + pending_message_import->promise.set_error(result.move_as_error()); + return; + } + + CHECK(pending_message_import->upload_files_multipromise.promise_count() == 0); + + auto promise = std::move(pending_message_import->promise); + auto dialog_id = pending_message_import->dialog_id; + + TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); + + td_->create_handler(std::move(promise))->send(dialog_id, pending_message_import->import_id); } bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_message_id, const string &source) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 16b94d513..c5bb5d49e 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -896,9 +896,6 @@ class MessagesManager : public Actor { void upload_dialog_photo(DialogId dialog_id, FileId file_id, bool is_animation, double main_frame_timestamp, bool is_reupload, Promise &&promise, vector bad_parts = {}); - void upload_imported_messages(DialogId dialog_id, FileId file_id, vector attached_file_ids, bool is_reupload, - Promise &&promise, vector bad_parts = {}); - void on_binlog_events(vector &&events); void set_poll_answer(FullMessageId full_message_id, vector &&option_ids, Promise &&promise); @@ -2833,9 +2830,20 @@ class MessagesManager : public Actor { tl_object_ptr &&input_chat_photo, Promise &&promise); + void upload_imported_messages(DialogId dialog_id, FileId file_id, vector attached_file_ids, bool is_reupload, + Promise &&promise, vector bad_parts = {}); + void on_upload_imported_messages(FileId file_id, tl_object_ptr input_file); void on_upload_imported_messages_error(FileId file_id, Status status); + void upload_imported_message_attachment(DialogId dialog_id, int64 import_id, FileId file_id, bool is_reupload, + Promise &&promise, vector bad_parts = {}); + + void on_upload_imported_message_attachment(FileId file_id, tl_object_ptr input_file); + void on_upload_imported_message_attachment_error(FileId file_id, Status status); + + void on_imported_message_attachments_uploaded(int64 random_id, Result &&result); + void add_sponsored_dialog(const Dialog *d, DialogSource source); void save_sponsored_dialog(); @@ -2969,11 +2977,13 @@ class MessagesManager : public Actor { class UploadThumbnailCallback; class UploadDialogPhotoCallback; class UploadImportedMessagesCallback; + class UploadImportedMessageAttachmentCallback; std::shared_ptr upload_media_callback_; std::shared_ptr upload_thumbnail_callback_; std::shared_ptr upload_dialog_photo_callback_; std::shared_ptr upload_imported_messages_callback_; + std::shared_ptr upload_imported_message_attachment_callback_; double last_channel_pts_jump_warning_time_ = 0; @@ -3072,6 +3082,28 @@ class MessagesManager : public Actor { }; std::unordered_map, FileIdHash> being_uploaded_imported_messages_; + struct UploadedImportedMessageAttachmentInfo { + DialogId dialog_id; + int64 import_id; + bool is_reupload; + Promise promise; + + UploadedImportedMessageAttachmentInfo(DialogId dialog_id, int64 import_id, bool is_reupload, + Promise &&promise) + : dialog_id(dialog_id), import_id(import_id), is_reupload(is_reupload), promise(std::move(promise)) { + } + }; + std::unordered_map, FileIdHash> + being_uploaded_imported_message_attachments_; + + struct PendingMessageImport { + MultiPromiseActor upload_files_multipromise{"UploadAttachedFilesMultiPromiseActor"}; + DialogId dialog_id; + int64 import_id = 0; + Promise promise; + }; + std::unordered_map> pending_message_imports_; + struct PendingMessageGroupSend { DialogId dialog_id; size_t finished_count = 0; From bff723436ac1430d1f8da8eb1a83485e732c6f39 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 18:13:38 +0300 Subject: [PATCH 071/232] Upload inported messages as small files. --- td/telegram/MessagesManager.cpp | 6 ++++-- td/telegram/files/FileManager.cpp | 22 +++++++++++++++++----- td/telegram/files/FileManager.h | 5 ++++- td/telegram/files/FileUploader.cpp | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index f15649056..180e042db 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -26475,7 +26475,8 @@ void MessagesManager::upload_imported_messages(DialogId dialog_id, FileId file_i file_id, td::make_unique(dialog_id, std::move(attached_file_ids), is_reupload, std::move(promise))); // TODO use force_reupload if is_reupload - td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_imported_messages_callback_, 1, 0); + td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_imported_messages_callback_, 1, 0, false, + true); } void MessagesManager::start_import_messages(DialogId dialog_id, int64 import_id, vector &&attached_file_ids, @@ -26524,7 +26525,8 @@ void MessagesManager::upload_imported_message_attachment(DialogId dialog_id, int file_id, td::make_unique(dialog_id, import_id, is_reupload, std::move(promise))); // TODO use force_reupload if is_reupload - td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_imported_message_attachment_callback_, 1, 0); + td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_imported_message_attachment_callback_, 1, 0, + false, true); } void MessagesManager::on_imported_message_attachments_uploaded(int64 random_id, Result &&result) { diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index ae6f01371..0e7b73b5e 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -1605,6 +1605,7 @@ Result FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy } node->need_load_from_pmc_ |= other_node->need_load_from_pmc_; node->can_search_locally_ &= other_node->can_search_locally_; + node->upload_prefer_small_ |= other_node->upload_prefer_small_; if (drop_last_successful_force_reupload_time) { node->last_successful_force_reupload_time_ = -1e10; @@ -2319,12 +2320,13 @@ void FileManager::run_download(FileNodePtr node, bool force_update_priority) { class FileManager::ForceUploadActor : public Actor { public: ForceUploadActor(FileManager *file_manager, FileId file_id, std::shared_ptr callback, - int32 new_priority, uint64 upload_order, ActorShared<> parent) + int32 new_priority, uint64 upload_order, bool prefer_small, ActorShared<> parent) : file_manager_(file_manager) , file_id_(file_id) , callback_(std::move(callback)) , new_priority_(new_priority) , upload_order_(upload_order) + , prefer_small_(prefer_small) , parent_(std::move(parent)) { } @@ -2334,9 +2336,11 @@ class FileManager::ForceUploadActor : public Actor { std::shared_ptr callback_; int32 new_priority_; uint64 upload_order_; + bool prefer_small_; ActorShared<> parent_; bool is_active_{false}; int attempt_{0}; + class UploadCallback : public FileManager::UploadCallback { public: explicit UploadCallback(ActorId callback) : callback_(std::move(callback)) { @@ -2433,7 +2437,7 @@ class FileManager::ForceUploadActor : public Actor { is_active_ = true; attempt_++; send_closure(G()->file_manager(), &FileManager::resume_upload, file_id_, std::vector(), create_callback(), - new_priority_, upload_order_, attempt_ == 2); + new_priority_, upload_order_, attempt_ == 2, prefer_small_); } void tear_down() override { @@ -2452,7 +2456,7 @@ void FileManager::on_force_reupload_success(FileId file_id) { } void FileManager::resume_upload(FileId file_id, std::vector bad_parts, std::shared_ptr callback, - int32 new_priority, uint64 upload_order, bool force) { + int32 new_priority, uint64 upload_order, bool force, bool prefer_small) { auto node = get_sync_file_node(file_id); if (!node) { LOG(INFO) << "File " << file_id << " not found"; @@ -2472,7 +2476,7 @@ void FileManager::resume_upload(FileId file_id, std::vector bad_parts, std: } create_actor("ForceUploadActor", this, file_id, std::move(callback), new_priority, upload_order, - context_->create_reference()) + prefer_small, context_->create_reference()) .release(); return; } @@ -2481,6 +2485,9 @@ void FileManager::resume_upload(FileId file_id, std::vector bad_parts, std: if (force) { node->remote_.is_full_alive = false; } + if (prefer_small) { + node->upload_prefer_small_ = true; + } if (node->upload_pause_ == file_id) { node->set_upload_pause(FileId()); } @@ -2790,10 +2797,15 @@ void FileManager::run_upload(FileNodePtr node, std::vector bad_parts) { auto new_priority = narrow_cast(bad_parts.empty() ? -priority : priority); td::remove_if(bad_parts, [](auto part_id) { return part_id < 0; }); + auto expected_size = file_view.expected_size(true); + if (node->upload_prefer_small_ && (10 << 20) < expected_size && expected_size < (30 << 20)) { + expected_size = 10 << 20; + } + QueryId id = queries_container_.create(Query{file_id, Query::Type::Upload}); node->upload_id_ = id; send_closure(file_load_manager_, &FileLoadManager::upload, id, node->local_, node->remote_.partial_or_empty(), - file_view.expected_size(true), node->encryption_key_, new_priority, std::move(bad_parts)); + expected_size, node->encryption_key_, new_priority, std::move(bad_parts)); LOG(INFO) << "File " << file_id << " upload request has sent to FileLoadManager"; } diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index 60332ac1c..f76ca7239 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -156,6 +156,7 @@ class FileNode { double last_successful_force_reupload_time_ = -1e10; FileId upload_pause_; + int8 upload_priority_ = 0; int8 download_priority_ = 0; int8 generate_priority_ = 0; @@ -183,6 +184,8 @@ class FileNode { bool upload_was_update_file_reference_{false}; bool download_was_update_file_reference_{false}; + bool upload_prefer_small_{false}; + void init_ready_size(); void recalc_ready_prefix_size(int64 prefix_offset, int64 ready_prefix_size); @@ -444,7 +447,7 @@ class FileManager : public FileLoadManager::Callback { int64 limit); void upload(FileId file_id, std::shared_ptr callback, int32 new_priority, uint64 upload_order); void resume_upload(FileId file_id, std::vector bad_parts, std::shared_ptr callback, - int32 new_priority, uint64 upload_order, bool force = false); + int32 new_priority, uint64 upload_order, bool force = false, bool prefer_small = false); void cancel_upload(FileId file_id); bool delete_partial_remote_location(FileId file_id); void delete_file_reference(FileId file_id, std::string file_reference); diff --git a/td/telegram/files/FileUploader.cpp b/td/telegram/files/FileUploader.cpp index 7cad1b0e1..72860950e 100644 --- a/td/telegram/files/FileUploader.cpp +++ b/td/telegram/files/FileUploader.cpp @@ -184,7 +184,7 @@ Result FileUploader::on_update_local_location(const Loca } local_size_ = local_size; - if (expected_size_ < local_size_) { + if (expected_size_ < local_size_ && (expected_size_ != (10 << 20) || local_size_ >= (30 << 20))) { expected_size_ = local_size_; } local_is_ready_ = local_is_ready; From 1159323ecc7868caaf881a68f0ffb024e0d7abbe Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 19:14:35 +0300 Subject: [PATCH 072/232] Add getMessageFileType method. --- td/generate/scheme/td_api.tl | 15 ++++++++++++ td/generate/scheme/td_api.tlo | Bin 195112 -> 195484 bytes td/telegram/MessagesManager.cpp | 42 ++++++++++++++++++++++++++++++++ td/telegram/MessagesManager.h | 3 +++ td/telegram/Td.cpp | 7 ++++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 13 ++++++++++ 7 files changed, 82 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c607f9ae5..84eeeb526 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2615,6 +2615,18 @@ checkChatUsernameResultPublicChatsTooMuch = CheckChatUsernameResult; checkChatUsernameResultPublicGroupsUnavailable = CheckChatUsernameResult; +//@class MessageFileType @description Contains information about a file with messages exported from another app + +//@description The messages was exported from a private chat +messageFileTypePrivate = MessageFileType; + +//@description The messages was exported from a group chat @title Title of the group chat +messageFileTypeGroup title:string = MessageFileType; + +//@description The messages was exported from a chat of unknown type +messageFileTypeUnknown = MessageFileType; + + //@class PushMessageContent @description Contains content of a push message notification //@description A general message with hidden content @is_pinned True, if the message is a pinned message with the specified content @@ -4388,6 +4400,9 @@ readFilePart file_id:int32 offset:int32 count:int32 = FilePart; deleteFile file_id:int32 = Ok; +//@description Returns information about a file with messages exported from another app @message_file_head Beginning of the message file; up to 100 first lines +getMessageFileType message_file_head:string = MessageFileType; + //@description Imports messages exported from another app //@chat_id Identifier of a chat to which the messages will be imported. It must be an identifier of a private chat with a mutual contact or an identifier of a created supergroup chat //@message_file File with messages to import. Only inputFileLocal and inputFileGenerated are supported. The file must not be previously uploaded diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index c8d19851c02934d9b35b8ea76cdce006f2276195..58381c582bb49e03dec3346ba7eb5f3a7ea7bd7c 100644 GIT binary patch delta 312 zcmZ4Sg?r9-Zr(?;^{p77Kzk!^w2a99w+znwzNy831*6FD(En0;!$;$AHnm1zp#sU9xWIwua_q=jE5@;k0%83v0%lcZ_qk zhiWm+kCeH>fBcb9dTI%pC&Ah$H?)azqyfDhpOKn4{h<(}_~Zgp4i=Crw!dg*awr1; DBZY4s delta 59 zcmbR9oqNR>Zr(?;^{p77Kx-p!w9MuyGCRZ>Gn#!(w)>heUS{8B%b4(varX8^ZKnB= K+cR33Ov(U2NfWgI diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 180e042db..9163426a0 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1009,6 +1009,43 @@ class CreateChannelQuery : public Td::ResultHandler { } }; +class CheckHistoryImportQuery : public Td::ResultHandler { + Promise> promise_; + + public: + explicit CheckHistoryImportQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const string &message_file_head) { + send_query(G()->net_query_creator().create(telegram_api::messages_checkHistoryImport(message_file_head))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for CheckHistoryImportQuery: " << to_string(ptr); + auto file_type = [&]() -> td_api::object_ptr { + if (ptr->pm_) { + return td_api::make_object(); + } else if (ptr->group_) { + return td_api::make_object(ptr->title_); + } else { + return td_api::make_object(); + } + }(); + promise_.set_value(std::move(file_type)); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class InitHistoryImportQuery : public Td::ResultHandler { Promise promise_; FileId file_id_; @@ -26405,6 +26442,11 @@ Result MessagesManager::add_local_message( return message_id; } +void MessagesManager::get_message_file_type(const string &message_file_head, + Promise> &&promise) { + td_->create_handler(std::move(promise))->send(message_file_head); +} + void MessagesManager::import_messages(DialogId dialog_id, const td_api::object_ptr &message_file, const vector> &attached_files, Promise &&promise) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index c5bb5d49e..02549acd2 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -394,6 +394,9 @@ class MessagesManager : public Actor { tl_object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; + void get_message_file_type(const string &message_file_head, + Promise> &&promise); + void import_messages(DialogId dialog_id, const td_api::object_ptr &message_file, const vector> &attached_files, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index eee7d2cbc..31e3b8d37 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6535,6 +6535,13 @@ void Td::on_request(uint64 id, const td_api::deleteFile &request) { "td_api::deleteFile"); } +void Td::on_request(uint64 id, td_api::getMessageFileType &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.message_file_head_); + CREATE_REQUEST_PROMISE(); + messages_manager_->get_message_file_type(request.message_file_head_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::importMessages &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 4607683de..db5e62a91 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -836,6 +836,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::deleteFile &request); + void on_request(uint64 id, td_api::getMessageFileType &request); + void on_request(uint64 id, const td_api::importMessages &request); void on_request(uint64 id, const td_api::blockMessageSenderFromReplies &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index cf3798460..706e8d128 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -24,6 +24,7 @@ #include "td/utils/crypto.h" #include "td/utils/ExitGuard.h" #include "td/utils/FileLog.h" +#include "td/utils/filesystem.h" #include "td/utils/format.h" #include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" @@ -41,6 +42,7 @@ #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" #include "td/utils/Time.h" +#include "td/utils/utf8.h" #ifndef USE_READLINE #include "td/utils/find_boundary.h" @@ -2971,6 +2973,17 @@ class CliClient final : public Actor { as_input_file(document), nullptr, true, as_caption("")); return content; }))); + } else if (op == "gmft") { + auto r_message_file_head = read_file_str(args, 2 << 10); + if (r_message_file_head.is_error()) { + LOG(ERROR) << r_message_file_head.error(); + } else { + auto message_file_head = r_message_file_head.move_as_ok(); + while (!check_utf8(message_file_head)) { + message_file_head.pop_back(); + } + send_request(td_api::make_object(message_file_head)); + } } else if (op == "im") { string chat_id; string message_file; From e584eccc65bbb53d94fed8f658ff0dfae8b47bb9 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 19:23:44 +0300 Subject: [PATCH 073/232] Add createNewSupergroupChat.for_import. --- td/generate/scheme/td_api.tl | 9 +++++++-- td/generate/scheme/td_api.tlo | Bin 195484 -> 195520 bytes td/telegram/MessagesManager.cpp | 10 +++++++--- td/telegram/MessagesManager.h | 3 ++- td/telegram/Td.cpp | 9 ++++++--- td/telegram/cli.cpp | 10 ++++++---- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 84eeeb526..a8133c92a 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4192,8 +4192,13 @@ createSecretChat secret_chat_id:int32 = Chat; //@description Creates a new basic group and sends a corresponding messageBasicGroupChatCreate. Returns the newly created chat @user_ids Identifiers of users to be added to the basic group @title Title of the new basic group; 1-128 characters createNewBasicGroupChat user_ids:vector title:string = Chat; -//@description Creates a new supergroup or channel and sends a corresponding messageSupergroupChatCreate. Returns the newly created chat @title Title of the new chat; 1-128 characters @is_channel True, if a channel chat should be created @param_description Chat description; 0-255 characters @location Chat location if a location-based supergroup is being created -createNewSupergroupChat title:string is_channel:Bool description:string location:chatLocation = Chat; +//@description Creates a new supergroup or channel and sends a corresponding messageSupergroupChatCreate. Returns the newly created chat +//@title Title of the new chat; 1-128 characters +//@is_channel True, if a channel chat needs to be created +//@param_description Chat description; 0-255 characters +//@location Chat location if a location-based supergroup is being created +//@for_import True, if the supergroup is created for importing messages using importMessage +createNewSupergroupChat title:string is_channel:Bool description:string location:chatLocation for_import:Bool = Chat; //@description Creates a new secret chat. Returns the newly created chat @user_id Identifier of the target user createNewSecretChat user_id:int32 = Chat; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 58381c582bb49e03dec3346ba7eb5f3a7ea7bd7c..a78718c4ba47e2d3c617bfe1312343eb63be0fc0 100644 GIT binary patch delta 82 zcmbR9o%_Id?uHh|Eld#}EC-i0pPL@z!KA>*I(cD*`t*h=j4YF7=EzKM;022O^-!Mv m1}MoZ{p`LbS6Y5id}eMz{&YhVCh_STT$m)bt9UY1Yyto-B_3P= delta 63 zcmX@`oqNuA?uHh|Eld#}ENj=5{huD=!KA>*GI?Qz`t*h=K!(g5nduF@jI5LYdMHnq Txyqz5J;0qwV7rGWQ^h6#BE}bI diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 9163426a0..bfd0c76a1 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -975,7 +975,7 @@ class CreateChannelQuery : public Td::ResultHandler { } void send(const string &title, bool is_megagroup, const string &about, const DialogLocation &location, - int64 random_id) { + bool for_import, int64 random_id) { int32 flags = 0; if (is_megagroup) { flags |= telegram_api::channels_createChannel::MEGAGROUP_MASK; @@ -985,6 +985,9 @@ class CreateChannelQuery : public Td::ResultHandler { if (!location.empty()) { flags |= telegram_api::channels_createChannel::GEO_POINT_MASK; } + if (for_import) { + flags |= telegram_api::channels_createChannel::FOR_IMPORT_MASK; + } random_id_ = random_id; send_query(G()->net_query_creator().create( @@ -18979,7 +18982,7 @@ DialogId MessagesManager::create_new_group_chat(const vector &user_ids, } DialogId MessagesManager::create_new_channel_chat(const string &title, bool is_megagroup, const string &description, - const DialogLocation &location, int64 &random_id, + const DialogLocation &location, bool for_import, int64 &random_id, Promise &&promise) { LOG(INFO) << "Trying to create " << (is_megagroup ? "supergroup" : "broadcast") << " with title \"" << title << "\", description \"" << description << "\" and " << location; @@ -19014,7 +19017,8 @@ DialogId MessagesManager::create_new_channel_chat(const string &title, bool is_m created_dialogs_[random_id]; // reserve place for result td_->create_handler(std::move(promise)) - ->send(new_title, is_megagroup, strip_empty_characters(description, MAX_DESCRIPTION_LENGTH), location, random_id); + ->send(new_title, is_megagroup, strip_empty_characters(description, MAX_DESCRIPTION_LENGTH), location, for_import, + random_id); return DialogId(); } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 02549acd2..0758bf38b 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -637,7 +637,8 @@ class MessagesManager : public Actor { Promise &&promise); DialogId create_new_channel_chat(const string &title, bool is_megagroup, const string &description, - const DialogLocation &location, int64 &random_id, Promise &&promise); + const DialogLocation &location, bool for_import, int64 &random_id, + Promise &&promise); void create_new_secret_chat(UserId user_id, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 31e3b8d37..4797bfba1 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1888,13 +1888,14 @@ class CreateNewSupergroupChatRequest : public RequestActor<> { bool is_megagroup_; string description_; DialogLocation location_; + bool for_import_; int64 random_id_; DialogId dialog_id_; void do_run(Promise &&promise) override { dialog_id_ = td->messages_manager_->create_new_channel_chat(title_, is_megagroup_, description_, location_, - random_id_, std::move(promise)); + for_import_, random_id_, std::move(promise)); } void do_send_result() override { @@ -1904,12 +1905,14 @@ class CreateNewSupergroupChatRequest : public RequestActor<> { public: CreateNewSupergroupChatRequest(ActorShared td, uint64 request_id, string title, bool is_megagroup, - string description, td_api::object_ptr &&location) + string description, td_api::object_ptr &&location, + bool for_import) : RequestActor(std::move(td), request_id) , title_(std::move(title)) , is_megagroup_(is_megagroup) , description_(std::move(description)) , location_(std::move(location)) + , for_import_(for_import) , random_id_(0) { } }; @@ -5873,7 +5876,7 @@ void Td::on_request(uint64 id, td_api::createNewSupergroupChat &request) { CLEAN_INPUT_STRING(request.title_); CLEAN_INPUT_STRING(request.description_); CREATE_REQUEST(CreateNewSupergroupChatRequest, std::move(request.title_), !request.is_channel_, - std::move(request.description_), std::move(request.location_)); + std::move(request.description_), std::move(request.location_), request.for_import_); } void Td::on_request(uint64 id, td_api::createNewSecretChat &request) { CREATE_REQUEST(CreateNewSecretChatRequest, request.user_id_); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 706e8d128..3f1f6a634 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3438,13 +3438,15 @@ class CliClient final : public Actor { get_args(args, user_ids_string, title); send_request(td_api::make_object(as_user_ids(user_ids_string), title)); } else if (op == "cnchc") { - send_request(td_api::make_object(args, true, "Description", nullptr)); + send_request(td_api::make_object(args, true, "Description", nullptr, false)); } else if (op == "cnsgc") { - send_request(td_api::make_object(args, false, "Description", nullptr)); + send_request(td_api::make_object(args, false, "Description", nullptr, false)); } else if (op == "cnsgcloc") { send_request(td_api::make_object( - args, false, "Description", - td_api::make_object(as_location("40.0", "60.0"), "address"))); + args, false, "Description", td_api::make_object(as_location("40.0", "60.0"), "address"), + false)); + } else if (op == "cnsgcimport") { + send_request(td_api::make_object(args, false, "Description", nullptr, true)); } else if (op == "UpgradeBasicGroupChatToSupergroupChat") { send_request(td_api::make_object(as_chat_id(args))); } else if (op == "DeleteChat") { From 6ddce1dd7a052217b50f90930ad977e58fe5c605 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 22:42:32 +0300 Subject: [PATCH 074/232] Add name to messageFileTypePrivate. --- td/generate/scheme/td_api.tl | 8 ++++---- td/generate/scheme/td_api.tlo | Bin 195520 -> 195552 bytes td/telegram/MessagesManager.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index a8133c92a..b3d3c9d1f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2617,10 +2617,10 @@ checkChatUsernameResultPublicGroupsUnavailable = CheckChatUsernameResult; //@class MessageFileType @description Contains information about a file with messages exported from another app -//@description The messages was exported from a private chat -messageFileTypePrivate = MessageFileType; +//@description The messages was exported from a private chat @name Name of the other party; may be empty if unrecognized +messageFileTypePrivate name:string = MessageFileType; -//@description The messages was exported from a group chat @title Title of the group chat +//@description The messages was exported from a group chat @title Title of the group chat; may be empty if unrecognized messageFileTypeGroup title:string = MessageFileType; //@description The messages was exported from a chat of unknown type @@ -4878,7 +4878,7 @@ deleteAccount reason:string = Ok; //@description Removes a chat action bar without any other action @chat_id Chat identifier removeChatActionBar chat_id:int53 = Ok; -//@description Reports a chat to the Telegram moderators. A chat can be reported only from the chat action bar, or if this is a private chats with a bot, a private chat with a user sharing their location, a supergroup, or a channel, since other chats can't be checked by moderators @chat_id Chat identifier @reason The reason for reporting the chat @message_ids Identifiers of reported messages, if any +//@description Reports a chat to the Telegram moderators. A chat can be reported only from the chat action bar, or if this is a private chat with a bot, a private chat with a user sharing their location, a supergroup, or a channel, since other chats can't be checked by moderators @chat_id Chat identifier @reason The reason for reporting the chat @message_ids Identifiers of reported messages, if any reportChat chat_id:int53 reason:ChatReportReason message_ids:vector = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index a78718c4ba47e2d3c617bfe1312343eb63be0fc0..5a56caf93e3a6a79904f27a9aed6b573cb5d18cb 100644 GIT binary patch delta 147 zcmX@`o%_Le?hP3-EPT~6AEH|~dI59odEi)%Iq_QA2peVB} zu_ToNq;mR(NJay&p6MYrj2aMDgAGh<3s6h~r0(W1As+@HC~{`g;jnEx2oh`mV#B!o Xiw%>92Sl4gE0YbFwY{O0$)OAY;9xK6 delta 157 zcmaFxo%_Id?hP3-Ec@RwI5+3WY|oKl+#+w1dgI4;vE0<+;>7e+x6GW>kjjG8fTGN@ z#FA77kjk6KgnSr)pvaj`hr_n*AV_R_i4CI$*qrGLY#0q7tSdk<30ykbMQj td_api::object_ptr { if (ptr->pm_) { - return td_api::make_object(); + return td_api::make_object(ptr->title_); } else if (ptr->group_) { return td_api::make_object(ptr->title_); } else { From 60b76e89aac13a1ae7d76011e4d50eb4dfa63192 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Jan 2021 23:45:39 +0300 Subject: [PATCH 075/232] Allow import messages with can_change_info administrator right. --- td/generate/scheme/td_api.tl | 16 ++++++++-------- td/telegram/MessagesManager.cpp | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b3d3c9d1f..9cab5a650 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4236,11 +4236,11 @@ getRecommendedChatFilters = RecommendedChatFilters; getChatFilterDefaultIconName filter:chatFilter = Text; -//@description Changes the chat title. Supported only for basic groups, supergroups and channels. Requires can_change_info rights +//@description Changes the chat title. Supported only for basic groups, supergroups and channels. Requires can_change_info administrator right //@chat_id Chat identifier @title New title of the chat; 1-128 characters setChatTitle chat_id:int53 title:string = Ok; -//@description Changes the photo of a chat. Supported only for basic groups, supergroups and channels. Requires can_change_info rights +//@description Changes the photo of a chat. Supported only for basic groups, supergroups and channels. Requires can_change_info administrator right //@chat_id Chat identifier @photo New chat photo. Pass null to delete the chat photo setChatPhoto chat_id:int53 photo:InputChatPhoto = Ok; @@ -4264,10 +4264,10 @@ toggleChatDefaultDisableNotification chat_id:int53 default_disable_notification: //@description Changes application-specific data associated with a chat @chat_id Chat identifier @client_data New value of client_data setChatClientData chat_id:int53 client_data:string = Ok; -//@description Changes information about a chat. Available for basic groups, supergroups, and channels. Requires can_change_info rights @chat_id Identifier of the chat @param_description New chat description; 0-255 characters +//@description Changes information about a chat. Available for basic groups, supergroups, and channels. Requires can_change_info administrator right @chat_id Identifier of the chat @param_description New chat description; 0-255 characters setChatDescription chat_id:int53 description:string = Ok; -//@description Changes the discussion group of a channel chat; requires can_change_info rights in the channel if it is specified @chat_id Identifier of the channel chat. Pass 0 to remove a link from the supergroup passed in the second argument to a linked channel chat (requires can_pin_messages rights in the supergroup) @discussion_chat_id Identifier of a new channel's discussion group. Use 0 to remove the discussion group. +//@description Changes the discussion group of a channel chat; requires can_change_info administrator right in the channel if it is specified @chat_id Identifier of the channel chat. Pass 0 to remove a link from the supergroup passed in the second argument to a linked channel chat (requires can_pin_messages rights in the supergroup) @discussion_chat_id Identifier of a new channel's discussion group. Use 0 to remove the discussion group. //-Use the method getSuitableDiscussionChats to find all suitable groups. Basic group chats must be first upgraded to supergroup chats. If new chat members don't have access to old messages in the supergroup, then toggleSupergroupIsAllHistoryAvailable must be used first to change that setChatDiscussionGroup chat_id:int53 discussion_chat_id:int53 = Ok; @@ -4409,7 +4409,7 @@ deleteFile file_id:int32 = Ok; getMessageFileType message_file_head:string = MessageFileType; //@description Imports messages exported from another app -//@chat_id Identifier of a chat to which the messages will be imported. It must be an identifier of a private chat with a mutual contact or an identifier of a created supergroup chat +//@chat_id Identifier of a chat to which the messages will be imported. It must be an identifier of a private chat with a mutual contact or an identifier of a supergroup chat with can_change_info administrator right //@message_file File with messages to import. Only inputFileLocal and inputFileGenerated are supported. The file must not be previously uploaded //@attached_files Files used in the imported messages. Only inputFileLocal and inputFileGenerated are supported. The files must not be previously uploaded importMessages chat_id:int53 message_file:InputFile attached_files:vector = Ok; @@ -4730,13 +4730,13 @@ disconnectAllWebsites = Ok; //@description Changes the username of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username setSupergroupUsername supergroup_id:int32 username:string = Ok; -//@description Changes the sticker set of a supergroup; requires can_change_info rights @supergroup_id Identifier of the supergroup @sticker_set_id New value of the supergroup sticker set identifier. Use 0 to remove the supergroup sticker set +//@description Changes the sticker set of a supergroup; requires can_change_info administrator right @supergroup_id Identifier of the supergroup @sticker_set_id New value of the supergroup sticker set identifier. Use 0 to remove the supergroup sticker set setSupergroupStickerSet supergroup_id:int32 sticker_set_id:int64 = Ok; -//@description Toggles sender signatures messages sent in a channel; requires can_change_info rights @supergroup_id Identifier of the channel @sign_messages New value of sign_messages +//@description Toggles sender signatures messages sent in a channel; requires can_change_info administrator right @supergroup_id Identifier of the channel @sign_messages New value of sign_messages toggleSupergroupSignMessages supergroup_id:int32 sign_messages:Bool = Ok; -//@description Toggles whether the message history of a supergroup is available to new members; requires can_change_info rights @supergroup_id The identifier of the supergroup @is_all_history_available The new value of is_all_history_available +//@description Toggles whether the message history of a supergroup is available to new members; requires can_change_info administrator right @supergroup_id The identifier of the supergroup @is_all_history_available The new value of is_all_history_available toggleSupergroupIsAllHistoryAvailable supergroup_id:int32 is_all_history_available:Bool = Ok; //@description Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup @supergroup_id Supergroup identifier @user_id User identifier @message_ids Identifiers of messages sent in the supergroup by the user. This list must be non-empty diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index f73a24865..4d752f402 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -26472,7 +26472,7 @@ void MessagesManager::import_messages(DialogId dialog_id, const td_api::object_p if (is_broadcast_channel(dialog_id)) { return promise.set_error(Status::Error(400, "Can't import messages to channels")); } - if (!td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).is_creator()) { + if (!td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).can_change_info_and_settings()) { return promise.set_error(Status::Error(400, "Not enough rights to import messages")); } break; From f77adeb28c71d28315f65a99be43863ebd4d4486 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 02:00:12 +0300 Subject: [PATCH 076/232] Update layer 123. --- td/generate/scheme/td_api.tl | 3 +-- td/generate/scheme/td_api.tlo | Bin 195552 -> 195500 bytes td/generate/scheme/telegram_api.tl | 12 +++++++----- td/generate/scheme/telegram_api.tlo | Bin 229016 -> 229124 bytes td/telegram/ContactsManager.cpp | 9 +++++++-- td/telegram/GroupCallParticipant.cpp | 11 ++--------- td/telegram/GroupCallParticipant.h | 1 - td/telegram/MessagesManager.cpp | 24 +++++++++++++----------- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 9cab5a650..6414081b3 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2131,10 +2131,9 @@ groupCallJoinResponse payload:groupCallPayload candidates:vector|<(C#XC+6e?Bo>uqCTA8T=9MroFo5K4 y9uxB6p4{lIzB%Dg2IJ&Ehkd}Bn|+RK_c_A&!;%%Gh;h2&eMb518UGk7{sI7v{VOj3 delta 117 zcmZ4Uo%_Le?hQG@EKRa4?wbpQm#kw0u|(3Rf4sva%)`LI0OH;}Cgj62-SIo4=Ja>W zjC`954n;5uOXQZ8q{ipxlr{<(h=C4wkeDbh2*tBMeBikj8F#fOv^QI>} NU{u&{@{h6NF93PGE*t;= diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 403a55779..6f7156438 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -204,7 +204,7 @@ inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings; -peerSettings#733f2961 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true geo_distance:flags.6?int = PeerSettings; +peerSettings#733f2961 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true geo_distance:flags.6?int = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; wallPaperNoFile#8af40b25 flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; @@ -1183,7 +1183,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2 inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#b881f32b flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int muted_cnt:flags.8?int = GroupCallParticipant; +groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; phone.groupCall#66ab0bfc call:GroupCall participants:Vector participants_next_offset:string users:Vector = phone.GroupCall; @@ -1205,7 +1205,9 @@ messages.chatInviteImporters#81b6b00a count:int importers:Vector = messages.AffectedFoundMessages; ---functions--- @@ -1366,7 +1368,7 @@ messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#14b9bcd7 flags:# legacy_revoke_permanent:flags.2?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; +messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1462,7 +1464,7 @@ messages.deleteRevokedExportedChatInvites#52041463 peer:InputPeer = Bool; messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.deleteChat#83247d11 chat_id:int = Bool; -messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; +messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages; messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index 500f36e339b965e6d1eca44bff07c102d91d68b6..672599f199d552a15138ce8f70a210d98a0419a8 100644 GIT binary patch delta 441 zcmbR7m$&5~FYlw-`c@23;IxtVHW!Pi*0Ge$Z@BI#E9cntzL(2QEiO(>Pc7DSOiN2m zE=f&s%P-AK@r8;`oG-cggx&{cmYv)A<2LUxy1~X+xp}JPeR;4klNJ0GCeMfrftdLr zaz#8F$aInP$?v$tz=G2iBp3~*e@JE4oXnfezgb~d1LO1pTV_sXkRxvFzOi+($t0ca zcM2JO82R}!^U5+yQsZ+|bCXhwiYLpLDQ?#&X0+&K1sTdP*)UIi`i66i5?hxroe0oH zb$lW=$1{M`+&m`a!!rFKCzJd1Gug}v(-&kiX-pSTXB1%p$!<>wWO5M(`)>M*6ea~Q zYw8D}oByOR#Y}flViK9|;KIbo4D|l?ic}^Maj>_RPpDyH1hckJsbyMH1u?+p2a^ey zwY}sA(-RgJ&L^jTPLKHuG!!QvDAop<^LHfEK{B5!pjzu6$L K{RunsmNEdA0H}Qc delta 381 zcmZqq$2;RMFYlw-`c@23;JA_ZHW!Qb=f)kI-*DYg-pr!^ftkf=^_1StKa6g$F;;B8 zXn9|r6(r9vd7`iUTG{g$mqkkJ)@Y>q8DPx^o8P# z>eD}*W0ctXgy}@U_MRXn3t_PHrk_Yps$aICDOq|R>=Wm~p z#v~#RHh=ksS|&y?Yx|ZurX^Jn{V_k8Ou($|Q+_f%VPSEV{+~0w<{!||i=c>)ft^`p@{(2^3GtlN^u*-K_@dOZ{Or{Dg4Cki#JtqJ jlIb6qnH?rC*&s4Gqe^1(g$*LpC$KQfY 0) { flags |= telegram_api::messages_exportChatInvite::EXPIRE_DATE_MASK; @@ -1593,8 +1595,11 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { if (is_permanent) { flags |= telegram_api::messages_exportChatInvite::LEGACY_REVOKE_PERMANENT_MASK; } - send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( - flags, false /*ignored*/, std::move(input_peer), expire_date, usage_limit))); + */ + + send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite(std::move(input_peer)))); + // send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( + // flags, false /*ignored*/, std::move(input_peer), expire_date, usage_limit))); } void on_result(uint64 id, BufferSlice packet) override { diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index 76848c3ab..9eed3f65b 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -19,13 +19,6 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptrmuted_; can_self_unmute = participant->can_self_unmute_; is_muted_only_for_self = participant->muted_by_you_; - if ((participant->flags_ & telegram_api::groupCallParticipant::MUTED_CNT_MASK) != 0) { - muted_count = participant->muted_cnt_; - if (muted_count < 0) { - LOG(ERROR) << "Receive " << to_string(participant); - muted_count = 0; - } - } if ((participant->flags_ & telegram_api::groupCallParticipant::VOLUME_MASK) != 0) { volume_level = participant->volume_; if (volume_level < MIN_VOLUME_LEVEL || volume_level > MAX_VOLUME_LEVEL) { @@ -95,11 +88,11 @@ td_api::object_ptr GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), audio_source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, - can_be_unmuted_only_for_self, is_muted, can_self_unmute, muted_count, volume_level, order); + can_be_unmuted_only_for_self, is_muted, can_self_unmute, volume_level, order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { - return lhs.user_id == rhs.user_id && lhs.audio_source == rhs.audio_source && lhs.muted_count == rhs.muted_count && + return lhs.user_id == rhs.user_id && lhs.audio_source == rhs.audio_source && lhs.can_be_muted_for_all_users == rhs.can_be_muted_for_all_users && lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index e3be59b19..abaff79e5 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -22,7 +22,6 @@ struct GroupCallParticipant { int32 audio_source = 0; int32 joined_date = 0; int32 active_date = 0; - int32 muted_count = 0; int32 volume_level = 10000; bool is_muted = false; bool can_self_unmute = false; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 4d752f402..5d285e3e7 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -2512,20 +2512,22 @@ class DeletePhoneCallHistoryQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - auto affected_history = result_ptr.move_as_ok(); - CHECK(affected_history->get_id() == telegram_api::messages_affectedHistory::ID); + auto affected_messages = result_ptr.move_as_ok(); + CHECK(affected_messages->get_id() == telegram_api::messages_affectedFoundMessages::ID); - if (affected_history->pts_count_ > 0) { - affected_history->pts_count_ = 0; // force receiving real updates from the server - auto promise = affected_history->offset_ > 0 ? Promise() : std::move(promise_); - td->updates_manager_->add_pending_pts_update(make_tl_object(), affected_history->pts_, - affected_history->pts_count_, std::move(promise), + if (affected_messages->pts_count_ > 0) { + auto promise = affected_messages->offset_ > 0 ? Promise() : std::move(promise_); + auto pts = affected_messages->pts_; + auto pts_count = affected_messages->pts_count_; + auto update = + make_tl_object(std::move(affected_messages->messages_), pts, pts_count); + td->updates_manager_->add_pending_pts_update(std::move(update), pts, pts_count, std::move(promise), "delete phone call history query"); - } else if (affected_history->offset_ <= 0) { + } else if (affected_messages->offset_ <= 0) { promise_.set_value(Unit()); } - if (affected_history->offset_ > 0) { + if (affected_messages->offset_ > 0) { send_request(); return; } @@ -4278,7 +4280,7 @@ class UpdatePeerSettingsQuery : public Td::ResultHandler { dialog_id_, make_tl_object(0, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, 0), + false /*ignored*/, false /*ignored*/, 0), true); promise_.set_value(Unit()); @@ -4320,7 +4322,7 @@ class ReportEncryptedSpamQuery : public Td::ResultHandler { dialog_id_, make_tl_object(0, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, 0), + false /*ignored*/, false /*ignored*/, 0), true); promise_.set_value(Unit()); From 0b868c0aadbb68dae2a270fa5b1fa36a145272fb Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 02:22:35 +0300 Subject: [PATCH 077/232] Add chatActionBarInviteMembers. --- td/generate/scheme/td_api.tl | 3 +++ td/generate/scheme/td_api.tlo | Bin 195500 -> 195568 bytes td/telegram/MessagesManager.cpp | 43 ++++++++++++++++++++++++++++---- td/telegram/MessagesManager.h | 1 + 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 6414081b3..145e2e97b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -938,6 +938,9 @@ chatActionBarReportSpam can_unarchive:Bool = ChatActionBar; //@description The chat is a location-based supergroup, which can be reported as having unrelated location using the method reportChat with the reason chatReportReasonUnrelatedLocation chatActionBarReportUnrelatedLocation = ChatActionBar; +//@description The chat is a recently created group chat, to which new members can be invited +chatActionBarInviteMembers = ChatActionBar; + //@description The chat is a private or secret chat, which can be reported using the method reportChat, or the other user can be blocked using the method blockUser, or the other user can be added to the contact list using the method addContact //@can_unarchive If true, the chat was automatically archived and can be moved back to the main chat list using addChatToList simultaneously with setting chat notification settings to default using setChatNotificationSettings //@distance If non-negative, the current user was found by the peer through searchChatsNearby and this is the distance between the users diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 8477a16f250da9cbe67f767fcd87a0a13915b019..37f89fc4089d1c9049c99f3623d29e609ffcbcea 100644 GIT binary patch delta 277 zcmZ4Uo%_Rg?hQU{qJ9@kzVbR}B$hZPmt^MWIVBb`FtBY-WP7d4n6uf}gjNrwjjD%@?L delta 201 zcmezHoqNr9?hQU{qIPQ)n|Ylx5=$JDOEUBGoDz!|7+5zavc1-2%--y4!mY^yQn5KE zb_FY#HJKyX0>W@fwvYhH-8?4b!vF+D&TKjywrvMNVv}zq>p+#K#6TDsDH^zyuShY# hDcSrXW&4K|#tU6wbEX^IXH;l^@}6<~llM$KJOE`8Mcan_report_spam && !d->can_add_contact && !d->can_block_user && !d->can_share_phone_number && - !d->can_report_location && !d->can_unarchive && d->distance < 0) { + !d->can_report_location && !d->can_unarchive && d->distance < 0 && !d->can_invite_members) { return; } @@ -7950,6 +7953,7 @@ void MessagesManager::hide_dialog_action_bar(Dialog *d) { d->can_report_location = false; d->can_unarchive = false; d->distance = -1; + d->can_invite_members = false; send_update_chat_action_bar(d); } @@ -7979,7 +7983,7 @@ void MessagesManager::remove_dialog_action_bar(DialogId dialog_id, Promise } if (!d->can_report_spam && !d->can_add_contact && !d->can_block_user && !d->can_share_phone_number && - !d->can_report_location && !d->can_unarchive && d->distance < 0) { + !d->can_report_location && !d->can_unarchive && d->distance < 0 && !d->can_invite_members) { return promise.set_value(Unit()); } @@ -8213,9 +8217,11 @@ void MessagesManager::on_get_peer_settings(DialogId dialog_id, auto can_unarchive = (peer_settings->flags_ & telegram_api::peerSettings::AUTOARCHIVED_MASK) != 0; auto distance = (peer_settings->flags_ & telegram_api::peerSettings::GEO_DISTANCE_MASK) != 0 ? peer_settings->geo_distance_ : -1; + auto can_invite_members = (peer_settings->flags_ & telegram_api::peerSettings::INVITE_MEMBERS_MASK) != 0; if (d->can_report_spam == can_report_spam && d->can_add_contact == can_add_contact && d->can_block_user == can_block_user && d->can_share_phone_number == can_share_phone_number && - d->can_report_location == can_report_location && d->can_unarchive == can_unarchive && d->distance == distance) { + d->can_report_location == can_report_location && d->can_unarchive == can_unarchive && d->distance == distance && + d->can_invite_members == can_invite_members) { if (!d->know_action_bar || !d->know_can_report_spam) { d->know_can_report_spam = true; d->know_action_bar = true; @@ -8233,6 +8239,7 @@ void MessagesManager::on_get_peer_settings(DialogId dialog_id, d->can_report_location = can_report_location; d->can_unarchive = can_unarchive; d->distance = distance < 0 ? -1 : distance; + d->can_invite_members = can_invite_members; fix_dialog_action_bar(d); @@ -8255,10 +8262,28 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) { if (dialog_type != DialogType::Channel) { LOG(ERROR) << "Receive can_report_location in " << d->dialog_id; d->can_report_location = false; + } else if (d->can_report_spam || d->can_add_contact || d->can_block_user || d->can_share_phone_number || + d->can_unarchive || d->can_invite_members) { + LOG(ERROR) << "Receive action bar " << d->can_report_spam << "/" << d->can_add_contact << "/" << d->can_block_user + << "/" << d->can_share_phone_number << "/" << d->can_report_location << "/" << d->can_unarchive << "/" + << d->can_invite_members; + d->can_report_spam = false; + d->can_add_contact = false; + d->can_block_user = false; + d->can_share_phone_number = false; + d->can_unarchive = false; + d->can_invite_members = false; + CHECK(d->distance == -1); + } + } + if (d->can_invite_members) { + if (dialog_type != DialogType::Chat && (dialog_type != DialogType::Channel || is_broadcast_channel(d->dialog_id))) { + LOG(ERROR) << "Receive can_invite_members in " << d->dialog_id; + d->can_invite_members = false; } else if (d->can_report_spam || d->can_add_contact || d->can_block_user || d->can_share_phone_number || d->can_unarchive) { LOG(ERROR) << "Receive action bar " << d->can_report_spam << "/" << d->can_add_contact << "/" << d->can_block_user - << "/" << d->can_share_phone_number << "/" << d->can_report_location << "/" << d->can_unarchive; + << "/" << d->can_share_phone_number << "/" << d->can_unarchive << "/" << d->can_invite_members; d->can_report_spam = false; d->can_add_contact = false; d->can_block_user = false; @@ -8290,6 +8315,7 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) { } if (d->can_share_phone_number) { CHECK(!d->can_report_location); + CHECK(!d->can_invite_members); if (dialog_type != DialogType::User) { LOG(ERROR) << "Receive can_share_phone_number in " << d->dialog_id; d->can_share_phone_number = false; @@ -8304,6 +8330,7 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) { } if (d->can_block_user) { CHECK(!d->can_report_location); + CHECK(!d->can_invite_members); CHECK(!d->can_share_phone_number); if (dialog_type != DialogType::User) { LOG(ERROR) << "Receive can_block_user in " << d->dialog_id; @@ -8317,6 +8344,7 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) { } if (d->can_add_contact) { CHECK(!d->can_report_location); + CHECK(!d->can_invite_members); CHECK(!d->can_share_phone_number); if (dialog_type != DialogType::User) { LOG(ERROR) << "Receive can_add_contact in " << d->dialog_id; @@ -19546,9 +19574,14 @@ td_api::object_ptr MessagesManager::get_chat_action_bar_o if (d->can_report_location) { CHECK(d->dialog_id.get_type() == DialogType::Channel); - CHECK(!d->can_share_phone_number && !d->can_block_user && !d->can_add_contact && !d->can_report_spam); + CHECK(!d->can_share_phone_number && !d->can_block_user && !d->can_add_contact && !d->can_report_spam && + !d->can_invite_members); return td_api::make_object(); } + if (d->can_invite_members) { + CHECK(!d->can_share_phone_number && !d->can_block_user && !d->can_add_contact && !d->can_report_spam); + return td_api::make_object(); + } if (d->can_share_phone_number) { CHECK(d->dialog_id.get_type() == DialogType::User); CHECK(!d->can_block_user && !d->can_add_contact && !d->can_report_spam); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 0758bf38b..f61fcb15c 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -1216,6 +1216,7 @@ class MessagesManager : public Actor { bool can_report_location = false; bool can_unarchive = false; bool hide_distance = false; + bool can_invite_members = false; bool is_opened = false; From 15fe7f7a04c57a8747d4331ed2894d381c7e1bc2 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 02:39:48 +0300 Subject: [PATCH 078/232] Rename suggested_name to suggested_path. --- td/telegram/MessageContent.cpp | 14 ++++++++------ td/telegram/MessagesManager.cpp | 2 +- td/telegram/SecureManager.cpp | 2 +- td/telegram/SecureValue.cpp | 2 +- td/telegram/StickersManager.cpp | 4 ++-- td/telegram/files/FileManager.cpp | 16 ++++++++-------- td/telegram/files/FileManager.h | 4 ++-- 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index a124dafba..f01027987 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -1583,8 +1583,8 @@ static Result create_input_message_content( string mime_type; if (file_id.is_valid()) { file_view = td->file_manager_->get_file_view(file_id); - auto suggested_name = file_view.suggested_name(); - const PathView path_view(suggested_name); + auto suggested_path = file_view.suggested_path(); + const PathView path_view(suggested_path); file_name = path_view.file_name().str(); mime_type = MimeType::from_extension(path_view.extension()); } @@ -2508,11 +2508,13 @@ tl_object_ptr get_fake_input_media(Td *td, tl_object_p case FileType::Video: case FileType::VoiceNote: { vector> attributes; - auto file_name = file_view.suggested_name(); + auto file_path = file_view.suggested_path(); + const PathView path_view(file_path); + Slice file_name = path_view.file_name(); if (!file_name.empty()) { - attributes.push_back(make_tl_object(file_name)); + attributes.push_back(make_tl_object(file_name.str())); } - string mime_type = MimeType::from_extension(PathView(file_name).extension()); + string mime_type = MimeType::from_extension(path_view.extension()); int32 flags = 0; if (file_type == FileType::Video) { flags |= telegram_api::inputMediaUploadedDocument::NOSOUND_VIDEO_MASK; @@ -4183,7 +4185,7 @@ unique_ptr dup_message_content(Td *td, DialogId dialog_id, const if (to_secret && !file_view.is_encrypted_secret()) { auto download_file_id = file_manager->dup_file_id(file_id); file_id = file_manager - ->register_generate(FileType::Encrypted, FileLocationSource::FromServer, file_view.suggested_name(), + ->register_generate(FileType::Encrypted, FileLocationSource::FromServer, file_view.suggested_path(), PSTRING() << "#file_id#" << download_file_id.get(), dialog_id, file_view.size()) .ok(); } diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 813482085..643ec2e21 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8910,7 +8910,7 @@ void MessagesManager::on_upload_imported_message_attachment(FileId file_id, CHECK(input_file != nullptr); td_->create_handler(std::move(promise)) - ->send(dialog_id, import_id, file_view.suggested_name(), file_id, + ->send(dialog_id, import_id, file_view.suggested_path(), file_id, get_fake_input_media(td_, std::move(input_file), file_id)); } diff --git a/td/telegram/SecureManager.cpp b/td/telegram/SecureManager.cpp index 89d49660a..79b196fdb 100644 --- a/td/telegram/SecureManager.cpp +++ b/td/telegram/SecureManager.cpp @@ -546,7 +546,7 @@ void SetSecureValue::start_upload(FileManager *file_manager, FileId &file_id, Se auto download_file_id = file_manager->dup_file_id(file_id); file_id = file_manager - ->register_generate(FileType::Secure, FileLocationSource::FromServer, file_view.suggested_name(), + ->register_generate(FileType::Secure, FileLocationSource::FromServer, file_view.suggested_path(), PSTRING() << "#file_id#" << download_file_id.get(), DialogId(), file_view.size()) .ok(); } diff --git a/td/telegram/SecureValue.cpp b/td/telegram/SecureValue.cpp index d57318b1f..09376b4fd 100644 --- a/td/telegram/SecureValue.cpp +++ b/td/telegram/SecureValue.cpp @@ -434,7 +434,7 @@ static td_api::object_ptr get_dated_file_object(FileManager * file_view.remote_location().get_access_hash(), file_view.remote_location().get_dc_id(), ""), FileLocationSource::FromServer, DialogId(), file_view.size(), - file_view.expected_size(), file_view.suggested_name()); + file_view.expected_size(), file_view.suggested_path()); return get_dated_file_object(file_manager, dated_file); } diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 4e16dde2a..b8344bb87 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -2385,8 +2385,8 @@ tl_object_ptr StickersManager::get_input_media( } auto mime_type = get_sticker_mime_type(s); if (!s->is_animated && !s->set_id.is_valid()) { - auto suggested_name = file_view.suggested_name(); - const PathView path_view(suggested_name); + auto suggested_path = file_view.suggested_path(); + const PathView path_view(suggested_path); if (path_view.extension() == "tgs") { mime_type = "application/x-tgsticker"; } diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index 0e7b73b5e..627974367 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -462,7 +462,7 @@ void FileNode::on_info_flushed() { info_changed_flag_ = false; } -string FileNode::suggested_name() const { +string FileNode::suggested_path() const { if (!remote_name_.empty()) { return remote_name_; } @@ -681,8 +681,8 @@ const string &FileView::remote_name() const { return node_->remote_name_; } -string FileView::suggested_name() const { - return node_->suggested_name(); +string FileView::suggested_path() const { + return node_->suggested_path(); } DialogId FileView::owner_dialog_id() const { @@ -1985,7 +1985,7 @@ bool FileManager::set_content(FileId file_id, BufferSlice bytes) { node->download_id_ = id; node->is_download_started_ = true; send_closure(file_load_manager_, &FileLoadManager::from_bytes, id, node->remote_.full.value().file_type_, - std::move(bytes), node->suggested_name()); + std::move(bytes), node->suggested_path()); return true; } @@ -2302,7 +2302,7 @@ void FileManager::run_download(FileNodePtr node, bool force_update_priority) { node->download_id_ = id; node->is_download_started_ = false; LOG(INFO) << "Run download of file " << file_id << " of size " << node->size_ << " from " - << node->remote_.full.value() << " with suggested name " << node->suggested_name() << " and encyption key " + << node->remote_.full.value() << " with suggested name " << node->suggested_path() << " and encyption key " << node->encryption_key_; auto download_offset = node->download_offset_; auto download_limit = node->download_limit_; @@ -2313,7 +2313,7 @@ void FileManager::run_download(FileNodePtr node, bool force_update_priority) { download_offset = 0; } send_closure(file_load_manager_, &FileLoadManager::download, id, node->remote_.full.value(), node->local_, - node->size_, node->suggested_name(), node->encryption_key_, node->can_search_locally_, download_offset, + node->size_, node->suggested_path(), node->encryption_key_, node->can_search_locally_, download_offset, download_limit, priority); } @@ -2667,7 +2667,7 @@ void FileManager::run_generate(FileNodePtr node) { QueryId id = queries_container_.create(Query{file_id, Query::Type::Generate}); node->generate_id_ = id; send_closure(file_generate_manager_, &FileGenerateManager::generate_file, id, *node->generate_, node->local_, - node->suggested_name(), [file_manager = this, id] { + node->suggested_path(), [file_manager = this, id] { class Callback : public FileGenerateCallback { ActorId actor_; uint64 query_id_; @@ -3490,7 +3490,7 @@ void FileManager::on_upload_ok(QueryId query_id, FileType file_type, const Parti file_info->download_priority_ = 0; FileView file_view(file_node); - string file_name = get_file_name(file_type, file_view.suggested_name()); + string file_name = get_file_name(file_type, file_view.suggested_path()); if (file_view.is_encrypted_secret()) { tl_object_ptr input_file; diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index f76ca7239..89ca92d73 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -118,7 +118,7 @@ class FileNode { void on_pmc_flushed(); void on_info_flushed(); - string suggested_name() const; + string suggested_path() const; private: friend class FileView; @@ -258,7 +258,7 @@ class FileView { const string &remote_name() const; - string suggested_name() const; + string suggested_path() const; DialogId owner_dialog_id() const; From ef59e478073934891aa4ff270d7d85069ad8233d Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 02:55:48 +0300 Subject: [PATCH 079/232] Fix uploadImportedMedia. --- td/telegram/MessagesManager.cpp | 5 ++++- td/telegram/files/FileManager.cpp | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 643ec2e21..8c5e62315 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -64,6 +64,7 @@ #include "td/utils/algorithm.h" #include "td/utils/format.h" #include "td/utils/misc.h" +#include "td/utils/PathView.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" #include "td/utils/Time.h" @@ -8909,8 +8910,10 @@ void MessagesManager::on_upload_imported_message_attachment(FileId file_id, } CHECK(input_file != nullptr); + auto suggested_path = file_view.suggested_path(); + const PathView path_view(suggested_path); td_->create_handler(std::move(promise)) - ->send(dialog_id, import_id, file_view.suggested_path(), file_id, + ->send(dialog_id, import_id, path_view.file_name().str(), file_id, get_fake_input_media(td_, std::move(input_file), file_id)); } diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index 627974367..fc9c8de5f 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -860,7 +860,7 @@ string FileManager::get_file_name(FileType file_type, Slice path) { break; case FileType::VoiceNote: if (extension != "ogg" && extension != "oga" && extension != "mp3" && extension != "mpeg3" && - extension != "m4a") { + extension != "m4a" && extension != "opus") { return fix_file_extension(file_name, "voice", "oga"); } break; @@ -3195,7 +3195,7 @@ FileType FileManager::guess_file_type(const tl_object_ptr &fi if (extension == "mp3" || extension == "mpeg3" || extension == "m4a") { return FileType::Audio; } - if (extension == "webp" || extension == "tgs" || extension == "opus") { + if (extension == "webp" || extension == "tgs") { return FileType::Sticker; } if (extension == "gif") { From d648ce0b69d55f69342d647c45290a2209fd3e6a Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 03:13:09 +0300 Subject: [PATCH 080/232] Remove chatActionImportingMessages. --- td/generate/scheme/td_api.tl | 2 -- td/generate/scheme/td_api.tlo | Bin 195568 -> 195464 bytes td/telegram/DialogAction.cpp | 11 +++++++---- td/telegram/DialogAction.h | 2 ++ td/telegram/MessagesManager.cpp | 7 +++++++ td/telegram/cli.cpp | 3 --- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 145e2e97b..27dfc6dd3 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1971,8 +1971,6 @@ chatActionStartPlayingGame = ChatAction; chatActionRecordingVideoNote = ChatAction; //@description The user is uploading a video note @progress Upload progress, as a percentage chatActionUploadingVideoNote progress:int32 = ChatAction; -//@description The user is importing message history. This action is sent automatically and can't be explicitly sent @progress Import progress, as a percentage -chatActionImportingMessages progress:int32 = ChatAction; //@description The user has cancelled the previous action chatActionCancel = ChatAction; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 37f89fc4089d1c9049c99f3623d29e609ffcbcea..d51bd584b362fd4ef9b13b29d71d7148139fcebb 100644 GIT binary patch delta 403 zcmezHox9^Z_XZU0Hhki6xH7C7Jno47`&!s!MG4W&5won7!H8L|ID$q~hi= zAs+@HC~{`g;jnEx2ojsz5vPM&@=Tl#3s}{LcoPW2B;EkG@|t)9H03Yi(G3VlKo{AO zfX4-Y@JTu(;?dcXh{vU86470{AV~$M$C~dXZNHPmm@) yPE1cNMhy{*Bo&+?(p-|Xy(Eb-WeO|Uj_DikGs &&action) { init(Type::UploadingVideoNote, uploading_action->progress_); break; } - case td_api::chatActionImportingMessages::ID: - init(Type::Cancel); // it can't be sent explicitly - break; default: UNREACHABLE(); break; @@ -258,7 +255,6 @@ tl_object_ptr DialogAction::get_chat_action_object() const { case Type::UploadingVideoNote: return td_api::make_object(progress_); case Type::ImportingMessages: - return td_api::make_object(progress_); case Type::SpeakingInVoiceChat: default: UNREACHABLE(); @@ -363,6 +359,13 @@ DialogAction DialogAction::get_speaking_action() { return DialogAction(Type::SpeakingInVoiceChat, 0); } +int32 DialogAction::get_importing_messages_action_progress() const { + if (type_ != Type::ImportingMessages) { + return -1; + } + return progress_; +} + StringBuilder &operator<<(StringBuilder &string_builder, const DialogAction &action) { string_builder << "ChatAction"; const char *type = [action_type = action.type_] { diff --git a/td/telegram/DialogAction.h b/td/telegram/DialogAction.h index 09826b47b..cd33d248a 100644 --- a/td/telegram/DialogAction.h +++ b/td/telegram/DialogAction.h @@ -64,6 +64,8 @@ class DialogAction { static DialogAction get_speaking_action(); + int32 get_importing_messages_action_progress() const; + friend bool operator==(const DialogAction &lhs, const DialogAction &rhs) { return lhs.type_ == rhs.type_ && lhs.progress_ == rhs.progress_; } diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 8c5e62315..b8bdadde0 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -7056,6 +7056,13 @@ void MessagesManager::on_user_dialog_action(DialogId dialog_id, MessageId top_th } return; } + { + auto message_import_progress = action.get_importing_messages_action_progress(); + if (message_import_progress >= 0) { + // TODO + return; + } + } if (!td_->contacts_manager_->have_min_user(user_id)) { LOG(DEBUG) << "Ignore typing of unknown " << user_id; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 3f1f6a634..57f2af242 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1312,9 +1312,6 @@ class CliClient final : public Actor { if (action == "uvn" || action == "upload_video_note") { return td_api::make_object(50); } - if (action == "im" || action == "import_messages") { - return td_api::make_object(50); - } return td_api::make_object(); } From f5f16a1f5d18aeb8258933fa3775884520ba9bfc Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 11:45:36 +0300 Subject: [PATCH 081/232] Add opus extension support. --- tdutils/generate/mime_types.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdutils/generate/mime_types.txt b/tdutils/generate/mime_types.txt index 188845c27..18dcfb775 100644 --- a/tdutils/generate/mime_types.txt +++ b/tdutils/generate/mime_types.txt @@ -594,7 +594,7 @@ audio/basic au snd audio/midi mid midi kar rmi audio/mp4 m4a mp4a audio/mpeg mpga mp2 mp2a mp3 m2a m3a -audio/ogg oga ogg spx +audio/ogg oga ogg opus spx audio/s3m s3m audio/silk sil audio/vnd.dece.audio uva uvva From 6878b41dc753fa12600a5e5f7276584de820b42d Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 13:07:03 +0300 Subject: [PATCH 082/232] Update layer 123. --- td/generate/scheme/td_api.tl | 37 ---------------------------- td/generate/scheme/td_api.tlo | Bin 195464 -> 194220 bytes td/generate/scheme/telegram_api.tl | 13 ---------- td/generate/scheme/telegram_api.tlo | Bin 229124 -> 227064 bytes td/telegram/ContactsManager.cpp | 12 ++++----- td/telegram/ContactsManager.h | 4 +-- td/telegram/Td.cpp | 4 +-- td/telegram/Td.h | 4 +-- td/telegram/cli.cpp | 2 ++ 9 files changed, 14 insertions(+), 62 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 27dfc6dd3..840c96d9a 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4418,43 +4418,6 @@ importMessages chat_id:int53 message_file:InputFile attached_files:vectorFScS8%~7A6gC#yQ(HwVC3j6#nTp>x(28r6!i7I%g!7c;=O5mZbV*=4CT5 zF!)J6UN}AAA*0UZ8I>NBCzxWcq*Sf0?g+QqgfJC|amaJE5}vY3=clC=r users:Vector = messages.ExportedChatInvites; - -messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector = messages.ExportedChatInvite; - -messages.chatInviteImporters#81b6b00a count:int importers:Vector users:Vector = messages.ChatInviteImporters; - messages.historyImport#1662af0b id:long = messages.HistoryImport; messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true title:flags.2?string = messages.HistoryImportParsed; @@ -1458,11 +1450,6 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; -messages.getExportedChatInvites#6a72ac6c flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; -messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; -messages.deleteRevokedExportedChatInvites#52041463 peer:InputPeer = Bool; -messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; -messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.deleteChat#83247d11 chat_id:int = Bool; messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages; messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index 672599f199d552a15138ce8f70a210d98a0419a8..c3ca7a83b92086f6717c59ef1cc1675486c8dd8e 100644 GIT binary patch delta 104 zcmZqq$NS?gFYlw-`c@23V7HNXGt1@=EFIdDw-_AR{KoJJJ7ekQi62|Huu=8qQh-DSWoOGb4W&`Ji7_;d$P#sC%&tGT6NdrJf3muniR zZpGAC4D>fh&GZIKCY|XTU5p}=eP)V)4VnHyhslKnB)L5&oGFFP64gJc82$mt-#jMd z!vr)_`q_O=woGv7F)&QdEfb&qM~BJ60;B{M)K}%WVo<%0X(Y&bAT z^ji&KYI`ygCYzR3#`)<|1hb8jGR7) zo0(&JgCMia^a2xR4wlm5)S~GZJeeh@*WO}MhQx3L2lERy^_(?DSt#aXI=UDb+|&Ov zG8#@!Stl@=;~~%oHx(xLtl*jKk{~es#4#oTxOFVk9jqA@r_bePW@pLC%*&pv(62DP Q<`$C*B+5+qnUACc096E5aR2}S diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index dc61580bb..723915227 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1626,7 +1626,7 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { promise_.set_error(std::move(status)); } }; - +/* class EditChatInviteLinkQuery : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -1649,7 +1649,7 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { flags |= telegram_api::messages_editExportedChatInvite::REVOKED_MASK; } send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( - flags, false /*ignored*/, std::move(input_peer), invite_link, expire_date, usage_limit))); + flags, false / *ignored* /, std::move(input_peer), invite_link, expire_date, usage_limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1707,7 +1707,7 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { flags |= telegram_api::messages_getExportedChatInvites::REVOKED_MASK; } send_query(G()->net_query_creator().create( - telegram_api::messages_getExportedChatInvites(flags, false /*ignored*/, std::move(input_peer), + telegram_api::messages_getExportedChatInvites(flags, false / *ignored* /, std::move(input_peer), std::move(input_user), offset_date, offset_invite_link, limit))); } @@ -1874,7 +1874,7 @@ class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { promise_.set_error(std::move(status)); } }; - +*/ class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -7218,7 +7218,7 @@ void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 e td_->create_handler(std::move(promise)) ->send(dialog_id, expire_date, usage_limit, is_permanent); } - +/* void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise) { @@ -7281,7 +7281,7 @@ void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, td_->create_handler(std::move(promise))->send(dialog_id); } - +*/ void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { if (invite_link_infos_.count(invite_link) > 0) { return promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 4d7b62ee1..3eec07f93 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -396,7 +396,7 @@ class ContactsManager : public Actor { void export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent, Promise> &&promise); - + /* void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise); @@ -411,7 +411,7 @@ class ContactsManager : public Actor { void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); void delete_all_revoked_dialog_invite_links(DialogId dialog_id, Promise &&promise); - + */ void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 4797bfba1..5d2d7429c 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6311,7 +6311,7 @@ void Td::on_request(uint64 id, const td_api::replacePermanentChatInviteLink &req CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), 0, 0, true, std::move(promise)); } - +/* void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.member_limit_, @@ -6359,7 +6359,7 @@ void Td::on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &re CREATE_OK_REQUEST_PROMISE(); contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), std::move(promise)); } - +*/ void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index db5e62a91..4d3561727 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -793,7 +793,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatAdministrators &request); void on_request(uint64 id, const td_api::replacePermanentChatInviteLink &request); - + /* void on_request(uint64 id, const td_api::createChatInviteLink &request); void on_request(uint64 id, td_api::editChatInviteLink &request); @@ -807,7 +807,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request); void on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request); - + */ void on_request(uint64 id, td_api::checkChatInviteLink &request); void on_request(uint64 id, td_api::joinChatByInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 57f2af242..a6e0d6f5e 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2696,6 +2696,7 @@ class CliClient final : public Actor { } else if (op == "rpcil") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id))); + /* } else if (op == "ccilt") { string chat_id; int32 expire_date; @@ -2743,6 +2744,7 @@ class CliClient final : public Actor { } else if (op == "darcil") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id))); + */ } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From 37ef232d47a4026d463bbbf54b72d7062e94d2d9 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 23:15:33 +0300 Subject: [PATCH 083/232] Fix function name. --- td/telegram/ContactsManager.cpp | 6 +++--- td/telegram/ContactsManager.h | 4 ++-- td/telegram/cli.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 723915227..493807ad6 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -11858,7 +11858,7 @@ void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, Ch void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full, tl_object_ptr &&invite_link) { CHECK(chat_full != nullptr); - if (update_persistent_invite_link(chat_full->invite_link, std::move(invite_link))) { + if (update_permanent_invite_link(chat_full->invite_link, std::move(invite_link))) { chat_full->is_changed = true; } } @@ -11866,7 +11866,7 @@ void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full, void ContactsManager::on_update_channel_full_invite_link( ChannelFull *channel_full, tl_object_ptr &&invite_link) { CHECK(channel_full != nullptr); - if (update_persistent_invite_link(channel_full->invite_link, std::move(invite_link))) { + if (update_permanent_invite_link(channel_full->invite_link, std::move(invite_link))) { channel_full->is_changed = true; } } @@ -12174,7 +12174,7 @@ void ContactsManager::remove_dialog_access_by_invite_link(DialogId dialog_id) { invite_link_info_expire_timeout_.cancel_timeout(dialog_id.get()); } -bool ContactsManager::update_persistent_invite_link( +bool ContactsManager::update_permanent_invite_link( DialogInviteLink &invite_link, tl_object_ptr &&exported_chat_invite) { DialogInviteLink new_invite_link(std::move(exported_chat_invite)); if (new_invite_link != invite_link) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 3eec07f93..59dbc0397 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -1049,8 +1049,8 @@ class ContactsManager : public Actor { Status can_manage_dialog_invite_links(DialogId dialog_id); - bool update_persistent_invite_link(DialogInviteLink &invite_link, - tl_object_ptr &&exported_chat_invite); + bool update_permanent_invite_link(DialogInviteLink &invite_link, + tl_object_ptr &&exported_chat_invite); const DialogParticipant *get_chat_participant(ChatId chat_id, UserId user_id) const; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index a6e0d6f5e..1096e1588 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2696,7 +2696,7 @@ class CliClient final : public Actor { } else if (op == "rpcil") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id))); - /* + /* } else if (op == "ccilt") { string chat_id; int32 expire_date; From d246318a5c436a39b9b6bae6c6066ea28a1a959b Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jan 2021 23:59:09 +0300 Subject: [PATCH 084/232] Update permanent invite link after replacing. --- td/telegram/ContactsManager.cpp | 39 ++++++++++++++++++++++++++++----- td/telegram/ContactsManager.h | 5 +++-- td/telegram/DialogInviteLink.h | 4 ++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 493807ad6..a86a5ce94 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1618,6 +1618,9 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { if (invite_link.get_administrator_user_id() != td->contacts_manager_->get_my_id()) { return on_error(id, Status::Error(500, "Receive invalid invite link creator")); } + if (invite_link.is_permanent()) { + td->contacts_manager_->on_get_permanent_dialog_invite_link(dialog_id_, invite_link); + } promise_.set_value(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); } @@ -11855,10 +11858,38 @@ void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, Ch } } +void ContactsManager::on_get_permanent_dialog_invite_link(DialogId dialog_id, const DialogInviteLink &invite_link) { + switch (dialog_id.get_type()) { + case DialogType::Chat: { + auto chat_id = dialog_id.get_chat_id(); + auto chat_full = get_chat_full_force(chat_id, "on_get_permanent_dialog_invite_link"); + if (chat_full != nullptr && update_permanent_invite_link(chat_full->invite_link, invite_link)) { + chat_full->is_changed = true; + update_chat_full(chat_full, chat_id); + } + break; + } + case DialogType::Channel: { + auto channel_id = dialog_id.get_channel_id(); + auto channel_full = get_channel_full_force(channel_id, "on_get_permanent_dialog_invite_link"); + if (channel_full != nullptr && update_permanent_invite_link(channel_full->invite_link, invite_link)) { + channel_full->is_changed = true; + update_channel_full(channel_full, channel_id); + } + break; + } + case DialogType::User: + case DialogType::SecretChat: + case DialogType::None: + default: + UNREACHABLE(); + } +} + void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full, tl_object_ptr &&invite_link) { CHECK(chat_full != nullptr); - if (update_permanent_invite_link(chat_full->invite_link, std::move(invite_link))) { + if (update_permanent_invite_link(chat_full->invite_link, DialogInviteLink(std::move(invite_link)))) { chat_full->is_changed = true; } } @@ -11866,7 +11897,7 @@ void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full, void ContactsManager::on_update_channel_full_invite_link( ChannelFull *channel_full, tl_object_ptr &&invite_link) { CHECK(channel_full != nullptr); - if (update_permanent_invite_link(channel_full->invite_link, std::move(invite_link))) { + if (update_permanent_invite_link(channel_full->invite_link, DialogInviteLink(std::move(invite_link)))) { channel_full->is_changed = true; } } @@ -12174,9 +12205,7 @@ void ContactsManager::remove_dialog_access_by_invite_link(DialogId dialog_id) { invite_link_info_expire_timeout_.cancel_timeout(dialog_id.get()); } -bool ContactsManager::update_permanent_invite_link( - DialogInviteLink &invite_link, tl_object_ptr &&exported_chat_invite) { - DialogInviteLink new_invite_link(std::move(exported_chat_invite)); +bool ContactsManager::update_permanent_invite_link(DialogInviteLink &invite_link, DialogInviteLink new_invite_link) { if (new_invite_link != invite_link) { if (invite_link.is_valid() && invite_link.get_invite_link() != new_invite_link.get_invite_link()) { // old link was invalidated diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 59dbc0397..bbb784818 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -226,6 +226,8 @@ class ContactsManager : public Actor { bool on_get_channel_error(ChannelId channel_id, const Status &status, const string &source); + void on_get_permanent_dialog_invite_link(DialogId dialog_id, const DialogInviteLink &invite_link); + void on_get_dialog_invite_link_info(const string &invite_link, tl_object_ptr &&chat_invite_ptr, Promise &&promise); @@ -1049,8 +1051,7 @@ class ContactsManager : public Actor { Status can_manage_dialog_invite_links(DialogId dialog_id); - bool update_permanent_invite_link(DialogInviteLink &invite_link, - tl_object_ptr &&exported_chat_invite); + bool update_permanent_invite_link(DialogInviteLink &invite_link, DialogInviteLink new_invite_link); const DialogParticipant *get_chat_participant(ChatId chat_id, UserId user_id) const; diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index 4a7146330..8eb8f1c6f 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -51,6 +51,10 @@ class DialogInviteLink { return !invite_link_.empty() && administrator_user_id_.is_valid() && date_ > 0; } + bool is_permanent() const { + return is_permanent_; + } + bool is_expired() const; int32 get_expire_time() const; From cd3abdbeb21c7a67cef2982533a1703a914cdf24 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 27 Jan 2021 02:16:17 +0300 Subject: [PATCH 085/232] Move dialog participant methods from MessagesManager to ContactsManager. --- td/telegram/ContactsManager.cpp | 288 ++++++++++++++++++++++++++++- td/telegram/ContactsManager.h | 73 +++++--- td/telegram/GroupCallManager.cpp | 2 +- td/telegram/MessagesManager.cpp | 298 ------------------------------- td/telegram/MessagesManager.h | 23 --- td/telegram/Td.cpp | 18 +- 6 files changed, 343 insertions(+), 359 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index a86a5ce94..d2fdaddba 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -14064,7 +14064,7 @@ void ContactsManager::on_update_secret_chat(SecretChatId secret_chat_id, int64 a } std::pair> ContactsManager::search_among_users(const vector &user_ids, - const string &query, int32 limit) { + const string &query, int32 limit) const { Hints hints; // TODO cache Hints for (auto user_id : user_ids) { @@ -14085,6 +14085,272 @@ std::pair> ContactsManager::search_among_users(const vecto transform(result.second, [](int64 key) { return UserId(narrow_cast(key)); })}; } +void ContactsManager::add_dialog_participant(DialogId dialog_id, UserId user_id, int32 forward_limit, + Promise &&promise) { + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return promise.set_error(Status::Error(3, "Can't add members to a private chat")); + case DialogType::Chat: + return add_chat_participant(dialog_id.get_chat_id(), user_id, forward_limit, std::move(promise)); + case DialogType::Channel: + return add_channel_participant(dialog_id.get_channel_id(), user_id, std::move(promise)); + case DialogType::SecretChat: + return promise.set_error(Status::Error(3, "Can't add members to a secret chat")); + case DialogType::None: + default: + UNREACHABLE(); + } +} + +void ContactsManager::add_dialog_participants(DialogId dialog_id, const vector &user_ids, + Promise &&promise) { + if (td_->auth_manager_->is_bot()) { + return promise.set_error(Status::Error(3, "Method is not available for bots")); + } + + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return promise.set_error(Status::Error(3, "Can't add members to a private chat")); + case DialogType::Chat: + return promise.set_error(Status::Error(3, "Can't add many members at once to a basic group chat")); + case DialogType::Channel: + return add_channel_participants(dialog_id.get_channel_id(), user_ids, std::move(promise)); + case DialogType::SecretChat: + return promise.set_error(Status::Error(3, "Can't add members to a secret chat")); + case DialogType::None: + default: + UNREACHABLE(); + } +} + +void ContactsManager::set_dialog_participant_status(DialogId dialog_id, UserId user_id, + const tl_object_ptr &chat_member_status, + Promise &&promise) { + auto status = get_dialog_participant_status(chat_member_status); + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return promise.set_error(Status::Error(3, "Chat member status can't be changed in private chats")); + case DialogType::Chat: + return change_chat_participant_status(dialog_id.get_chat_id(), user_id, status, std::move(promise)); + case DialogType::Channel: + return change_channel_participant_status(dialog_id.get_channel_id(), user_id, status, std::move(promise)); + case DialogType::SecretChat: + return promise.set_error(Status::Error(3, "Chat member status can't be changed in secret chats")); + case DialogType::None: + default: + UNREACHABLE(); + } +} + +void ContactsManager::ban_dialog_participant(DialogId dialog_id, UserId user_id, int32 banned_until_date, + bool revoke_messages, Promise &&promise) { + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return promise.set_error(Status::Error(3, "Can't ban members in a private chat")); + case DialogType::Chat: + return delete_chat_participant(dialog_id.get_chat_id(), user_id, revoke_messages, std::move(promise)); + case DialogType::Channel: + return change_channel_participant_status(dialog_id.get_channel_id(), user_id, + DialogParticipantStatus::Banned(banned_until_date), std::move(promise)); + case DialogType::SecretChat: + return promise.set_error(Status::Error(3, "Can't ban members in a secret chat")); + case DialogType::None: + default: + UNREACHABLE(); + } +} + +DialogParticipant ContactsManager::get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, + bool force, Promise &&promise) { + LOG(INFO) << "Receive GetChatMember request to get " << user_id << " in " << dialog_id << " with random_id " + << random_id; + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + promise.set_error(Status::Error(3, "Chat not found")); + return DialogParticipant(); + } + + switch (dialog_id.get_type()) { + case DialogType::User: { + auto peer_user_id = dialog_id.get_user_id(); + if (user_id == get_my_id()) { + promise.set_value(Unit()); + return {user_id, peer_user_id, 0, DialogParticipantStatus::Member()}; + } + if (user_id == peer_user_id) { + promise.set_value(Unit()); + return {peer_user_id, user_id, 0, DialogParticipantStatus::Member()}; + } + + promise.set_error(Status::Error(3, "User is not a member of the private chat")); + break; + } + case DialogType::Chat: + return get_chat_participant(dialog_id.get_chat_id(), user_id, force, std::move(promise)); + case DialogType::Channel: + return get_channel_participant(dialog_id.get_channel_id(), user_id, random_id, force, std::move(promise)); + case DialogType::SecretChat: { + auto peer_user_id = get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + if (user_id == get_my_id()) { + promise.set_value(Unit()); + return {user_id, peer_user_id.is_valid() ? peer_user_id : user_id, 0, DialogParticipantStatus::Member()}; + } + if (peer_user_id.is_valid() && user_id == peer_user_id) { + promise.set_value(Unit()); + return {peer_user_id, user_id, 0, DialogParticipantStatus::Member()}; + } + + promise.set_error(Status::Error(3, "User is not a member of the secret chat")); + break; + } + case DialogType::None: + default: + UNREACHABLE(); + promise.set_error(Status::Error(500, "Wrong chat type")); + } + return DialogParticipant(); +} + +DialogParticipants ContactsManager::search_private_chat_participants(UserId my_user_id, UserId peer_user_id, + const string &query, int32 limit, + DialogParticipantsFilter filter) const { + vector user_ids; + switch (filter.type) { + case DialogParticipantsFilter::Type::Contacts: + if (peer_user_id.is_valid() && is_user_contact(peer_user_id)) { + user_ids.push_back(peer_user_id); + } + break; + case DialogParticipantsFilter::Type::Administrators: + break; + case DialogParticipantsFilter::Type::Members: + case DialogParticipantsFilter::Type::Mention: + user_ids.push_back(my_user_id); + if (peer_user_id.is_valid() && peer_user_id != my_user_id) { + user_ids.push_back(peer_user_id); + } + break; + case DialogParticipantsFilter::Type::Restricted: + break; + case DialogParticipantsFilter::Type::Banned: + break; + case DialogParticipantsFilter::Type::Bots: + if (td_->auth_manager_->is_bot()) { + user_ids.push_back(my_user_id); + } + if (peer_user_id.is_valid() && is_user_bot(peer_user_id) && peer_user_id != my_user_id) { + user_ids.push_back(peer_user_id); + } + break; + default: + UNREACHABLE(); + } + + auto result = search_among_users(user_ids, query, limit); + return {result.first, transform(result.second, [&](UserId user_id) { + return DialogParticipant(user_id, + user_id == my_user_id && peer_user_id.is_valid() ? peer_user_id : my_user_id, 0, + DialogParticipantStatus::Member()); + })}; +} + +void ContactsManager::search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, + DialogParticipantsFilter filter, bool without_bot_info, + Promise &&promise) { + LOG(INFO) << "Receive searchChatMembers request to search for \"" << query << "\" in " << dialog_id << " with filter " + << filter; + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + if (limit < 0) { + return promise.set_error(Status::Error(3, "Parameter limit must be non-negative")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + promise.set_value(search_private_chat_participants(get_my_id(), dialog_id.get_user_id(), query, limit, filter)); + return; + case DialogType::Chat: + return search_chat_participants(dialog_id.get_chat_id(), query, limit, filter, std::move(promise)); + case DialogType::Channel: { + td_api::object_ptr request_filter; + string additional_query; + int32 additional_limit = 0; + switch (filter.type) { + case DialogParticipantsFilter::Type::Contacts: + request_filter = td_api::make_object(); + break; + case DialogParticipantsFilter::Type::Administrators: + request_filter = td_api::make_object(); + break; + case DialogParticipantsFilter::Type::Members: + request_filter = td_api::make_object(query); + break; + case DialogParticipantsFilter::Type::Restricted: + request_filter = td_api::make_object(query); + break; + case DialogParticipantsFilter::Type::Banned: + request_filter = td_api::make_object(query); + break; + case DialogParticipantsFilter::Type::Mention: + request_filter = + td_api::make_object(query, filter.top_thread_message_id.get()); + break; + case DialogParticipantsFilter::Type::Bots: + request_filter = td_api::make_object(); + break; + default: + UNREACHABLE(); + } + switch (filter.type) { + case DialogParticipantsFilter::Type::Contacts: + case DialogParticipantsFilter::Type::Administrators: + case DialogParticipantsFilter::Type::Bots: + additional_query = query; + additional_limit = limit; + limit = 100; + break; + case DialogParticipantsFilter::Type::Members: + case DialogParticipantsFilter::Type::Restricted: + case DialogParticipantsFilter::Type::Banned: + case DialogParticipantsFilter::Type::Mention: + // query is passed to the server request + break; + default: + UNREACHABLE(); + } + + return get_channel_participants(dialog_id.get_channel_id(), std::move(request_filter), + std::move(additional_query), 0, limit, additional_limit, without_bot_info, + std::move(promise)); + } + case DialogType::SecretChat: { + auto peer_user_id = get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + promise.set_value(search_private_chat_participants(get_my_id(), peer_user_id, query, limit, filter)); + return; + } + case DialogType::None: + default: + UNREACHABLE(); + promise.set_error(Status::Error(500, "Wrong chat type")); + } +} + DialogParticipant ContactsManager::get_chat_participant(ChatId chat_id, UserId user_id, bool force, Promise &&promise) { LOG(INFO) << "Trying to get " << user_id << " as member of " << chat_id; @@ -14297,6 +14563,26 @@ void ContactsManager::do_get_channel_participants(ChannelId channel_id, ChannelP vector ContactsManager::get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise) { + LOG(INFO) << "Receive GetChatAdministrators request in " << dialog_id << " with " << left_tries << " left tries"; + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + promise.set_error(Status::Error(3, "Chat not found")); + return {}; + } + + switch (dialog_id.get_type()) { + case DialogType::User: + case DialogType::SecretChat: + promise.set_value(Unit()); + return {}; + case DialogType::Chat: + case DialogType::Channel: + break; + case DialogType::None: + default: + UNREACHABLE(); + return {}; + } + auto it = dialog_administrators_.find(dialog_id); if (it != dialog_administrators_.end()) { promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index bbb784818..6092eaac4 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -374,21 +374,6 @@ class ContactsManager : public Actor { void load_statistics_graph(DialogId dialog_id, const string &token, int64 x, Promise> &&promise); - void add_chat_participant(ChatId chat_id, UserId user_id, int32 forward_limit, Promise &&promise); - - void add_channel_participant(ChannelId channel_id, UserId user_id, Promise &&promise, - DialogParticipantStatus old_status = DialogParticipantStatus::Left()); - - void add_channel_participants(ChannelId channel_id, const vector &user_ids, Promise &&promise); - - void change_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status, - Promise &&promise); - - void change_channel_participant_status(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, - Promise &&promise); - - void delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, Promise &&promise); - void can_transfer_ownership(Promise &&promise); static td_api::object_ptr get_can_transfer_ownership_result_object( @@ -503,24 +488,31 @@ class ContactsManager : public Actor { ChannelId get_channel_linked_channel_id(ChannelId channel_id); int32 get_channel_slow_mode_delay(ChannelId channel_id); - std::pair> search_among_users(const vector &user_ids, const string &query, int32 limit); + void add_dialog_participant(DialogId dialog_id, UserId user_id, int32 forward_limit, Promise &&promise); - DialogParticipant get_chat_participant(ChatId chat_id, UserId user_id, bool force, Promise &&promise); + void add_dialog_participants(DialogId dialog_id, const vector &user_ids, Promise &&promise); - void search_chat_participants(ChatId chat_id, const string &query, int32 limit, DialogParticipantsFilter filter, - Promise &&promise); + void set_dialog_participant_status(DialogId dialog_id, UserId user_id, + const tl_object_ptr &chat_member_status, + Promise &&promise); - DialogParticipant get_channel_participant(ChannelId channel_id, UserId user_id, int64 &random_id, bool force, - Promise &&promise); - - void get_channel_participants(ChannelId channel_id, tl_object_ptr &&filter, - string additional_query, int32 offset, int32 limit, int32 additional_limit, - bool without_bot_info, Promise &&promise); + void ban_dialog_participant(DialogId dialog_id, UserId user_id, int32 banned_until_date, bool revoke_messages, + Promise &&promise); DialogParticipant get_dialog_participant(ChannelId channel_id, tl_object_ptr &&participant_ptr) const; - vector get_dialog_administrators(DialogId chat_id, int left_tries, Promise &&promise); + DialogParticipant get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, + Promise &&promise); + + void search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, DialogParticipantsFilter filter, + bool without_bot_info, Promise &&promise); + + vector get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise); + + void get_channel_participants(ChannelId channel_id, tl_object_ptr &&filter, + string additional_query, int32 offset, int32 limit, int32 additional_limit, + bool without_bot_info, Promise &&promise); int32 get_user_id_object(UserId user_id, const char *source) const; @@ -1053,10 +1045,28 @@ class ContactsManager : public Actor { bool update_permanent_invite_link(DialogInviteLink &invite_link, DialogInviteLink new_invite_link); + void add_chat_participant(ChatId chat_id, UserId user_id, int32 forward_limit, Promise &&promise); + + void add_channel_participant(ChannelId channel_id, UserId user_id, Promise &&promise, + DialogParticipantStatus old_status = DialogParticipantStatus::Left()); + + void add_channel_participants(ChannelId channel_id, const vector &user_ids, Promise &&promise); + const DialogParticipant *get_chat_participant(ChatId chat_id, UserId user_id) const; static const DialogParticipant *get_chat_participant(const ChatFull *chat_full, UserId user_id); + std::pair> search_among_users(const vector &user_ids, const string &query, + int32 limit) const; + + DialogParticipants search_private_chat_participants(UserId my_user_id, UserId peer_user_id, const string &query, + int32 limit, DialogParticipantsFilter filter) const; + + DialogParticipant get_chat_participant(ChatId chat_id, UserId user_id, bool force, Promise &&promise); + + DialogParticipant get_channel_participant(ChannelId channel_id, UserId user_id, int64 &random_id, bool force, + Promise &&promise); + static string get_dialog_administrators_database_key(DialogId dialog_id); void load_dialog_administrators(DialogId dialog_id, Promise &&promise); @@ -1108,7 +1118,16 @@ class ContactsManager : public Actor { void update_dialogs_for_discussion(DialogId dialog_id, bool is_suitable); - void delete_chat_participant(ChatId chat_id, UserId user_id, Promise &&promise); + void change_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status, + Promise &&promise); + + void change_channel_participant_status(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, + Promise &&promise); + + void delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, Promise &&promise); + + void search_chat_participants(ChatId chat_id, const string &query, int32 limit, DialogParticipantsFilter filter, + Promise &&promise); void do_search_chat_participants(ChatId chat_id, const string &query, int32 limit, DialogParticipantsFilter filter, Promise &&promise); diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 9b658b8d4..8243d8b5c 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1490,7 +1490,7 @@ void GroupCallManager::try_load_group_call_administrators(InputGroupCallId input send_closure(actor_id, &GroupCallManager::finish_load_group_call_administrators, input_group_call_id, std::move(result)); }); - td_->messages_manager_->search_dialog_participants( + td_->contacts_manager_->search_dialog_participants( dialog_id, string(), 100, DialogParticipantsFilter(DialogParticipantsFilter::Type::Administrators), true, std::move(promise)); } diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index b8bdadde0..810dc2150 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -30800,304 +30800,6 @@ void MessagesManager::unpin_all_dialog_messages_on_server(DialogId dialog_id, ui ->send(dialog_id); } -void MessagesManager::add_dialog_participant(DialogId dialog_id, UserId user_id, int32 forward_limit, - Promise &&promise) { - if (!have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(3, "Chat not found")); - } - - switch (dialog_id.get_type()) { - case DialogType::User: - return promise.set_error(Status::Error(3, "Can't add members to a private chat")); - case DialogType::Chat: - return td_->contacts_manager_->add_chat_participant(dialog_id.get_chat_id(), user_id, forward_limit, - std::move(promise)); - case DialogType::Channel: - return td_->contacts_manager_->add_channel_participant(dialog_id.get_channel_id(), user_id, std::move(promise)); - case DialogType::SecretChat: - return promise.set_error(Status::Error(3, "Can't add members to a secret chat")); - case DialogType::None: - default: - UNREACHABLE(); - } -} - -void MessagesManager::add_dialog_participants(DialogId dialog_id, const vector &user_ids, - Promise &&promise) { - if (td_->auth_manager_->is_bot()) { - return promise.set_error(Status::Error(3, "Method is not available for bots")); - } - - if (!have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(3, "Chat not found")); - } - - switch (dialog_id.get_type()) { - case DialogType::User: - return promise.set_error(Status::Error(3, "Can't add members to a private chat")); - case DialogType::Chat: - return promise.set_error(Status::Error(3, "Can't add many members at once to a basic group chat")); - case DialogType::Channel: - return td_->contacts_manager_->add_channel_participants(dialog_id.get_channel_id(), user_ids, std::move(promise)); - case DialogType::SecretChat: - return promise.set_error(Status::Error(3, "Can't add members to a secret chat")); - case DialogType::None: - default: - UNREACHABLE(); - } -} - -void MessagesManager::set_dialog_participant_status(DialogId dialog_id, UserId user_id, - const tl_object_ptr &chat_member_status, - Promise &&promise) { - auto status = get_dialog_participant_status(chat_member_status); - if (!have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(3, "Chat not found")); - } - - switch (dialog_id.get_type()) { - case DialogType::User: - return promise.set_error(Status::Error(3, "Chat member status can't be changed in private chats")); - case DialogType::Chat: - return td_->contacts_manager_->change_chat_participant_status(dialog_id.get_chat_id(), user_id, status, - std::move(promise)); - case DialogType::Channel: - return td_->contacts_manager_->change_channel_participant_status(dialog_id.get_channel_id(), user_id, status, - std::move(promise)); - case DialogType::SecretChat: - return promise.set_error(Status::Error(3, "Chat member status can't be changed in secret chats")); - case DialogType::None: - default: - UNREACHABLE(); - } -} - -void MessagesManager::ban_dialog_participant(DialogId dialog_id, UserId user_id, int32 banned_until_date, - bool revoke_messages, Promise &&promise) { - if (!have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(3, "Chat not found")); - } - - switch (dialog_id.get_type()) { - case DialogType::User: - return promise.set_error(Status::Error(3, "Can't ban members in a private chat")); - case DialogType::Chat: - return td_->contacts_manager_->delete_chat_participant(dialog_id.get_chat_id(), user_id, revoke_messages, - std::move(promise)); - case DialogType::Channel: - return td_->contacts_manager_->change_channel_participant_status( - dialog_id.get_channel_id(), user_id, DialogParticipantStatus::Banned(banned_until_date), std::move(promise)); - case DialogType::SecretChat: - return promise.set_error(Status::Error(3, "Can't ban members in a secret chat")); - case DialogType::None: - default: - UNREACHABLE(); - } -} - -DialogParticipant MessagesManager::get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, - bool force, Promise &&promise) { - LOG(INFO) << "Receive GetChatMember request to get " << user_id << " in " << dialog_id << " with random_id " - << random_id; - if (!have_dialog_force(dialog_id)) { - promise.set_error(Status::Error(3, "Chat not found")); - return DialogParticipant(); - } - - switch (dialog_id.get_type()) { - case DialogType::User: { - auto peer_user_id = dialog_id.get_user_id(); - if (user_id == td_->contacts_manager_->get_my_id()) { - promise.set_value(Unit()); - return {user_id, peer_user_id, 0, DialogParticipantStatus::Member()}; - } - if (user_id == peer_user_id) { - promise.set_value(Unit()); - return {peer_user_id, user_id, 0, DialogParticipantStatus::Member()}; - } - - promise.set_error(Status::Error(3, "User is not a member of the private chat")); - break; - } - case DialogType::Chat: - return td_->contacts_manager_->get_chat_participant(dialog_id.get_chat_id(), user_id, force, std::move(promise)); - case DialogType::Channel: - return td_->contacts_manager_->get_channel_participant(dialog_id.get_channel_id(), user_id, random_id, force, - std::move(promise)); - case DialogType::SecretChat: { - auto peer_user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); - if (user_id == td_->contacts_manager_->get_my_id()) { - promise.set_value(Unit()); - return {user_id, peer_user_id.is_valid() ? peer_user_id : user_id, 0, DialogParticipantStatus::Member()}; - } - if (peer_user_id.is_valid() && user_id == peer_user_id) { - promise.set_value(Unit()); - return {peer_user_id, user_id, 0, DialogParticipantStatus::Member()}; - } - - promise.set_error(Status::Error(3, "User is not a member of the secret chat")); - break; - } - case DialogType::None: - default: - UNREACHABLE(); - promise.set_error(Status::Error(500, "Wrong chat type")); - } - return DialogParticipant(); -} - -DialogParticipants MessagesManager::search_private_chat_participants(UserId my_user_id, UserId peer_user_id, - const string &query, int32 limit, - DialogParticipantsFilter filter) const { - vector user_ids; - switch (filter.type) { - case DialogParticipantsFilter::Type::Contacts: - if (peer_user_id.is_valid() && td_->contacts_manager_->is_user_contact(peer_user_id)) { - user_ids.push_back(peer_user_id); - } - break; - case DialogParticipantsFilter::Type::Administrators: - break; - case DialogParticipantsFilter::Type::Members: - case DialogParticipantsFilter::Type::Mention: - user_ids.push_back(my_user_id); - if (peer_user_id.is_valid() && peer_user_id != my_user_id) { - user_ids.push_back(peer_user_id); - } - break; - case DialogParticipantsFilter::Type::Restricted: - break; - case DialogParticipantsFilter::Type::Banned: - break; - case DialogParticipantsFilter::Type::Bots: - if (td_->auth_manager_->is_bot()) { - user_ids.push_back(my_user_id); - } - if (peer_user_id.is_valid() && td_->contacts_manager_->is_user_bot(peer_user_id) && peer_user_id != my_user_id) { - user_ids.push_back(peer_user_id); - } - break; - default: - UNREACHABLE(); - } - - auto result = td_->contacts_manager_->search_among_users(user_ids, query, limit); - return {result.first, transform(result.second, [&](UserId user_id) { - return DialogParticipant(user_id, - user_id == my_user_id && peer_user_id.is_valid() ? peer_user_id : my_user_id, 0, - DialogParticipantStatus::Member()); - })}; -} - -void MessagesManager::search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, - DialogParticipantsFilter filter, bool without_bot_info, - Promise &&promise) { - LOG(INFO) << "Receive searchChatMembers request to search for \"" << query << "\" in " << dialog_id << " with filter " - << filter; - if (!have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(3, "Chat not found")); - } - if (limit < 0) { - return promise.set_error(Status::Error(3, "Parameter limit must be non-negative")); - } - - switch (dialog_id.get_type()) { - case DialogType::User: - promise.set_value(search_private_chat_participants(td_->contacts_manager_->get_my_id(), dialog_id.get_user_id(), - query, limit, filter)); - return; - case DialogType::Chat: - return td_->contacts_manager_->search_chat_participants(dialog_id.get_chat_id(), query, limit, filter, - std::move(promise)); - case DialogType::Channel: { - tl_object_ptr request_filter; - string additional_query; - int32 additional_limit = 0; - switch (filter.type) { - case DialogParticipantsFilter::Type::Contacts: - request_filter = td_api::make_object(); - break; - case DialogParticipantsFilter::Type::Administrators: - request_filter = td_api::make_object(); - break; - case DialogParticipantsFilter::Type::Members: - request_filter = td_api::make_object(query); - break; - case DialogParticipantsFilter::Type::Restricted: - request_filter = td_api::make_object(query); - break; - case DialogParticipantsFilter::Type::Banned: - request_filter = td_api::make_object(query); - break; - case DialogParticipantsFilter::Type::Mention: - request_filter = - td_api::make_object(query, filter.top_thread_message_id.get()); - break; - case DialogParticipantsFilter::Type::Bots: - request_filter = td_api::make_object(); - break; - default: - UNREACHABLE(); - } - switch (filter.type) { - case DialogParticipantsFilter::Type::Contacts: - case DialogParticipantsFilter::Type::Administrators: - case DialogParticipantsFilter::Type::Bots: - additional_query = query; - additional_limit = limit; - limit = 100; - break; - case DialogParticipantsFilter::Type::Members: - case DialogParticipantsFilter::Type::Restricted: - case DialogParticipantsFilter::Type::Banned: - case DialogParticipantsFilter::Type::Mention: - // query is passed to the server request - break; - default: - UNREACHABLE(); - } - - return td_->contacts_manager_->get_channel_participants(dialog_id.get_channel_id(), std::move(request_filter), - std::move(additional_query), 0, limit, additional_limit, - without_bot_info, std::move(promise)); - } - case DialogType::SecretChat: { - auto peer_user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); - promise.set_value( - search_private_chat_participants(td_->contacts_manager_->get_my_id(), peer_user_id, query, limit, filter)); - return; - } - case DialogType::None: - default: - UNREACHABLE(); - promise.set_error(Status::Error(500, "Wrong chat type")); - } -} - -vector MessagesManager::get_dialog_administrators(DialogId dialog_id, int left_tries, - Promise &&promise) { - LOG(INFO) << "Receive GetChatAdministrators request in " << dialog_id; - if (!have_dialog_force(dialog_id)) { - promise.set_error(Status::Error(3, "Chat not found")); - return {}; - } - - switch (dialog_id.get_type()) { - case DialogType::User: - case DialogType::SecretChat: - promise.set_value(Unit()); - break; - case DialogType::Chat: - case DialogType::Channel: - return td_->contacts_manager_->get_dialog_administrators(dialog_id, left_tries, std::move(promise)); - case DialogType::None: - default: - UNREACHABLE(); - promise.set_error(Status::Error(500, "Wrong chat type")); - } - return {}; -} - tl_object_ptr MessagesManager::get_channel_admin_log_events_filter( const tl_object_ptr &filters) { if (filters == nullptr) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index f61fcb15c..88a035a80 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -10,7 +10,6 @@ #include "td/telegram/ChannelId.h" #include "td/telegram/Dependencies.h" #include "td/telegram/DialogAction.h" -#include "td/telegram/DialogAdministrator.h" #include "td/telegram/DialogDate.h" #include "td/telegram/DialogDb.h" #include "td/telegram/DialogFilterId.h" @@ -478,25 +477,6 @@ class MessagesManager : public Actor { void unpin_all_dialog_messages(DialogId dialog_id, Promise &&promise); - void add_dialog_participant(DialogId dialog_id, UserId user_id, int32 forward_limit, Promise &&promise); - - void add_dialog_participants(DialogId dialog_id, const vector &user_ids, Promise &&promise); - - void set_dialog_participant_status(DialogId dialog_id, UserId user_id, - const tl_object_ptr &chat_member_status, - Promise &&promise); - - void ban_dialog_participant(DialogId dialog_id, UserId user_id, int32 banned_until_date, bool revoke_messages, - Promise &&promise); - - DialogParticipant get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, - Promise &&promise); - - void search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, DialogParticipantsFilter filter, - bool without_bot_info, Promise &&promise); - - vector get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise); - void get_dialog_info_full(DialogId dialog_id, Promise &&promise); int64 get_dialog_event_log(DialogId dialog_id, const string &query, int64 from_event_id, int32 limit, @@ -2535,9 +2515,6 @@ class MessagesManager : public Actor { DialogFolder *get_dialog_folder(FolderId folder_id); const DialogFolder *get_dialog_folder(FolderId folder_id) const; - DialogParticipants search_private_chat_participants(UserId my_user_id, UserId peer_user_id, const string &query, - int32 limit, DialogParticipantsFilter filter) const; - static unique_ptr *treap_find_message(unique_ptr *v, MessageId message_id); static const unique_ptr *treap_find_message(const unique_ptr *v, MessageId message_id); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 5d2d7429c..224758f11 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1946,7 +1946,7 @@ class GetChatMemberRequest : public RequestActor<> { DialogParticipant dialog_participant_; void do_run(Promise &&promise) override { - dialog_participant_ = td->messages_manager_->get_dialog_participant(dialog_id_, user_id_, random_id_, + dialog_participant_ = td->contacts_manager_->get_dialog_participant(dialog_id_, user_id_, random_id_, get_tries() < 3, std::move(promise)); } @@ -1970,7 +1970,7 @@ class GetChatAdministratorsRequest : public RequestActor<> { vector administrators_; void do_run(Promise &&promise) override { - administrators_ = td->messages_manager_->get_dialog_administrators(dialog_id_, get_tries(), std::move(promise)); + administrators_ = td->contacts_manager_->get_dialog_administrators(dialog_id_, get_tries(), std::move(promise)); } void do_send_result() override { @@ -6208,7 +6208,7 @@ void Td::on_request(uint64 id, const td_api::unpinAllChatMessages &request) { void Td::on_request(uint64 id, const td_api::joinChat &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - messages_manager_->add_dialog_participant(DialogId(request.chat_id_), contacts_manager_->get_my_id(), 0, + contacts_manager_->add_dialog_participant(DialogId(request.chat_id_), contacts_manager_->get_my_id(), 0, std::move(promise)); } @@ -6227,14 +6227,14 @@ void Td::on_request(uint64 id, const td_api::leaveChat &request) { td_api::make_object(status.get_rank(), status.is_anonymous(), false); } } - messages_manager_->set_dialog_participant_status(dialog_id, contacts_manager_->get_my_id(), std::move(new_status), + contacts_manager_->set_dialog_participant_status(dialog_id, contacts_manager_->get_my_id(), std::move(new_status), std::move(promise)); } void Td::on_request(uint64 id, const td_api::addChatMember &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - messages_manager_->add_dialog_participant(DialogId(request.chat_id_), UserId(request.user_id_), + contacts_manager_->add_dialog_participant(DialogId(request.chat_id_), UserId(request.user_id_), request.forward_limit_, std::move(promise)); } @@ -6245,19 +6245,19 @@ void Td::on_request(uint64 id, const td_api::addChatMembers &request) { for (auto &user_id : request.user_ids_) { user_ids.emplace_back(user_id); } - messages_manager_->add_dialog_participants(DialogId(request.chat_id_), user_ids, std::move(promise)); + contacts_manager_->add_dialog_participants(DialogId(request.chat_id_), user_ids, std::move(promise)); } void Td::on_request(uint64 id, td_api::setChatMemberStatus &request) { CREATE_OK_REQUEST_PROMISE(); - messages_manager_->set_dialog_participant_status(DialogId(request.chat_id_), UserId(request.user_id_), + contacts_manager_->set_dialog_participant_status(DialogId(request.chat_id_), UserId(request.user_id_), request.status_, std::move(promise)); } void Td::on_request(uint64 id, const td_api::banChatMember &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - messages_manager_->ban_dialog_participant(DialogId(request.chat_id_), UserId(request.user_id_), + contacts_manager_->ban_dialog_participant(DialogId(request.chat_id_), UserId(request.user_id_), request.banned_until_date_, request.revoke_messages_, std::move(promise)); } @@ -6298,7 +6298,7 @@ void Td::on_request(uint64 id, td_api::searchChatMembers &request) { promise.set_value(result.ok().get_chat_members_object(td)); } }); - messages_manager_->search_dialog_participants(DialogId(request.chat_id_), request.query_, request.limit_, + contacts_manager_->search_dialog_participants(DialogId(request.chat_id_), request.query_, request.limit_, get_dialog_participants_filter(request.filter_), false, std::move(query_promise)); } From 9e37c85af3c592b70858017d33396a46f73a1049 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 27 Jan 2021 02:57:59 +0300 Subject: [PATCH 086/232] Minor improvements. --- td/telegram/ContactsManager.h | 1 - td/telegram/DialogInviteLink.cpp | 1 + td/telegram/DialogInviteLink.h | 2 +- td/telegram/MessagesManager.cpp | 2 -- td/telegram/UpdatesManager.cpp | 2 +- td/telegram/UpdatesManager.h | 1 + tdutils/td/utils/translit.cpp | 1 - 7 files changed, 4 insertions(+), 6 deletions(-) diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 6092eaac4..6f8b376fa 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -37,7 +37,6 @@ #include "td/utils/common.h" #include "td/utils/Hints.h" -#include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index 3eeddaf94..085c09c82 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -9,6 +9,7 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" +#include "td/utils/logging.h" #include "td/utils/misc.h" namespace td { diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index 8eb8f1c6f..2ced9c838 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -39,7 +39,7 @@ class DialogInviteLink { public: DialogInviteLink() = default; - DialogInviteLink(tl_object_ptr exported_invite); + explicit DialogInviteLink(tl_object_ptr exported_invite); static bool is_valid_invite_link(Slice invite_link); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 810dc2150..80f266be8 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -69,12 +69,10 @@ #include "td/utils/Slice.h" #include "td/utils/Time.h" #include "td/utils/tl_helpers.h" -#include "td/utils/tl_storers.h" #include "td/utils/utf8.h" #include #include -#include #include #include #include diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index f9ed0e73d..e812b2bb3 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -50,7 +50,6 @@ #include "td/utils/algorithm.h" #include "td/utils/buffer.h" -#include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Random.h" @@ -58,6 +57,7 @@ #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" +#include #include namespace td { diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 810f37472..07b296dc0 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -21,6 +21,7 @@ #include "td/utils/common.h" #include "td/utils/logging.h" +#include "td/utils/Status.h" #include "td/utils/tl_storers.h" #include diff --git a/tdutils/td/utils/translit.cpp b/tdutils/td/utils/translit.cpp index 239fa7f5c..60ee4e78a 100644 --- a/tdutils/td/utils/translit.cpp +++ b/tdutils/td/utils/translit.cpp @@ -7,7 +7,6 @@ #include "td/utils/translit.h" #include "td/utils/algorithm.h" -#include "td/utils/format.h" #include "td/utils/misc.h" #include "td/utils/utf8.h" From f0355b42c47543129b0d86713236db96b57e7bbd Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 28 Jan 2021 22:12:55 +0300 Subject: [PATCH 087/232] Disable notifications for importe messages. --- td/telegram/MessagesManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 80f266be8..59f310ef2 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -27629,6 +27629,9 @@ bool MessagesManager::is_message_notification_disabled(const Dialog *d, const Me G()->shared_config().get_option_boolean("disable_sent_scheduled_message_notifications")) { return true; } + if (m->forward_info != nullptr && m->forward_info->is_imported) { + return true; + } switch (m->content->get_type()) { case MessageContentType::ChatDeleteHistory: From 312e3b740afbc9dbc8b8a539d4fb0d510b614779 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 28 Jan 2021 22:18:33 +0300 Subject: [PATCH 088/232] Update version to 1.7.1. --- CMakeLists.txt | 2 +- README.md | 2 +- build.html | 2 +- example/cpp/CMakeLists.txt | 2 +- example/uwp/extension.vsixmanifest | 2 +- td/telegram/Td.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7faa9a005..97e1b5883 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) -project(TDLib VERSION 1.7.0 LANGUAGES CXX C) +project(TDLib VERSION 1.7.1 LANGUAGES CXX C) if (NOT DEFINED CMAKE_MODULE_PATH) set(CMAKE_MODULE_PATH "") diff --git a/README.md b/README.md index ca683dc20..0acb25138 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ target_link_libraries(YourTarget PRIVATE Td::TdStatic) Or you could install `TDLib` and then reference it in your CMakeLists.txt like this: ``` -find_package(Td 1.7.0 REQUIRED) +find_package(Td 1.7.1 REQUIRED) target_link_libraries(YourTarget PRIVATE Td::TdStatic) ``` See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt). diff --git a/build.html b/build.html index cfd2b192e..2cb47895f 100644 --- a/build.html +++ b/build.html @@ -715,7 +715,7 @@ function onOptionsChanged() { commands.push('git clone https://github.com/tdlib/td.git'); commands.push('cd td'); - commands.push('git checkout v1.7.0'); + // commands.push('git checkout v1.7.0'); if (use_vcpkg) { commands.push('git clone https://github.com/Microsoft/vcpkg.git'); diff --git a/example/cpp/CMakeLists.txt b/example/cpp/CMakeLists.txt index 2d1e0cde0..ebf3d1120 100644 --- a/example/cpp/CMakeLists.txt +++ b/example/cpp/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(TdExample VERSION 1.0 LANGUAGES CXX) -find_package(Td 1.7.0 REQUIRED) +find_package(Td 1.7.1 REQUIRED) add_executable(tdjson_example tdjson_example.cpp) target_link_libraries(tdjson_example PRIVATE Td::TdJson) diff --git a/example/uwp/extension.vsixmanifest b/example/uwp/extension.vsixmanifest index 62a2374f5..435f183ef 100644 --- a/example/uwp/extension.vsixmanifest +++ b/example/uwp/extension.vsixmanifest @@ -1,6 +1,6 @@ - + TDLib for Universal Windows Platform TDLib is a library for building Telegram clients https://core.telegram.org/tdlib diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 4d3561727..d46ca943f 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -235,7 +235,7 @@ class Td final : public NetQueryCallback { static td_api::object_ptr static_request(td_api::object_ptr function); private: - static constexpr const char *TDLIB_VERSION = "1.7.0"; + static constexpr const char *TDLIB_VERSION = "1.7.1"; static constexpr int64 ONLINE_ALARM_ID = 0; static constexpr int64 PING_SERVER_ALARM_ID = -1; static constexpr int32 PING_SERVER_TIMEOUT = 300; From be815519f12f4ad28e2d81e28820cbdad1201e26 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 29 Jan 2021 20:16:11 +0300 Subject: [PATCH 089/232] Improve documentation. --- td/generate/scheme/td_api.tl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 840c96d9a..acd064ec4 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -587,10 +587,10 @@ basicGroup id:int32 member_count:int32 status:ChatMemberStatus is_active:Bool up //@description Contains full information about a basic group //@photo Chat photo; may be null -//@param_description Group description +//@param_description Group description. Updated only after the basic group is opened //@creator_user_id User identifier of the creator of the group; 0 if unknown //@members Group members -//@invite_link Permanent invite link for this group; only for the group creator +//@invite_link Permanent invite link for this group; may be null. For chat administrators with can_invite_users right only. Updated only after the basic group is opened basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 members:vector invite_link:chatInviteLink = BasicGroupFullInfo; @@ -629,7 +629,7 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co //@is_all_history_available True, if new chat members will have access to old messages. In public or discussion groups and both public and private channels, old messages are always available, so this option affects only private supergroups without a linked chat. The value of this field is only available for chat administrators //@sticker_set_id Identifier of the supergroup sticker set; 0 if none //@location Location to which the supergroup is connected; may be null -//@invite_link Permanent invite link for this chat; for chat administrators with can_invite_users right only +//@invite_link Permanent invite link for this chat; may be null. For chat administrators with can_invite_users right only //@upgraded_from_basic_group_id Identifier of the basic group from which supergroup was upgraded; 0 if none //@upgraded_from_max_message_id Identifier of the last message in the basic group from which supergroup was upgraded; 0 if none supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_get_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:chatInviteLink upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo; From a6a6237a3f6937e9240937dd2a6177c84c5c36e9 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 30 Jan 2021 23:52:16 +0300 Subject: [PATCH 090/232] Split need_group_call_participants into two functions. --- td/telegram/GroupCallManager.cpp | 26 ++++++++++++++------------ td/telegram/GroupCallManager.h | 2 ++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 8243d8b5c..16586f373 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -747,7 +747,7 @@ void GroupCallManager::get_group_call(GroupCallId group_call_id, void GroupCallManager::on_update_group_call_rights(InputGroupCallId input_group_call_id) { auto group_call = get_group_call(input_group_call_id); - if (need_group_call_participants(input_group_call_id)) { + if (need_group_call_participants(input_group_call_id, group_call)) { CHECK(group_call != nullptr && group_call->is_inited); try_load_group_call_administrators(input_group_call_id, group_call->dialog_id); @@ -846,7 +846,11 @@ void GroupCallManager::finish_check_group_call_is_joined(InputGroupCallId input_ } bool GroupCallManager::need_group_call_participants(InputGroupCallId input_group_call_id) const { - auto *group_call = get_group_call(input_group_call_id); + return need_group_call_participants(input_group_call_id, get_group_call(input_group_call_id)); +} + +bool GroupCallManager::need_group_call_participants(InputGroupCallId input_group_call_id, + const GroupCall *group_call) const { if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) { return false; } @@ -1505,11 +1509,10 @@ void GroupCallManager::finish_load_group_call_administrators(InputGroupCallId in return; } - if (!need_group_call_participants(input_group_call_id)) { + auto *group_call = get_group_call(input_group_call_id); + if (!need_group_call_participants(input_group_call_id, group_call)) { return; } - - auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr); if (!group_call->dialog_id.is_valid() || !can_manage_group_calls(group_call->dialog_id).is_ok()) { return; @@ -1810,10 +1813,10 @@ void GroupCallManager::load_group_call_participants(GroupCallId group_call_id, i TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); - if (!need_group_call_participants(input_group_call_id)) { + auto *group_call = get_group_call(input_group_call_id); + if (!need_group_call_participants(input_group_call_id, group_call)) { return promise.set_error(Status::Error(400, "Can't load group call participants")); } - auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); if (group_call->loaded_all_participants) { return promise.set_value(Unit()); @@ -1987,7 +1990,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptraudio_source; *group_call = std::move(call); - if (need_group_call_participants(input_group_call_id)) { + if (need_group_call_participants(input_group_call_id, group_call)) { // init version group_call->version = call.version; if (process_pending_group_call_participant_updates(input_group_call_id)) { @@ -2028,7 +2031,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrparticipant_count = call.participant_count; need_update = true; } - if (need_group_call_participants(input_group_call_id) && !join_params.empty()) { + if (need_group_call_participants(input_group_call_id, group_call) && !join_params.empty()) { LOG(INFO) << "Init " << call.group_call_id << " version to " << call.version; if (group_call->can_self_unmute != call.can_self_unmute) { group_call->can_self_unmute = call.can_self_unmute; @@ -2058,11 +2061,10 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptris_inited); if (group_call->version == -1) { return; diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index aa7602cfa..462f46427 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -137,6 +137,8 @@ class GroupCallManager : public Actor { bool need_group_call_participants(InputGroupCallId input_group_call_id) const; + bool need_group_call_participants(InputGroupCallId input_group_call_id, const GroupCall *group_call) const; + bool process_pending_group_call_participant_updates(InputGroupCallId input_group_call_id); void sync_group_call_participants(InputGroupCallId input_group_call_id); From 41f6003b092570064a71ff802e16f68bc0611cfd Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 31 Jan 2021 20:43:57 +0300 Subject: [PATCH 091/232] Fix supergroupMembersFilterMention with non-zero thread. --- td/telegram/DialogParticipant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td/telegram/DialogParticipant.cpp b/td/telegram/DialogParticipant.cpp index d4700b0d2..65ff4c06a 100644 --- a/td/telegram/DialogParticipant.cpp +++ b/td/telegram/DialogParticipant.cpp @@ -733,7 +733,7 @@ ChannelParticipantsFilter::get_input_channel_participants_filter() const { if (!query.empty()) { flags |= telegram_api::channelParticipantsMentions::Q_MASK; } - if (!top_thread_message_id.is_valid()) { + if (top_thread_message_id.is_valid()) { flags |= telegram_api::channelParticipantsMentions::TOP_MSG_ID_MASK; } return make_tl_object( From a29f4b43d40508e3b384c797e55f3f499ac564e6 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Feb 2021 02:43:23 +0300 Subject: [PATCH 092/232] Load even known group call participant after receiving typing from them. --- td/telegram/GroupCallManager.cpp | 14 +++++++++++++- td/telegram/GroupCallManager.h | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 16586f373..04f2aa958 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -967,6 +967,16 @@ GroupCallManager::GroupCallParticipants *GroupCallManager::add_group_call_partic return participants.get(); } +const GroupCallParticipant *GroupCallManager::get_group_call_participant( + const GroupCallParticipants *group_call_participants, UserId user_id) { + for (auto &group_call_participant : group_call_participants->participants) { + if (group_call_participant.user_id == user_id) { + return &group_call_participant; + } + } + return nullptr; +} + void GroupCallManager::on_update_group_call_participants( InputGroupCallId input_group_call_id, vector> &&participants, int32 version) { @@ -2114,7 +2124,9 @@ void GroupCallManager::on_user_speaking_in_group_call(GroupCallId group_call_id, return; } - if (!td_->contacts_manager_->have_user_force(user_id)) { + if (!td_->contacts_manager_->have_user_force(user_id) || + (!recursive && need_group_call_participants(input_group_call_id, group_call) && + get_group_call_participant(add_group_call_participants(input_group_call_id), user_id) == nullptr)) { if (recursive) { LOG(ERROR) << "Failed to find speaking " << user_id << " from " << input_group_call_id; } else { diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 462f46427..c4c885838 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -167,6 +167,9 @@ class GroupCallManager : public Actor { GroupCallParticipants *add_group_call_participants(InputGroupCallId input_group_call_id); + const GroupCallParticipant *get_group_call_participant(const GroupCallParticipants *group_call_participants, + UserId user_id); + void on_group_call_left(InputGroupCallId input_group_call_id, int32 audio_source, bool need_rejoin); void on_group_call_left_impl(GroupCall *group_call, bool need_rejoin); From c6466caee2c5be59d357a36f44b977a88fc7435f Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Feb 2021 13:06:38 +0300 Subject: [PATCH 093/232] Add logging on long pts gap filling. --- td/telegram/UpdatesManager.cpp | 18 +++++++++++++----- td/telegram/UpdatesManager.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index e812b2bb3..1dbd0d80e 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1905,13 +1905,12 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr if (old_pts + accumulated_pts_count_ < accumulated_pts_) { set_pts_gap_timeout(MAX_UNFILLED_GAP_TIME); + last_pts_gap_time_ = Time::now(); return; } CHECK(old_pts + accumulated_pts_count_ == accumulated_pts_); - if (!pending_pts_updates_.empty()) { - process_pending_pts_updates(); - } + process_pending_pts_updates(); } void UpdatesManager::postpone_pts_update(tl_object_ptr &&update, int32 pts, int32 pts_count, @@ -1972,8 +1971,17 @@ void UpdatesManager::process_pending_pts_updates() { update.second.promise.set_value(Unit()); } - set_pts(accumulated_pts_, "process pending updates") - .set_value(Unit()); // TODO can't set until get messages really stored on persistent storage + if (last_pts_gap_time_ != 0) { + auto diff = Time::now() - last_pts_gap_time_; + last_pts_gap_time_ = 0; + if (diff > 0.1) { + VLOG(get_difference) << "Gap in pts from " << accumulated_pts_ - accumulated_pts_count_ << " to " + << accumulated_pts_ << " has been filled in " << diff << " seconds"; + } + } + + set_pts(accumulated_pts_, "postpone_pending_pts_update") + .set_value(Unit()); // TODO can't set until updates are stored on persistent storage drop_pending_pts_updates(); } diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 07b296dc0..945e36d53 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -171,6 +171,7 @@ class UpdatesManager : public Actor { int32 accumulated_pts_count_ = 0; int32 accumulated_pts_ = -1; double last_pts_jump_warning_time_ = 0; + double last_pts_gap_time_ = 0; std::multimap pending_pts_updates_; std::multimap postponed_pts_updates_; From ad0c5e6783fe30178fe137e099dd5e80e45cff3a Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Feb 2021 13:41:20 +0300 Subject: [PATCH 094/232] Minor improvements. --- td/telegram/ContactsManager.cpp | 4 +++- td/telegram/MessagesManager.cpp | 27 ++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index d2fdaddba..06ea3e580 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -7317,7 +7317,9 @@ void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, bo auto my_id = get_my_id(); if (c->status.is_left()) { if (user_id == my_id) { - td_->messages_manager_->delete_dialog(DialogId(chat_id)); + if (revoke_messages) { + td_->messages_manager_->delete_dialog(DialogId(chat_id)); + } return promise.set_value(Unit()); } else { return promise.set_error(Status::Error(3, "Not in the chat")); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 59f310ef2..fadd3b886 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -10801,10 +10801,10 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia MessageId max_message_id = d->last_database_message_id.is_valid() ? d->last_database_message_id : d->last_new_message_id; if (max_message_id.is_valid()) { - read_history_inbox(d->dialog_id, max_message_id, -1, "delete_all_dialog_messages"); + read_history_inbox(d->dialog_id, max_message_id, -1, "delete_all_dialog_messages 1"); } if (d->server_unread_count != 0 || d->local_unread_count != 0) { - set_dialog_last_read_inbox_message_id(d, MessageId::min(), 0, 0, true, "delete_all_dialog_messages"); + set_dialog_last_read_inbox_message_id(d, MessageId::min(), 0, 0, true, "delete_all_dialog_messages 2"); } } @@ -10830,7 +10830,7 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia vector deleted_message_ids; do_delete_all_dialog_messages(d, d->messages, is_permanently_deleted, deleted_message_ids); - delete_all_dialog_messages_from_database(d, MessageId::max(), "delete_all_dialog_messages"); + delete_all_dialog_messages_from_database(d, MessageId::max(), "delete_all_dialog_messages 3"); if (is_permanently_deleted) { for (auto id : deleted_message_ids) { d->deleted_message_ids.insert(MessageId{id}); @@ -10841,9 +10841,10 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia set_dialog_reply_markup(d, MessageId()); } - set_dialog_first_database_message_id(d, MessageId(), "delete_all_dialog_messages"); - set_dialog_last_database_message_id(d, MessageId(), "delete_all_dialog_messages"); - set_dialog_last_clear_history_date(d, last_message_date, last_clear_history_message_id, "delete_all_dialog_messages"); + set_dialog_first_database_message_id(d, MessageId(), "delete_all_dialog_messages 4"); + set_dialog_last_database_message_id(d, MessageId(), "delete_all_dialog_messages 5"); + set_dialog_last_clear_history_date(d, last_message_date, last_clear_history_message_id, + "delete_all_dialog_messages 6"); d->last_read_all_mentions_message_id = MessageId(); // it is not needed anymore d->message_notification_group.max_removed_notification_id = NotificationId(); // it is not needed anymore d->message_notification_group.max_removed_message_id = MessageId(); // it is not needed anymore @@ -10853,16 +10854,16 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia d->notification_id_to_message_id.clear(); if (has_last_message_id) { - set_dialog_last_message_id(d, MessageId(), "delete_all_dialog_messages"); - send_update_chat_last_message(d, "delete_all_dialog_messages"); + set_dialog_last_message_id(d, MessageId(), "delete_all_dialog_messages 7"); + send_update_chat_last_message(d, "delete_all_dialog_messages 8"); } if (remove_from_dialog_list) { - set_dialog_order(d, DEFAULT_ORDER, true, false, "delete_all_dialog_messages 1"); + set_dialog_order(d, DEFAULT_ORDER, true, false, "delete_all_dialog_messages 9"); } else { - update_dialog_pos(d, "delete_all_dialog_messages 2"); + update_dialog_pos(d, "delete_all_dialog_messages 10"); } - on_dialog_updated(d->dialog_id, "delete_all_dialog_messages"); + on_dialog_updated(d->dialog_id, "delete_all_dialog_messages 11"); send_update_delete_messages(d->dialog_id, std::move(deleted_message_ids), is_permanently_deleted, false); } @@ -13625,6 +13626,10 @@ void MessagesManager::set_dialog_last_clear_history_date(Dialog *d, int32 date, const char *source, bool is_loaded_from_database) { CHECK(!last_clear_history_message_id.is_scheduled()); + if (d->last_clear_history_message_id == last_clear_history_message_id && d->last_clear_history_date == date) { + return; + } + LOG(INFO) << "Set " << d->dialog_id << " last clear history date to " << date << " of " << last_clear_history_message_id << " from " << source; if (d->last_clear_history_message_id.is_valid()) { From 90b34e7d0cd72f61b410b69bc6ee25b2b0f82103 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Feb 2021 14:51:38 +0300 Subject: [PATCH 095/232] Remove authorization loss logic from DcAuthManager, because we can be logged before entering password and before authorization. --- td/telegram/net/DcAuthManager.cpp | 20 ++++---------------- td/telegram/net/DcAuthManager.h | 1 - td/telegram/net/Session.cpp | 2 ++ 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/td/telegram/net/DcAuthManager.cpp b/td/telegram/net/DcAuthManager.cpp index 0e9f83511..41e3f3291 100644 --- a/td/telegram/net/DcAuthManager.cpp +++ b/td/telegram/net/DcAuthManager.cpp @@ -8,7 +8,6 @@ #include "td/actor/actor.h" -#include "td/telegram/ConfigShared.h" #include "td/telegram/Global.h" #include "td/telegram/net/AuthDataShared.h" #include "td/telegram/net/NetQuery.h" @@ -66,9 +65,7 @@ void DcAuthManager::add_dc(std::shared_ptr auth_data) { info.shared_auth_data = std::move(auth_data); auto state_was_auth = info.shared_auth_data->get_auth_key_state(); info.auth_key_state = state_was_auth.first; - VLOG(dc) << "Add " << info.dc_id << " with auth key state " << info.auth_key_state - << " and was_auth = " << state_was_auth.second; - was_auth_ |= state_was_auth.second; + VLOG(dc) << "Add " << info.dc_id << " with auth key state " << info.auth_key_state; if (!main_dc_id_.is_exact()) { main_dc_id_ = info.dc_id; VLOG(dc) << "Set main DcId to " << main_dc_id_; @@ -101,10 +98,8 @@ void DcAuthManager::update_auth_key_state() { int32 dc_id = narrow_cast(get_link_token()); auto &dc = get_dc(dc_id); auto state_was_auth = dc.shared_auth_data->get_auth_key_state(); - VLOG(dc) << "Update " << dc_id << " auth key state from " << dc.auth_key_state << " to " << state_was_auth.first - << " with was_auth = " << state_was_auth.second; + VLOG(dc) << "Update " << dc_id << " auth key state from " << dc.auth_key_state << " to " << state_was_auth.first; dc.auth_key_state = state_was_auth.first; - was_auth_ |= state_was_auth.second; loop(); } @@ -239,15 +234,8 @@ void DcAuthManager::loop() { } auto main_dc = find_dc(main_dc_id_.get_raw_id()); if (!main_dc || main_dc->auth_key_state != AuthKeyState::OK) { - VLOG(dc) << "Main is " << main_dc_id_ << ", main auth key state is " - << (main_dc ? main_dc->auth_key_state : AuthKeyState::Empty) << ", was_auth = " << was_auth_; - if (was_auth_) { - G()->shared_config().set_option_boolean("auth", false); - destroy_loop(); - } - VLOG(dc) << "Skip loop because auth state of main DcId " << main_dc_id_.get_raw_id() << " is " - << (main_dc != nullptr ? (PSTRING() << main_dc->auth_key_state) : "unknown"); - + VLOG(dc) << "Skip loop, because main DC is " << main_dc_id_ << ", main auth key state is " + << (main_dc != nullptr ? main_dc->auth_key_state : AuthKeyState::Empty); return; } for (auto &dc : dcs_) { diff --git a/td/telegram/net/DcAuthManager.h b/td/telegram/net/DcAuthManager.h index 2adb14f45..9d420dd55 100644 --- a/td/telegram/net/DcAuthManager.h +++ b/td/telegram/net/DcAuthManager.h @@ -47,7 +47,6 @@ class DcAuthManager : public NetQueryCallback { ActorShared<> parent_; std::vector dcs_; - bool was_auth_{false}; DcId main_dc_id_; bool close_flag_{false}; Promise<> destroy_promise_; diff --git a/td/telegram/net/Session.cpp b/td/telegram/net/Session.cpp index 95287a389..1c62b7596 100644 --- a/td/telegram/net/Session.cpp +++ b/td/telegram/net/Session.cpp @@ -8,6 +8,7 @@ #include "td/telegram/telegram_api.h" +#include "td/telegram/ConfigShared.h" #include "td/telegram/DhCache.h" #include "td/telegram/Global.h" #include "td/telegram/net/DcAuthManager.h" @@ -778,6 +779,7 @@ void Session::on_message_result_error(uint64 id, int error_code, BufferSlice mes LOG(WARNING) << "Lost authorization due to " << tag("msg", message.as_slice()); } auth_data_.set_auth_flag(false); + G()->shared_config().set_option_boolean("auth", false); shared_auth_data_->set_auth_key(auth_data_.get_main_auth_key()); on_session_failed(Status::OK()); } From e3cb608293162f780d5b120e200496d371ef0ff5 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Feb 2021 15:07:10 +0300 Subject: [PATCH 096/232] Remove was_auth flag. --- td/mtproto/AuthKey.h | 10 +--------- td/telegram/ConfigManager.cpp | 6 ++---- td/telegram/net/AuthDataShared.cpp | 12 +++++------- td/telegram/net/AuthDataShared.h | 2 +- td/telegram/net/DcAuthManager.cpp | 8 +++----- td/telegram/net/SessionProxy.cpp | 4 ++-- 6 files changed, 14 insertions(+), 28 deletions(-) diff --git a/td/mtproto/AuthKey.h b/td/mtproto/AuthKey.h index 5c3959605..20eb414e2 100644 --- a/td/mtproto/AuthKey.h +++ b/td/mtproto/AuthKey.h @@ -34,11 +34,7 @@ class AuthKey { bool auth_flag() const { return auth_flag_; } - bool was_auth_flag() const { - return was_auth_flag_; - } void set_auth_flag(bool new_auth_flag) { - was_auth_flag_ |= new_auth_flag; auth_flag_ = new_auth_flag; } @@ -67,15 +63,13 @@ class AuthKey { } static constexpr int32 AUTH_FLAG = 1; - static constexpr int32 WAS_AUTH_FLAG = 2; static constexpr int32 HAS_CREATED_AT = 4; template void store(StorerT &storer) const { storer.store_binary(auth_key_id_); bool has_created_at = created_at_ != 0; - storer.store_binary(static_cast((auth_flag_ ? AUTH_FLAG : 0) | (was_auth_flag_ ? WAS_AUTH_FLAG : 0) | - (has_created_at ? HAS_CREATED_AT : 0))); + storer.store_binary(static_cast((auth_flag_ ? AUTH_FLAG : 0) | (has_created_at ? HAS_CREATED_AT : 0))); storer.store_string(auth_key_); if (has_created_at) { storer.store_binary(created_at_); @@ -87,7 +81,6 @@ class AuthKey { auth_key_id_ = parser.fetch_long(); auto flags = parser.fetch_int(); auth_flag_ = (flags & AUTH_FLAG) != 0; - was_auth_flag_ = (flags & WAS_AUTH_FLAG) != 0 || auth_flag_; auth_key_ = parser.template fetch_string(); if ((flags & HAS_CREATED_AT) != 0) { created_at_ = parser.fetch_double(); @@ -100,7 +93,6 @@ class AuthKey { uint64 auth_key_id_{0}; string auth_key_; bool auth_flag_{false}; - bool was_auth_flag_{false}; bool need_header_{true}; double expires_at_{0}; double created_at_{0}; diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index c5be27e4c..6dcc93712 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -434,10 +434,8 @@ ActorOwn<> get_full_config(DcOption option, Promise promise, ActorSh } return res; } - std::pair get_auth_key_state() override { - auto auth_key = get_auth_key(); - AuthKeyState state = AuthDataShared::get_auth_key_state(auth_key); - return std::make_pair(state, auth_key.was_auth_flag()); + AuthKeyState get_auth_key_state() override { + return AuthDataShared::get_auth_key_state(get_auth_key()); } void set_auth_key(const mtproto::AuthKey &auth_key) override { G()->td_db()->get_binlog_pmc()->set(auth_key_key(), serialize(auth_key)); diff --git a/td/telegram/net/AuthDataShared.cpp b/td/telegram/net/AuthDataShared.cpp index 35f545ed2..aaf628cae 100644 --- a/td/telegram/net/AuthDataShared.cpp +++ b/td/telegram/net/AuthDataShared.cpp @@ -41,12 +41,9 @@ class AuthDataSharedImpl : public AuthDataShared { } return res; } - using AuthDataShared::get_auth_key_state; - std::pair get_auth_key_state() override { - // TODO (perf): - auto auth_key = get_auth_key(); - AuthKeyState state = get_auth_key_state(auth_key); - return std::make_pair(state, auth_key.was_auth_flag()); + + AuthKeyState get_auth_key_state() override { + return AuthDataShared::get_auth_key_state(get_auth_key()); } void set_auth_key(const mtproto::AuthKey &auth_key) override { @@ -106,7 +103,8 @@ class AuthDataSharedImpl : public AuthDataShared { } void log_auth_key(const mtproto::AuthKey &auth_key) { - LOG(WARNING) << dc_id_ << " " << tag("auth_key_id", auth_key.id()) << tag("state", get_auth_key_state(auth_key)) + LOG(WARNING) << dc_id_ << " " << tag("auth_key_id", auth_key.id()) + << tag("state", AuthDataShared::get_auth_key_state(auth_key)) << tag("created_at", auth_key.created_at()); } }; diff --git a/td/telegram/net/AuthDataShared.h b/td/telegram/net/AuthDataShared.h index 9266f4ead..1fdb2d42e 100644 --- a/td/telegram/net/AuthDataShared.h +++ b/td/telegram/net/AuthDataShared.h @@ -51,7 +51,7 @@ class AuthDataShared { virtual DcId dc_id() const = 0; virtual const std::shared_ptr &public_rsa_key() = 0; virtual mtproto::AuthKey get_auth_key() = 0; - virtual std::pair get_auth_key_state() = 0; + virtual AuthKeyState get_auth_key_state() = 0; virtual void set_auth_key(const mtproto::AuthKey &auth_key) = 0; virtual void update_server_time_difference(double diff) = 0; virtual double get_server_time_difference() = 0; diff --git a/td/telegram/net/DcAuthManager.cpp b/td/telegram/net/DcAuthManager.cpp index 41e3f3291..4cdb0d9ac 100644 --- a/td/telegram/net/DcAuthManager.cpp +++ b/td/telegram/net/DcAuthManager.cpp @@ -63,8 +63,7 @@ void DcAuthManager::add_dc(std::shared_ptr auth_data) { info.dc_id = auth_data->dc_id(); CHECK(info.dc_id.is_exact()); info.shared_auth_data = std::move(auth_data); - auto state_was_auth = info.shared_auth_data->get_auth_key_state(); - info.auth_key_state = state_was_auth.first; + info.auth_key_state = info.shared_auth_data->get_auth_key_state(); VLOG(dc) << "Add " << info.dc_id << " with auth key state " << info.auth_key_state; if (!main_dc_id_.is_exact()) { main_dc_id_ = info.dc_id; @@ -97,9 +96,8 @@ DcAuthManager::DcInfo *DcAuthManager::find_dc(int32 dc_id) { void DcAuthManager::update_auth_key_state() { int32 dc_id = narrow_cast(get_link_token()); auto &dc = get_dc(dc_id); - auto state_was_auth = dc.shared_auth_data->get_auth_key_state(); - VLOG(dc) << "Update " << dc_id << " auth key state from " << dc.auth_key_state << " to " << state_was_auth.first; - dc.auth_key_state = state_was_auth.first; + dc.auth_key_state = dc.shared_auth_data->get_auth_key_state(); + VLOG(dc) << "Update " << dc_id << " auth key state from " << dc.auth_key_state << " to " << dc.auth_key_state; loop(); } diff --git a/td/telegram/net/SessionProxy.cpp b/td/telegram/net/SessionProxy.cpp index 2be3e9e46..44baab333 100644 --- a/td/telegram/net/SessionProxy.cpp +++ b/td/telegram/net/SessionProxy.cpp @@ -102,7 +102,7 @@ void SessionProxy::start_up() { private: ActorShared session_proxy_; }; - auth_key_state_ = auth_data_->get_auth_key_state().first; + auth_key_state_ = auth_data_->get_auth_key_state(); auth_data_->add_auth_key_listener(make_unique(actor_shared(this))); open_session(); } @@ -212,7 +212,7 @@ void SessionProxy::open_session(bool force) { void SessionProxy::update_auth_key_state() { auto old_auth_key_state = auth_key_state_; - auth_key_state_ = auth_data_->get_auth_key_state().first; + auth_key_state_ = auth_data_->get_auth_key_state(); if (auth_key_state_ != old_auth_key_state && old_auth_key_state == AuthKeyState::OK) { close_session(); } From dfdc08162eb2b0e1e3b589fcc8ddd5099046f5d4 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Feb 2021 22:29:45 +0300 Subject: [PATCH 097/232] Fix updating of volume_level by min-updates. --- td/generate/scheme/telegram_api.tl | 2 +- td/generate/scheme/telegram_api.tlo | Bin 227064 -> 227148 bytes td/telegram/GroupCallManager.cpp | 6 ++++++ td/telegram/GroupCallParticipant.cpp | 2 ++ td/telegram/GroupCallParticipant.h | 2 ++ 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 595265fb7..1e645fe8c 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -1183,7 +1183,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2 inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; +groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; phone.groupCall#66ab0bfc call:GroupCall participants:Vector participants_next_offset:string users:Vector = phone.GroupCall; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index c3ca7a83b92086f6717c59ef1cc1675486c8dd8e..450582c74b0a77d74a08f9b491af26f24e219f58 100644 GIT binary patch delta 71 zcmV-N0J#77?G4QC4X~WJ0S=b|TLV;+ZD$XXxuBO24FU|47<3qupt&#*4|Z>Cb!}x| dVtHR-WNm3~mvG(!AcN4khtRnJx6ru)D7_{^9x(s_ delta 47 zcmV+~0MP%;?hW|u4X~WJ0SuSn+yO0<%DC2(#JMn+a9{%vm#|3zEQ4UWhhVw^w_v&g F?7T~)7ijflags_ & telegram_api::groupCallParticipant::VOLUME_BY_ADMIN_MASK) == 0; } if (!participant->left_) { joined_date = participant->date_; @@ -38,6 +39,7 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptrjust_joined_; + is_min = (participant->flags_ & telegram_api::groupCallParticipant::MIN_MASK) != 0; } bool GroupCallParticipant::is_versioned_update(const tl_object_ptr &participant) { diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index abaff79e5..842eb5d05 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -23,6 +23,7 @@ struct GroupCallParticipant { int32 joined_date = 0; int32 active_date = 0; int32 volume_level = 10000; + bool is_volume_level_local = false; bool is_muted = false; bool can_self_unmute = false; bool is_muted_only_for_self = false; @@ -32,6 +33,7 @@ struct GroupCallParticipant { bool can_be_muted_only_for_self = false; bool can_be_unmuted_only_for_self = false; + bool is_min = false; bool is_just_joined = false; bool is_speaking = false; int32 local_active_date = 0; From 1f1d1b442b0757e535302cb1484f9124cfea1901 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Feb 2021 22:40:42 +0300 Subject: [PATCH 098/232] Fix updating of is_muted_only_for_self by min-updates. --- td/telegram/GroupCallManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 697687198..6a922a35c 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1365,6 +1365,9 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou participant.order = real_order; } participant.is_just_joined = false; + if (participant.is_min) { + participant.is_muted_only_for_self = old_participant.is_muted_only_for_self; + } update_group_call_participant_can_be_muted(can_manage, participants, participant); if (old_participant.is_volume_level_local && !participant.is_volume_level_local) { participant.is_volume_level_local = true; From 658db2defded5553ce2ca482cf71d010db29ec9a Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Feb 2021 00:40:40 +0300 Subject: [PATCH 099/232] Update volume_level locally immediately. --- td/telegram/GroupCallManager.cpp | 57 ++++++++++++++++++++++++++-- td/telegram/GroupCallManager.h | 9 ++++- td/telegram/GroupCallParticipant.cpp | 8 +++- td/telegram/GroupCallParticipant.h | 5 +++ td/telegram/cli.cpp | 2 +- 5 files changed, 73 insertions(+), 8 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 6a922a35c..9e74fa4e3 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -967,8 +967,8 @@ GroupCallManager::GroupCallParticipants *GroupCallManager::add_group_call_partic return participants.get(); } -const GroupCallParticipant *GroupCallManager::get_group_call_participant( - const GroupCallParticipants *group_call_participants, UserId user_id) { +GroupCallParticipant *GroupCallManager::get_group_call_participant(GroupCallParticipants *group_call_participants, + UserId user_id) { for (auto &group_call_participant : group_call_participants->participants) { if (group_call_participant.user_id == user_id) { return &group_call_participant; @@ -1373,6 +1373,8 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou participant.is_volume_level_local = true; participant.volume_level = old_participant.volume_level; } + participant.pending_volume_level = old_participant.pending_volume_level; + participant.pending_volume_level_generation = old_participant.pending_volume_level_generation; participant.is_min = false; LOG(INFO) << "Edit " << old_participant << " to " << participant; @@ -1821,10 +1823,59 @@ void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group return promise.set_error(Status::Error(400, "Can't change self volume level")); } - td_->create_handler(std::move(promise)) + auto participant = get_group_call_participant(add_group_call_participants(input_group_call_id), user_id); + if (participant == nullptr) { + return promise.set_error(Status::Error(400, "Can't find group call participant")); + } + + if (participant->get_volume_level() == volume_level) { + return promise.set_value(Unit()); + } + + participant->pending_volume_level = volume_level; + participant->pending_volume_level_generation = ++set_volume_level_generation_; + send_update_group_call_participant(input_group_call_id, *participant); + + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, user_id, + generation = participant->pending_volume_level_generation, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &GroupCallManager::on_set_group_call_participant_volume_level, input_group_call_id, + user_id, generation, std::move(promise)); + } + }); + td_->create_handler(std::move(query_promise)) ->send(input_group_call_id, user_id, false, volume_level); } +void GroupCallManager::on_set_group_call_participant_volume_level(InputGroupCallId input_group_call_id, UserId user_id, + uint64 generation, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_value(Unit()); + } + + auto *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + return promise.set_value(Unit()); + } + + auto participant = get_group_call_participant(add_group_call_participants(input_group_call_id), user_id); + if (participant == nullptr || participant->pending_volume_level_generation != generation) { + return promise.set_value(Unit()); + } + + if (participant->volume_level != participant->pending_volume_level || !participant->is_volume_level_local) { + LOG(ERROR) << "Failed to set volume level of " << user_id << " in " << input_group_call_id; + participant->pending_volume_level = 0; + send_update_group_call_participant(input_group_call_id, *participant); + } else { + participant->pending_volume_level = 0; + } + promise.set_value(Unit()); +} + void GroupCallManager::load_group_call_participants(GroupCallId group_call_id, int32 limit, Promise &&promise) { if (limit <= 0) { return promise.set_error(Status::Error(400, "Parameter limit must be positive")); diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index c4c885838..c53df445a 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -167,8 +167,11 @@ class GroupCallManager : public Actor { GroupCallParticipants *add_group_call_participants(InputGroupCallId input_group_call_id); - const GroupCallParticipant *get_group_call_participant(const GroupCallParticipants *group_call_participants, - UserId user_id); + static GroupCallParticipant *get_group_call_participant(GroupCallParticipants *group_call_participants, + UserId user_id); + + void on_set_group_call_participant_volume_level(InputGroupCallId input_group_call_id, UserId user_id, + uint64 generation, Promise &&promise); void on_group_call_left(InputGroupCallId input_group_call_id, int32 audio_source, bool need_rejoin); @@ -234,6 +237,8 @@ class GroupCallManager : public Actor { std::unordered_map, InputGroupCallIdHash> pending_join_requests_; uint64 join_group_request_generation_ = 0; + uint64 set_volume_level_generation_ = 0; + MultiTimeout check_group_call_is_joined_timeout_{"CheckGroupCallIsJoinedTimeout"}; MultiTimeout pending_send_speaking_action_timeout_{"PendingSendSpeakingActionTimeout"}; MultiTimeout recent_speaker_update_timeout_{"RecentSpeakerUpdateTimeout"}; diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index cb5e6d656..1ee1fb98e 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -46,6 +46,10 @@ bool GroupCallParticipant::is_versioned_update(const tl_object_ptrjust_joined_ || participant->left_ || participant->versioned_; } +int32 GroupCallParticipant::get_volume_level() const { + return pending_volume_level != 0 ? pending_volume_level : volume_level; +} + bool GroupCallParticipant::update_can_be_muted(bool can_manage, bool is_self, bool is_admin) { bool new_can_be_muted_for_all_users = false; bool new_can_be_unmuted_for_all_users = false; @@ -90,7 +94,7 @@ td_api::object_ptr GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), audio_source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, - can_be_unmuted_only_for_self, is_muted, can_self_unmute, volume_level, order); + can_be_unmuted_only_for_self, is_muted, can_self_unmute, get_volume_level(), order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { @@ -100,7 +104,7 @@ bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && lhs.can_be_unmuted_only_for_self == rhs.can_be_unmuted_only_for_self && lhs.is_muted == rhs.is_muted && lhs.can_self_unmute == rhs.can_self_unmute && lhs.is_speaking == rhs.is_speaking && - lhs.volume_level == rhs.volume_level && lhs.order == rhs.order; + lhs.get_volume_level() == rhs.get_volume_level() && lhs.order == rhs.order; } bool operator!=(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 842eb5d05..5e10ba952 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -39,6 +39,9 @@ struct GroupCallParticipant { int32 local_active_date = 0; int64 order = 0; + int32 pending_volume_level = 0; + uint64 pending_volume_level_generation = 0; + static constexpr int32 MIN_VOLUME_LEVEL = 1; static constexpr int32 MAX_VOLUME_LEVEL = 20000; @@ -58,6 +61,8 @@ struct GroupCallParticipant { return user_id.is_valid(); } + int32 get_volume_level() const; + td_api::object_ptr get_group_call_participant_object( ContactsManager *contacts_manager) const; }; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 1096e1588..6d7c4c258 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2676,7 +2676,7 @@ class CliClient final : public Actor { get_args(args, group_call_id, user_id, is_muted); send_request(td_api::make_object(as_group_call_id(group_call_id), as_user_id(user_id), is_muted)); - } else if (op == "tgcpvl") { + } else if (op == "sgcpvl") { string group_call_id; string user_id; int32 volume_level; From c05ece2cb5ec06f39638ee740d6c3fe56c573938 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Feb 2021 01:06:49 +0300 Subject: [PATCH 100/232] Add GroupCallParticipant::update_from method. --- td/telegram/GroupCallManager.cpp | 24 +++--------------------- td/telegram/GroupCallParticipant.cpp | 23 +++++++++++++++++++++++ td/telegram/GroupCallParticipant.h | 2 ++ 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 9e74fa4e3..dd0ea67a6 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1350,32 +1350,14 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou return -1; } - if (participant.joined_date < old_participant.joined_date) { - LOG(ERROR) << "Join date of " << participant.user_id << " in " << input_group_call_id << " decreased from " - << old_participant.joined_date << " to " << participant.joined_date; - participant.joined_date = old_participant.joined_date; - } - if (participant.active_date < old_participant.active_date) { - participant.active_date = old_participant.active_date; - } - participant.local_active_date = old_participant.local_active_date; - participant.is_speaking = old_participant.is_speaking; + participant.update_from(old_participant); + + participant.is_just_joined = false; auto real_order = participant.get_real_order(); if (real_order >= participants->min_order) { participant.order = real_order; } - participant.is_just_joined = false; - if (participant.is_min) { - participant.is_muted_only_for_self = old_participant.is_muted_only_for_self; - } update_group_call_participant_can_be_muted(can_manage, participants, participant); - if (old_participant.is_volume_level_local && !participant.is_volume_level_local) { - participant.is_volume_level_local = true; - participant.volume_level = old_participant.volume_level; - } - participant.pending_volume_level = old_participant.pending_volume_level; - participant.pending_volume_level_generation = old_participant.pending_volume_level_generation; - participant.is_min = false; LOG(INFO) << "Edit " << old_participant << " to " << participant; if (old_participant != participant && (old_participant.order != 0 || participant.order != 0)) { diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index 1ee1fb98e..1f95f005f 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -50,6 +50,29 @@ int32 GroupCallParticipant::get_volume_level() const { return pending_volume_level != 0 ? pending_volume_level : volume_level; } +void GroupCallParticipant::update_from(const GroupCallParticipant &old_participant) { + CHECK(!old_participant.is_min); + if (joined_date < old_participant.joined_date) { + LOG(ERROR) << "Join date decreased from " << old_participant.joined_date << " to " << joined_date; + joined_date = old_participant.joined_date; + } + if (active_date < old_participant.active_date) { + active_date = old_participant.active_date; + } + local_active_date = old_participant.local_active_date; + is_speaking = old_participant.is_speaking; + if (is_min) { + is_muted_only_for_self = old_participant.is_muted_only_for_self; + } + if (old_participant.is_volume_level_local && !is_volume_level_local) { + is_volume_level_local = true; + volume_level = old_participant.volume_level; + } + pending_volume_level = old_participant.pending_volume_level; + pending_volume_level_generation = old_participant.pending_volume_level_generation; + is_min = false; +} + bool GroupCallParticipant::update_can_be_muted(bool can_manage, bool is_self, bool is_admin) { bool new_can_be_muted_for_all_users = false; bool new_can_be_unmuted_for_all_users = false; diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 5e10ba952..87f07b45a 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -51,6 +51,8 @@ struct GroupCallParticipant { static bool is_versioned_update(const tl_object_ptr &participant); + void update_from(const GroupCallParticipant &old_participant); + bool update_can_be_muted(bool can_manage, bool is_self, bool is_admin); int64 get_real_order() const { From 778a45029e2bf3320a000f20c64201d041cd69b9 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Feb 2021 01:23:12 +0300 Subject: [PATCH 101/232] Ignore min-updates about unknown group call participants. --- td/telegram/GroupCallManager.cpp | 45 +++++++++++++++++++++++--------- td/telegram/GroupCallManager.h | 2 +- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index dd0ea67a6..310277c30 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -431,8 +431,8 @@ struct GroupCallManager::GroupCallParticipants { bool are_administrators_loaded = false; vector administrator_user_ids; - std::map>> pending_version_updates_; - std::map>> pending_mute_updates_; + std::map> pending_version_updates_; + std::map> pending_mute_updates_; }; struct GroupCallManager::GroupCallRecentSpeakers { @@ -1034,9 +1034,23 @@ void GroupCallManager::on_update_group_call_participants( auto *group_call_participants = add_group_call_participants(input_group_call_id); auto &pending_mute_updates = group_call_participants->pending_mute_updates_[version]; - vector> version_updates; - for (auto &participant : participants) { - if (GroupCallParticipant::is_versioned_update(participant)) { + vector version_updates; + for (auto &group_call_participant : participants) { + GroupCallParticipant participant(group_call_participant); + if (participant.is_min && participant.joined_date != 0) { + auto old_participant = get_group_call_participant(group_call_participants, participant.user_id); + if (old_participant == nullptr) { + LOG(INFO) << "Can't apply min update about " << participant.user_id << " in " << input_group_call_id; + // TODO instead of synchronization, such participants can be received through GetGroupCallParticipantQuery + on_receive_group_call_version(input_group_call_id, version, true); + return; + } + + participant.update_from(*old_participant); + CHECK(!participant.is_min); + } + + if (GroupCallParticipant::is_versioned_update(group_call_participant)) { version_updates.push_back(std::move(participant)); } else { pending_mute_updates.push_back(std::move(participant)); @@ -1079,8 +1093,7 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup auto version = it->first; auto &participants = it->second; if (version <= group_call->version) { - for (auto &group_call_participant : participants) { - GroupCallParticipant participant(group_call_participant); + for (auto &participant : participants) { on_participant_speaking_in_group_call(input_group_call_id, participant); if (participant.user_id == td_->contacts_manager_->get_my_id() && version == group_call->version && participant.is_just_joined) { @@ -1123,8 +1136,7 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup auto version = it->first; if (version <= group_call->version) { auto &participants = it->second; - for (auto &group_call_participant : participants) { - GroupCallParticipant participant(group_call_participant); + for (auto &participant : participants) { on_participant_speaking_in_group_call(input_group_call_id, participant); int mute_diff = process_group_call_participant(input_group_call_id, std::move(participant)); CHECK(mute_diff == 0); @@ -1232,6 +1244,10 @@ void GroupCallManager::process_group_call_participants( LOG(ERROR) << "Receive invalid " << to_string(participant); continue; } + if (group_call_participant.is_min) { + LOG(ERROR) << "Receive unexpected min " << to_string(participant); + continue; + } auto real_order = group_call_participant.get_real_order(); if (real_order > min_order) { @@ -1375,7 +1391,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou return -1; } - // CHECK(!participant.is_min); + CHECK(!participant.is_min); int diff = participant.is_just_joined ? 1 : 0; if (participant.is_just_joined) { LOG(INFO) << "Add new " << participant; @@ -2112,7 +2128,8 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrpending_version_updates_[version]; // reserve place for updates - sync_participants_timeout_.add_timeout_in(group_call->group_call_id.get(), 1.0); + if (immediate_sync) { + sync_participants_timeout_.set_timeout_in(group_call->group_call_id.get(), 0.0); + } else { + sync_participants_timeout_.add_timeout_in(group_call->group_call_id.get(), 1.0); + } } void GroupCallManager::on_participant_speaking_in_group_call(InputGroupCallId input_group_call_id, diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index c53df445a..8c48fa272 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -179,7 +179,7 @@ class GroupCallManager : public Actor { InputGroupCallId update_group_call(const tl_object_ptr &group_call_ptr, DialogId dialog_id); - void on_receive_group_call_version(InputGroupCallId input_group_call_id, int32 version); + void on_receive_group_call_version(InputGroupCallId input_group_call_id, int32 version, bool immediate_sync = false); void on_participant_speaking_in_group_call(InputGroupCallId input_group_call_id, const GroupCallParticipant &participant); From 4371ed52affc8e7b875ecd4ebf727f6c3c2e5017 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Feb 2021 17:55:08 +0300 Subject: [PATCH 102/232] Don't drop group call participants while need to rejoin. --- td/telegram/GroupCallManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 310277c30..448d210ae 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -854,7 +854,7 @@ bool GroupCallManager::need_group_call_participants(InputGroupCallId input_group if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) { return false; } - if (group_call->is_joined) { + if (group_call->is_joined || group_call->need_rejoin) { return true; } if (pending_join_requests_.count(input_group_call_id) != 0) { @@ -1169,6 +1169,7 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup if (need_update) { send_update_group_call(group_call, "process_pending_group_call_participant_updates"); } + try_clear_group_call_participants(input_group_call_id); return need_update; } From 630f0fe6df0f46bcd45fa1b5867ee74f41255a21 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Feb 2021 18:58:06 +0300 Subject: [PATCH 103/232] Always show self in list of group call participants. --- td/telegram/GroupCallManager.cpp | 49 ++++++++++++++++++++------------ td/telegram/GroupCallManager.h | 2 ++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 448d210ae..c2cc20246 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1211,6 +1211,17 @@ void GroupCallManager::on_sync_group_call_participants_failed(InputGroupCallId i sync_participants_timeout_.add_timeout_in(group_call->group_call_id.get(), 1.0); } +int64 GroupCallManager::get_real_participant_order(const GroupCallParticipant &participant, int64 min_order) const { + auto real_order = participant.get_real_order(); + if (real_order < min_order && participant.user_id == td_->contacts_manager_->get_my_id()) { + return min_order; + } + if (real_order >= min_order) { + return real_order; + } + return 0; +} + void GroupCallManager::process_group_call_participants( InputGroupCallId input_group_call_id, vector> &&participants, bool is_load, bool is_sync) { @@ -1278,8 +1289,15 @@ void GroupCallManager::process_group_call_participants( // not synced user, needs to be deleted if (participant.order != 0) { CHECK(participant.order >= participants_it->second->min_order); - participant.order = 0; - send_update_group_call_participant(input_group_call_id, participant); + if (participant.user_id == td_->contacts_manager_->get_my_id()) { + if (participant.order != min_order) { + participant.order = min_order; + send_update_group_call_participant(input_group_call_id, participant); + } + } else { + participant.order = 0; + send_update_group_call_participant(input_group_call_id, participant); + } } participant_it = group_participants.erase(participant_it); } @@ -1298,9 +1316,9 @@ void GroupCallManager::process_group_call_participants( participants_it->second->min_order = min_order; for (auto &participant : participants_it->second->participants) { - auto real_order = participant.get_real_order(); + auto real_order = get_real_participant_order(participant, min_order); if (old_min_order > real_order && real_order >= min_order) { - CHECK(participant.order == 0); + CHECK(participant.order == 0 || participant.order == old_min_order); participant.order = real_order; send_update_group_call_participant(input_group_call_id, participant); } @@ -1370,10 +1388,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou participant.update_from(old_participant); participant.is_just_joined = false; - auto real_order = participant.get_real_order(); - if (real_order >= participants->min_order) { - participant.order = real_order; - } + participant.order = get_real_participant_order(participant, participants->min_order); update_group_call_participant_can_be_muted(can_manage, participants, participant); LOG(INFO) << "Edit " << old_participant << " to " << participant; @@ -1399,10 +1414,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou } else { LOG(INFO) << "Receive new " << participant; } - auto real_order = participant.get_real_order(); - if (real_order >= participants->min_order) { - participant.order = real_order; - } + participant.order = get_real_participant_order(participant, participants->min_order); participant.is_just_joined = false; update_group_call_participant_can_be_muted(can_manage, participants, participant); participants->participants.push_back(std::move(participant)); @@ -1833,7 +1845,9 @@ void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group participant->pending_volume_level = volume_level; participant->pending_volume_level_generation = ++set_volume_level_generation_; - send_update_group_call_participant(input_group_call_id, *participant); + if (participant->order != 0) { + send_update_group_call_participant(input_group_call_id, *participant); + } auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, user_id, generation = participant->pending_volume_level_generation, @@ -1868,7 +1882,9 @@ void GroupCallManager::on_set_group_call_participant_volume_level(InputGroupCall if (participant->volume_level != participant->pending_volume_level || !participant->is_volume_level_local) { LOG(ERROR) << "Failed to set volume level of " << user_id << " in " << input_group_call_id; participant->pending_volume_level = 0; - send_update_group_call_participant(input_group_call_id, *participant); + if (participant->order != 0) { + send_update_group_call_participant(input_group_call_id, *participant); + } } else { participant->pending_volume_level = 0; } @@ -2302,10 +2318,7 @@ UserId GroupCallManager::set_group_call_participant_is_speaking_by_source(InputG if (is_speaking) { participant.local_active_date = max(participant.local_active_date, date); } - auto real_order = participant.get_real_order(); - if (real_order >= participants_it->second->min_order) { - participant.order = real_order; - } + participant.order = get_real_participant_order(participant, participants_it->second->min_order); if (participant.order != 0) { send_update_group_call_participant(input_group_call_id, participant); } diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 8c48fa272..bc81a205f 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -145,6 +145,8 @@ class GroupCallManager : public Actor { void on_sync_group_call_participants_failed(InputGroupCallId input_group_call_id); + int64 get_real_participant_order(const GroupCallParticipant &participant, int64 min_order) const; + void process_group_call_participants(InputGroupCallId group_call_id, vector> &&participants, bool is_load, bool is_sync); From 9f2b41b9d10b360bfe45c700d627fc1fbb974455 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Feb 2021 19:04:29 +0300 Subject: [PATCH 104/232] Allow to call leaveGroupCall for calls with need_rejoin. --- td/telegram/GroupCallManager.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index c2cc20246..b2926787a 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1824,7 +1824,7 @@ void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group auto *group_call = get_group_call(input_group_call_id); if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { - return promise.set_error(Status::Error(400, "GROUP_CALL_JOIN_MISSING")); + return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); } if (!td_->contacts_manager_->have_input_user(user_id)) { return promise.set_error(Status::Error(400, "Have no access to the user")); @@ -1922,6 +1922,12 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise auto *group_call = get_group_call(input_group_call_id); if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + if (group_call->need_rejoin) { + group_call->need_rejoin = false; + send_update_group_call(group_call, "leave_group_call"); + try_clear_group_call_participants(input_group_call_id); + promise.set_value(Unit()); + } return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); } auto audio_source = group_call->audio_source; From be6793ec079b85274d3e3a79e8457889458cedd9 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Feb 2021 19:55:33 +0300 Subject: [PATCH 105/232] Always add self to group call participant list if absent. --- td/telegram/GroupCallManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index b2926787a..690a33260 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1093,10 +1093,11 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup auto version = it->first; auto &participants = it->second; if (version <= group_call->version) { + auto my_user_id = td_->contacts_manager_->get_my_id(); + auto my_participant = get_group_call_participant(participants_it->second.get(), my_user_id); for (auto &participant : participants) { on_participant_speaking_in_group_call(input_group_call_id, participant); - if (participant.user_id == td_->contacts_manager_->get_my_id() && version == group_call->version && - participant.is_just_joined) { + if (my_participant == nullptr && participant.user_id == my_user_id) { process_group_call_participant(input_group_call_id, std::move(participant)); } } From 8146ecf950768d8549f46ccdb0e78c1aaf4e7071 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Feb 2021 20:12:17 +0300 Subject: [PATCH 106/232] Don't expect local volume_level for admins. --- td/telegram/GroupCallManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 690a33260..22e349247 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1880,7 +1880,7 @@ void GroupCallManager::on_set_group_call_participant_volume_level(InputGroupCall return promise.set_value(Unit()); } - if (participant->volume_level != participant->pending_volume_level || !participant->is_volume_level_local) { + if (participant->volume_level != participant->pending_volume_level) { LOG(ERROR) << "Failed to set volume level of " << user_id << " in " << input_group_call_id; participant->pending_volume_level = 0; if (participant->order != 0) { From b07a9efb2eabb9ede3e16a10c0ecdafe4560cc17 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Feb 2021 21:08:01 +0300 Subject: [PATCH 107/232] Disable SFTP link highlighting. --- td/telegram/MessageEntity.cpp | 7 ++----- test/message_entities.cpp | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index ec8a7dda8..9a4c0be62 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -677,9 +677,7 @@ static vector match_urls(Slice str) { url_begin_ptr = url_begin_ptr - 7; } else if (ends_with(protocol, "https")) { url_begin_ptr = url_begin_ptr - 8; - } else if (ends_with(protocol, "sftp")) { - url_begin_ptr = url_begin_ptr - 7; - } else if (ends_with(protocol, "ftp") && protocol != "tftp") { + } else if (ends_with(protocol, "ftp") && protocol != "tftp" && protocol != "sftp") { url_begin_ptr = url_begin_ptr - 6; } else { is_bad = true; @@ -983,8 +981,7 @@ Slice fix_url(Slice str) { bool has_protocol = false; auto str_begin = to_lower(str.substr(0, 8)); - if (begins_with(str_begin, "http://") || begins_with(str_begin, "https://") || begins_with(str_begin, "sftp://") || - begins_with(str_begin, "ftp://")) { + if (begins_with(str_begin, "http://") || begins_with(str_begin, "https://") || begins_with(str_begin, "ftp://")) { auto pos = str.find(':'); str = str.substr(pos + 3); has_protocol = true; diff --git a/test/message_entities.cpp b/test/message_entities.cpp index b9636fa51..d65bc320f 100644 --- a/test/message_entities.cpp +++ b/test/message_entities.cpp @@ -381,7 +381,7 @@ TEST(MessageEntities, url) { check_url("http://telegram.org", {"http://telegram.org"}); check_url("ftp://telegram.org", {"ftp://telegram.org"}); check_url("ftps://telegram.org", {}); - check_url("sftp://telegram.org", {"sftp://telegram.org"}); + check_url("sftp://telegram.org", {}); check_url("hTtPs://telegram.org", {"hTtPs://telegram.org"}); check_url("HTTP://telegram.org", {"HTTP://telegram.org"}); check_url("аHTTP://telegram.org", {"HTTP://telegram.org"}); From e031a2c6c14593dc09fc6e62f8d3d7a7a8b1093b Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Feb 2021 15:54:12 +0300 Subject: [PATCH 108/232] Improve Slice::truncate usage. --- td/mtproto/ProxySecret.h | 6 +++--- td/mtproto/TcpTransport.cpp | 2 +- td/mtproto/Transport.cpp | 2 +- td/telegram/SecretChatActor.cpp | 2 +- td/telegram/files/FileDownloader.cpp | 2 +- tddb/td/db/binlog/BinlogEvent.cpp | 5 ++--- tdutils/td/utils/BufferedFd.h | 3 ++- tdutils/td/utils/BufferedUdp.h | 4 ++-- tdutils/td/utils/port/StdStreams.cpp | 3 ++- 9 files changed, 15 insertions(+), 14 deletions(-) diff --git a/td/mtproto/ProxySecret.h b/td/mtproto/ProxySecret.h index 6bb1e441a..fb88ff25c 100644 --- a/td/mtproto/ProxySecret.h +++ b/td/mtproto/ProxySecret.h @@ -32,9 +32,9 @@ class ProxySecret { } Slice get_proxy_secret() const { - auto proxy_secret = Slice(secret_).truncate(17); - if (proxy_secret.size() == 17) { - proxy_secret.remove_prefix(1); + Slice proxy_secret(secret_); + if (proxy_secret.size() >= 17) { + return proxy_secret.substr(1, 16); } return proxy_secret; } diff --git a/td/mtproto/TcpTransport.cpp b/td/mtproto/TcpTransport.cpp index 70546ec1c..98760aa1e 100644 --- a/td/mtproto/TcpTransport.cpp +++ b/td/mtproto/TcpTransport.cpp @@ -63,7 +63,7 @@ void IntermediateTransport::write_prepare_inplace(BufferWriter *message, bool qu size_t append_size = 0; if (with_padding()) { append_size = Random::secure_uint32() % 16; - MutableSlice append = message->prepare_append().truncate(append_size); + MutableSlice append = message->prepare_append().substr(0, append_size); CHECK(append.size() == append_size); Random::secure_bytes(append); message->confirm_append(append.size()); diff --git a/td/mtproto/Transport.cpp b/td/mtproto/Transport.cpp index 782aa8683..7aa616622 100644 --- a/td/mtproto/Transport.cpp +++ b/td/mtproto/Transport.cpp @@ -226,7 +226,7 @@ Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &a auto *header = reinterpret_cast(message.begin()); *header_ptr = header; auto to_decrypt = MutableSlice(header->encrypt_begin(), message.uend()); - to_decrypt = to_decrypt.truncate(to_decrypt.size() & ~15); + to_decrypt.truncate(to_decrypt.size() & ~15); if (to_decrypt.size() % 16 != 0) { return Status::Error(PSLICE() << "Invalid mtproto message: size of encrypted part is not multiple of 16 [size = " << to_decrypt.size() << "]"); diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index 571472767..cf290178c 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -2041,7 +2041,7 @@ void SecretChatActor::calc_key_hash() { auto sha256_slice = MutableSlice(sha256_buf, 32); sha256(pfs_state_.auth_key.key(), sha256_slice); - auth_state_.key_hash = sha1_slice.truncate(16).str() + sha256_slice.truncate(20).str(); + auth_state_.key_hash = PSTRING() << sha1_slice.substr(0, 16) << sha256_slice.substr(0, 20); } void SecretChatActor::send_update_secret_chat() { diff --git a/td/telegram/files/FileDownloader.cpp b/td/telegram/files/FileDownloader.cpp index 7598c8ea5..d06b697db 100644 --- a/td/telegram/files/FileDownloader.cpp +++ b/td/telegram/files/FileDownloader.cpp @@ -367,7 +367,7 @@ Result FileDownloader::process_part(Part part, NetQueryPtr net_query) { bytes.as_slice()); } - auto slice = bytes.as_slice().truncate(part.size); + auto slice = bytes.as_slice().substr(0, part.size); TRY_STATUS(acquire_fd()); LOG(INFO) << "Got " << slice.size() << " bytes at offset " << part.offset << " for \"" << path_ << '"'; TRY_RESULT(written, fd_.pwrite(slice, part.offset)); diff --git a/tddb/td/db/binlog/BinlogEvent.cpp b/tddb/td/db/binlog/BinlogEvent.cpp index 246c14110..3333a821f 100644 --- a/tddb/td/db/binlog/BinlogEvent.cpp +++ b/tddb/td/db/binlog/BinlogEvent.cpp @@ -28,8 +28,7 @@ Status BinlogEvent::init(BufferSlice &&raw_event, bool check_crc) { data_ = MutableSlice(const_cast(slice_data.begin()), slice_data.size()); crc32_ = static_cast(parser.fetch_int()); if (check_crc) { - CHECK(size_ >= TAIL_SIZE); - auto calculated_crc = crc32(raw_event.as_slice().truncate(size_ - TAIL_SIZE)); + auto calculated_crc = crc32(raw_event.as_slice().substr(0, size_ - TAIL_SIZE)); if (calculated_crc != crc32_) { return Status::Error(PSLICE() << "crc mismatch " << tag("actual", format::as_hex(calculated_crc)) << tag("expected", format::as_hex(crc32_)) << public_to_string()); @@ -44,7 +43,7 @@ Status BinlogEvent::validate() const { if (raw_event_.size() < 4) { return Status::Error("Too small event"); } - uint32 size = TlParser(raw_event_.as_slice().truncate(4)).fetch_int(); + uint32 size = TlParser(raw_event_.as_slice().substr(0, 4)).fetch_int(); if (size_ != size) { return Status::Error(PSLICE() << "Size of event changed: " << tag("was", size_) << tag("now", size)); } diff --git a/tdutils/td/utils/BufferedFd.h b/tdutils/td/utils/BufferedFd.h index 014c0667a..cd092ea55 100644 --- a/tdutils/td/utils/BufferedFd.h +++ b/tdutils/td/utils/BufferedFd.h @@ -101,7 +101,8 @@ Result BufferedFdBase::flush_read(size_t max_read) { CHECK(read_); size_t result = 0; while (::td::can_read_local(*this) && max_read) { - MutableSlice slice = read_->prepare_append().truncate(max_read); + MutableSlice slice = read_->prepare_append(); + slice.truncate(max_read); TRY_RESULT(x, FdT::read(slice)); slice.truncate(x); read_->confirm_append(x); diff --git a/tdutils/td/utils/BufferedUdp.h b/tdutils/td/utils/BufferedUdp.h index 348f89035..b99815baa 100644 --- a/tdutils/td/utils/BufferedUdp.h +++ b/tdutils/td/utils/BufferedUdp.h @@ -36,7 +36,7 @@ class UdpWriter { } size_t cnt; - auto status = fd.send_messages(::td::Span(messages).truncate(to_send_n), cnt); + auto status = fd.send_messages(Span(messages).truncate(to_send_n), cnt); queue.pop_n(cnt); return status; } @@ -51,7 +51,7 @@ class UdpReaderHelper { buffer_ = BufferSlice(RESERVED_SIZE); } CHECK(buffer_.size() >= MAX_PACKET_SIZE); - message.data = buffer_.as_slice().truncate(MAX_PACKET_SIZE); + message.data = buffer_.as_slice().substr(0, MAX_PACKET_SIZE); } UdpMessage extract_udp_message(UdpSocketFd::InboundMessage &message) { diff --git a/tdutils/td/utils/port/StdStreams.cpp b/tdutils/td/utils/port/StdStreams.cpp index 5fcd71441..df73a643f 100644 --- a/tdutils/td/utils/port/StdStreams.cpp +++ b/tdutils/td/utils/port/StdStreams.cpp @@ -201,7 +201,8 @@ class BufferedStdinImpl { size_t result = 0; ::td::sync_with_poll(*this); while (::td::can_read_local(*this) && max_read) { - MutableSlice slice = writer_.prepare_append().truncate(max_read); + MutableSlice slice = writer_.prepare_append(); + slice.truncate(max_read); TRY_RESULT(x, file_fd_.read(slice)); slice.truncate(x); writer_.confirm_append(x); From 65a7715248f070ea12a7dfdd8534d276a414bb5a Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Feb 2021 16:01:04 +0300 Subject: [PATCH 109/232] Pass promise to MessagesManager::on_dialog_deleted. --- td/telegram/ContactsManager.cpp | 5 +++-- td/telegram/MessagesManager.cpp | 5 +++-- td/telegram/MessagesManager.h | 2 +- td/telegram/Td.cpp | 3 +-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 06ea3e580..ccd57eac6 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -7318,7 +7318,7 @@ void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, bo if (c->status.is_left()) { if (user_id == my_id) { if (revoke_messages) { - td_->messages_manager_->delete_dialog(DialogId(chat_id)); + return td_->messages_manager_->delete_dialog_history(dialog_id, true, true, std::move(promise)); } return promise.set_value(Unit()); } else { @@ -9753,7 +9753,8 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from bool have_read_access = have_input_peer_channel(c, channel_id, AccessRights::Read); bool is_member = c->status.is_member(); if (c->had_read_access && !have_read_access) { - send_closure_later(G()->messages_manager(), &MessagesManager::delete_dialog, DialogId(channel_id)); + send_closure_later(G()->messages_manager(), &MessagesManager::on_dialog_deleted, DialogId(channel_id), + Promise()); } else if (!from_database && c->was_member != is_member) { DialogId dialog_id(channel_id); send_closure_later(G()->messages_manager(), &MessagesManager::force_create_dialog, dialog_id, "update channel", diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index fadd3b886..3d52ea015 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -10868,11 +10868,11 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia send_update_delete_messages(d->dialog_id, std::move(deleted_message_ids), is_permanently_deleted, false); } -void MessagesManager::delete_dialog(DialogId dialog_id) { +void MessagesManager::on_dialog_deleted(DialogId dialog_id, Promise &&promise) { LOG(INFO) << "Delete " << dialog_id; Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { - return; + return promise.set_value(Unit()); } delete_all_dialog_messages(d, true, false); @@ -10885,6 +10885,7 @@ void MessagesManager::delete_dialog(DialogId dialog_id) { } close_dialog(d); + promise.set_value(Unit()); } void MessagesManager::on_update_dialog_group_call_rights(DialogId dialog_id) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 88a035a80..a4b1b89c8 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -342,7 +342,7 @@ class MessagesManager : public Actor { void delete_dialog_messages_from_user(DialogId dialog_id, UserId user_id, Promise &&promise); - void delete_dialog(DialogId dialog_id); + void on_dialog_deleted(DialogId dialog_id, Promise &&promise); void on_update_dialog_group_call_rights(DialogId dialog_id); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 224758f11..a6fd56ab6 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5484,8 +5484,7 @@ void Td::on_request(uint64 id, const td_api::deleteChat &request) { if (result.is_error()) { promise.set_error(result.move_as_error()); } else { - send_closure(actor_id, &MessagesManager::delete_dialog, dialog_id); - promise.set_value(Unit()); + send_closure(actor_id, &MessagesManager::on_dialog_deleted, dialog_id, std::move(promise)); } }; contacts_manager_->delete_dialog(dialog_id, std::move(query_promise)); From a3cdc8ffef013ab34854652cf7bf2be8dde1a105 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Feb 2021 16:49:41 +0300 Subject: [PATCH 110/232] Fix CE. --- td/telegram/ContactsManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index ccd57eac6..f3bd2725a 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -7318,7 +7318,7 @@ void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, bo if (c->status.is_left()) { if (user_id == my_id) { if (revoke_messages) { - return td_->messages_manager_->delete_dialog_history(dialog_id, true, true, std::move(promise)); + return td_->messages_manager_->delete_dialog_history(DialogId(chat_id), true, true, std::move(promise)); } return promise.set_value(Unit()); } else { From 23b8c9073b1afab091b18f2a585c40f1f42504c9 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Feb 2021 17:28:19 +0300 Subject: [PATCH 111/232] Always update self group call participant if audio_source changes. --- td/telegram/GroupCallManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 22e349247..0288e0f5e 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1097,7 +1097,8 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup auto my_participant = get_group_call_participant(participants_it->second.get(), my_user_id); for (auto &participant : participants) { on_participant_speaking_in_group_call(input_group_call_id, participant); - if (my_participant == nullptr && participant.user_id == my_user_id) { + if (participant.user_id == my_user_id && + (my_participant == nullptr || my_participant->audio_source != participant.audio_source)) { process_group_call_participant(input_group_call_id, std::move(participant)); } } From f5b04a5701eb4b48bc880bb3d2da2a02a5975ac6 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Feb 2021 22:45:02 +0300 Subject: [PATCH 112/232] Improve condition for updating self in a group call --- td/telegram/GroupCallManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 0288e0f5e..8d76f70ce 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1098,7 +1098,9 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup for (auto &participant : participants) { on_participant_speaking_in_group_call(input_group_call_id, participant); if (participant.user_id == my_user_id && - (my_participant == nullptr || my_participant->audio_source != participant.audio_source)) { + (my_participant == nullptr || my_participant->joined_date < participant.joined_date || + (my_participant->joined_date <= participant.joined_date && + my_participant->audio_source != participant.audio_source))) { process_group_call_participant(input_group_call_id, std::move(participant)); } } From f1f739648c5e93eee576bcaf62837b4549c8a9fc Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 6 Feb 2021 22:54:36 +0300 Subject: [PATCH 113/232] Fix banChatMember description. --- td/generate/scheme/td_api.tl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index acd064ec4..aadf6ff40 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4309,7 +4309,7 @@ addChatMembers chat_id:int53 user_ids:vector = Ok; //@chat_id Chat identifier @user_id User identifier @status The new status of the member in the chat setChatMemberStatus chat_id:int53 user_id:int32 status:ChatMemberStatus = Ok; -//@description Bans a member in a chat. Members can't be banned in private or secret chats. In supergroups and channels, the user will not be able to return to the group on their own using invite links, etc., unless [unbanned](#unbanchatmember) first +//@description Bans a member in a chat. Members can't be banned in private or secret chats. In supergroups and channels, the user will not be able to return to the group on their own using invite links, etc., unless unbanned first //@chat_id Chat identifier //@user_id Identifier of the user //@banned_until_date Point in time (Unix timestamp) when the user will be unbanned; 0 if never. If the user is banned for more than 366 days or for less than 30 seconds from the current time, the user is considered to be banned forever. Ignored in basic groups From bfeb516bf0cc1a124c6e4a1748cbce4b3d8b13fb Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 6 Feb 2021 23:29:01 +0300 Subject: [PATCH 114/232] Add secret chat layer 123. --- td/telegram/SecretChatActor.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/td/telegram/SecretChatActor.h b/td/telegram/SecretChatActor.h index 682aa7044..e3e29bdd5 100644 --- a/td/telegram/SecretChatActor.h +++ b/td/telegram/SecretChatActor.h @@ -55,7 +55,8 @@ class SecretChatActor : public NetQueryCallback { VIDEO_NOTES_LAYER = 66, MTPROTO_2_LAYER = 73, NEW_ENTITIES_LAYER = 101, - MY_LAYER = NEW_ENTITIES_LAYER + DELETE_MESSAGES_ON_CLOSE_LAYER = 123, + MY_LAYER = DELETE_MESSAGES_ON_CLOSE_LAYER }; class Context { From b767414fc15a157aa431ad4ba02d8007c71c20db Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 10 Feb 2021 15:25:18 +0300 Subject: [PATCH 115/232] Resave dialog if last message can't be added. --- td/telegram/MessagesManager.cpp | 45 +++++++++++++++++---------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 3d52ea015..86bd13c95 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -33769,33 +33769,34 @@ void MessagesManager::add_dialog_last_database_message(Dialog *d, unique_ptrlast_database_message_id == message_id) << message_id << " " << d->last_database_message_id << " " << d->debug_set_dialog_last_database_message_id; - if (!have_input_peer(d->dialog_id, AccessRights::Read)) { - // do not add last message to inaccessible dialog + bool need_update_dialog_pos = false; + const Message *m = nullptr; + if (have_input_peer(d->dialog_id, AccessRights::Read)) { + bool need_update = false; + last_database_message->have_previous = false; + last_database_message->have_next = false; + last_database_message->from_database = true; + m = add_message_to_dialog(d, std::move(last_database_message), false, &need_update, &need_update_dialog_pos, + "add_dialog_last_database_message 1"); + if (need_update_dialog_pos) { + LOG(ERROR) << "Need to update pos in " << d->dialog_id; + } + } + if (m != nullptr) { + set_dialog_last_message_id(d, m->message_id, "add_dialog_last_database_message 2"); + send_update_chat_last_message(d, "add_dialog_last_database_message 3"); + } else { if (d->pending_last_message_date != 0) { d->pending_last_message_date = 0; d->pending_last_message_id = MessageId(); - update_dialog_pos(d, "add_dialog_last_database_message 1"); + need_update_dialog_pos = true; } - return; - } + on_dialog_updated(d->dialog_id, "add_dialog_last_database_message 4"); // resave without last database message - bool need_update = false; - bool need_update_dialog_pos = false; - last_database_message->have_previous = false; - last_database_message->have_next = false; - last_database_message->from_database = true; - Message *m = add_message_to_dialog(d, std::move(last_database_message), false, &need_update, &need_update_dialog_pos, - "add_dialog_last_database_message 2"); - if (need_update_dialog_pos) { - LOG(ERROR) << "Need to update pos in " << d->dialog_id; - } - if (m != nullptr) { - set_dialog_last_message_id(d, message_id, "add_dialog_last_database_message 3"); - send_update_chat_last_message(d, "add_dialog_last_database_message 4"); - } else if (d->pending_last_message_date != 0) { - d->pending_last_message_date = 0; - d->pending_last_message_id = MessageId(); - need_update_dialog_pos = true; + if (!td_->auth_manager_->is_bot() && d->dialog_id != being_added_dialog_id_ && + have_input_peer(d->dialog_id, AccessRights::Read) && (d->order != DEFAULT_ORDER || is_dialog_sponsored(d))) { + get_history_from_the_end(d->dialog_id, true, false, Auto()); + } } if (need_update_dialog_pos) { From 4a60fb10e859c05fb219c96b2d8a5c060e3fa241 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 10 Feb 2021 15:48:06 +0300 Subject: [PATCH 116/232] Always update recent_speakers, because is_speaking could be changed. --- td/telegram/GroupCallManager.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 8d76f70ce..0277f45cc 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -2245,17 +2245,11 @@ void GroupCallManager::on_user_speaking_in_group_call(GroupCallId group_call_id, return; } recent_speakers->users[i].second = date; - bool is_updated = false; while (i > 0 && recent_speakers->users[i - 1].second < date) { std::swap(recent_speakers->users[i - 1], recent_speakers->users[i]); i--; - is_updated = true; - } - if (is_updated) { - on_group_call_recent_speakers_updated(group_call, recent_speakers.get()); - } else { - LOG(INFO) << "Position of " << user_id << " in recent speakers list didn't change"; } + on_group_call_recent_speakers_updated(group_call, recent_speakers.get()); return; } } @@ -2369,7 +2363,7 @@ vector> GroupCallManager::get vector> recent_speaker_users; for (auto &recent_speaker : recent_speakers->users) { - recent_speaker_users.emplace_back(recent_speaker.first, recent_speaker.second > now - 5); + recent_speaker_users.emplace_back(recent_speaker.first, recent_speaker.second > now - 8); } if (recent_speakers->is_changed) { From 0c90ca3784e3ad98b1125470c444c5f21df6f4d5 Mon Sep 17 00:00:00 2001 From: Arseny Smirnov Date: Wed, 10 Feb 2021 23:24:42 +0300 Subject: [PATCH 117/232] some cryptography draft --- td/mtproto/Transport.h | 2 +- tdutils/td/utils/base64.cpp | 3 + tdutils/td/utils/base64.h | 1 + test/CMakeLists.txt | 3 + test/crypto.cpp | 267 ++++++++++++++++++++++++++++++++++++ 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 test/crypto.cpp diff --git a/td/mtproto/Transport.h b/td/mtproto/Transport.h index d8875f59a..57eea1a62 100644 --- a/td/mtproto/Transport.h +++ b/td/mtproto/Transport.h @@ -91,11 +91,11 @@ class Transport { static size_t write(const Storer &storer, const AuthKey &auth_key, PacketInfo *info, MutableSlice dest = MutableSlice()); + static std::pair calc_message_key2(const AuthKey &auth_key, int X, Slice to_encrypt); private: template static std::pair calc_message_ack_and_key(const HeaderT &head, size_t data_size); - static std::pair calc_message_key2(const AuthKey &auth_key, int X, Slice to_encrypt); template static size_t calc_crypto_size(size_t data_size); diff --git a/tdutils/td/utils/base64.cpp b/tdutils/td/utils/base64.cpp index e3eda3b3c..92f8e1d13 100644 --- a/tdutils/td/utils/base64.cpp +++ b/tdutils/td/utils/base64.cpp @@ -156,6 +156,9 @@ Result base64_decode_secure(Slice base64) { Result base64url_decode(Slice base64) { return base64_decode_impl(base64); } +Result base64url_decode_secure(Slice base64) { + return base64_decode_impl(base64); +} template static bool is_base64_impl(Slice input) { diff --git a/tdutils/td/utils/base64.h b/tdutils/td/utils/base64.h index d67115c6f..ec67dc6c9 100644 --- a/tdutils/td/utils/base64.h +++ b/tdutils/td/utils/base64.h @@ -19,6 +19,7 @@ Result base64_decode_secure(Slice base64); string base64url_encode(Slice input); Result base64url_decode(Slice base64); +Result base64url_decode_secure(Slice base64); bool is_base64(Slice input); bool is_base64url(Slice input); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 33b3fa426..154a38153 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,6 +32,9 @@ set(TESTS_MAIN if (NOT CMAKE_CROSSCOMPILING OR EMSCRIPTEN) #Tests + add_executable(test-crypto EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/crypto.cpp) + target_include_directories(test-crypto PUBLIC ${OPENSSL_INCLUDE_DIR}) + target_link_libraries(test-crypto ${OPENSSL_LIBRARIES} tdutils tdcore) add_executable(test-tdutils EXCLUDE_FROM_ALL ${TESTS_MAIN} ${TDUTILS_TEST_SOURCE}) add_executable(test-online EXCLUDE_FROM_ALL online.cpp) add_executable(run_all_tests ${TESTS_MAIN} ${TD_TEST_SOURCE}) diff --git a/test/crypto.cpp b/test/crypto.cpp new file mode 100644 index 000000000..ffbba3cd8 --- /dev/null +++ b/test/crypto.cpp @@ -0,0 +1,267 @@ + +#include +#include +#include +#include + +#include "td/utils/Status.h" +#include "td/utils/SharedSlice.h" +#include "td/utils/base64.h" +#include "td/utils/tl_storers.h" + +#include "td/utils/crypto.h" + +#include "td/mtproto/AuthKey.h" +#include "td/mtproto/KDF.h" +#include "td/mtproto/Transport.h" + + +class Handshake { + public: + struct KeyPair { + td::SecureString private_key; + td::SecureString public_key; + }; + + static td::Result generate_key_pair() { + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(NID_X25519, NULL); + SCOPE_EXIT { + EVP_PKEY_CTX_free(pctx); + }; + if (pctx == nullptr) { + return td::Status::Error("Can't create EXP_PKEY_CTX"); + } + if (EVP_PKEY_keygen_init(pctx) <= 0) { + return td::Status::Error("Can't init keygen"); + } + if (EVP_PKEY_keygen(pctx, &pkey) <= 0) { + return td::Status::Error("Can't generate key"); + } + + TRY_RESULT(private_key, X25519_key_from_PKEY(pkey, true)); + TRY_RESULT(public_key, X25519_key_from_PKEY(pkey, false)); + + KeyPair res; + res.private_key = std::move(private_key); + res.public_key = std::move(public_key); + + return res; + } + + static td::SecureString expand_secret(td::Slice secret) { + td::SecureString res(128); + td::hmac_sha512(secret, "0", res.as_mutable_slice().substr(0, 64)); + td::hmac_sha512(secret, "1", res.as_mutable_slice().substr(64, 64)); + return res; + } + + static td::Result privateKeyToPem(td::Slice key) { + auto pkey_private = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, nullptr, key.ubegin(), 32); + CHECK(pkey_private != nullptr); + auto res = X25519_pem_from_PKEY(pkey_private, true); + EVP_PKEY_free(pkey_private); + return res; + } + + static td::Result calc_shared_secret(td::Slice private_key, td::Slice other_public_key) { + auto pkey_private = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, nullptr, private_key.ubegin(), 32); + if (pkey_private == nullptr) { + return td::Status::Error("Invalid X25520 private key"); + } + SCOPE_EXIT { + EVP_PKEY_free(pkey_private); + }; + + auto pkey_public = + EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, nullptr, other_public_key.ubegin(), other_public_key.size()); + if (pkey_public == nullptr) { + return td::Status::Error("Invalid X25519 public key"); + } + SCOPE_EXIT { + EVP_PKEY_free(pkey_public); + }; + + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey_private, nullptr); + if (ctx == nullptr) { + return td::Status::Error("Can't create EVP_PKEY_CTX"); + } + SCOPE_EXIT { + EVP_PKEY_CTX_free(ctx); + }; + + if (EVP_PKEY_derive_init(ctx) <= 0) { + return td::Status::Error("Can't init derive"); + } + if (EVP_PKEY_derive_set_peer(ctx, pkey_public) <= 0) { + return td::Status::Error("Can't init derive"); + } + + size_t result_len = 0; + if (EVP_PKEY_derive(ctx, nullptr, &result_len) <= 0) { + return td::Status::Error("Can't get result length"); + } + if (result_len != 32) { + return td::Status::Error("Unexpected result length"); + } + + td::SecureString result(result_len, '\0'); + if (EVP_PKEY_derive(ctx, result.as_mutable_slice().ubegin(), &result_len) <= 0) { + return td::Status::Error("Failed to compute shared secret"); + } + return std::move(result); + } + + private: + static td::Result X25519_key_from_PKEY(EVP_PKEY *pkey, bool is_private) { + auto func = is_private ? &EVP_PKEY_get_raw_private_key : &EVP_PKEY_get_raw_public_key; + size_t len = 0; + if (func(pkey, nullptr, &len) == 0) { + return td::Status::Error("Failed to get raw key length"); + } + CHECK(len == 32); + + td::SecureString result(len); + if (func(pkey, result.as_mutable_slice().ubegin(), &len) == 0) { + return td::Status::Error("Failed to get raw key"); + } + return std::move(result); + } + static td::Result X25519_pem_from_PKEY(EVP_PKEY *pkey, bool is_private) { + BIO *mem_bio = BIO_new(BIO_s_mem()); + SCOPE_EXIT { + BIO_vfree(mem_bio); + }; + if (is_private) { + PEM_write_bio_PrivateKey(mem_bio, pkey, nullptr, nullptr, 0, nullptr, nullptr); + } else { + PEM_write_bio_PUBKEY(mem_bio, pkey); + } + char *data_ptr = nullptr; + auto data_size = BIO_get_mem_data(mem_bio, &data_ptr); + return std::string(data_ptr, data_size); + } +}; + +struct HandshakeTest { + Handshake::KeyPair alice; + Handshake::KeyPair bob; + + td::SecureString shared_secret; + td::SecureString key; +}; + +namespace td { + +void KDF2(Slice auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt128 *aes_iv) { + uint8 buf_raw[36 + 16]; + MutableSlice buf(buf_raw, 36 + 16); + Slice msg_key_slice = as_slice(msg_key); + + // sha256_a = SHA256 (msg_key + substr(auth_key, x, 36)); + buf.copy_from(msg_key_slice); + buf.substr(16, 36).copy_from(auth_key.substr(X, 36)); + uint8 sha256_a_raw[32]; + MutableSlice sha256_a(sha256_a_raw, 32); + sha256(buf, sha256_a); + + // sha256_b = SHA256 (substr(auth_key, 40+x, 36) + msg_key); + buf.copy_from(auth_key.substr(40 + X, 36)); + buf.substr(36).copy_from(msg_key_slice); + uint8 sha256_b_raw[32]; + MutableSlice sha256_b(sha256_b_raw, 32); + sha256(buf, sha256_b); + + // aes_key = substr(sha256_a, 0, 8) + substr(sha256_b, 8, 16) + substr(sha256_a, 24, 8); + MutableSlice aes_key_slice(aes_key->raw, sizeof(aes_key->raw)); + aes_key_slice.copy_from(sha256_a.substr(0, 8)); + aes_key_slice.substr(8).copy_from(sha256_b.substr(8, 16)); + aes_key_slice.substr(24).copy_from(sha256_a.substr(24, 8)); + + // aes_iv = substr(sha256_b, 0, 4) + substr(sha256_a, 8, 8) + substr(sha256_b, 24, 4); + MutableSlice aes_iv_slice(aes_iv->raw, sizeof(aes_iv->raw)); + aes_iv_slice.copy_from(sha256_b.substr(0, 4)); + aes_iv_slice.substr(4).copy_from(sha256_a.substr(8, 8)); + aes_iv_slice.substr(12).copy_from(sha256_b.substr(24, 4)); +} +} + +td::SecureString encrypt(td::Slice key, td::Slice data, td::int32 seqno, int X) { + td::SecureString res(data.size() + 4 + 16); + res.as_mutable_slice().substr(20).copy_from(data); + td::TlStorerUnsafe storer(res.as_mutable_slice().substr(16).ubegin()); + storer.store_int(seqno); + td::mtproto::AuthKey auth_key(0, key.str()); + auto payload = res.as_mutable_slice().substr(16); + td::UInt128 msg_key = td::mtproto::Transport::calc_message_key2(auth_key, X, payload).second; + td::UInt256 aes_key; + td::UInt128 aes_iv; + td::KDF2(key, msg_key, X, &aes_key, &aes_iv); + td::AesCtrState aes; + aes.init(aes_key.as_slice(), aes_iv.as_slice()); + aes.encrypt(payload, payload); + res.as_mutable_slice().copy_from(msg_key.as_slice()); + return res; +} + +HandshakeTest gen_test() { + HandshakeTest res; + res.alice = Handshake::generate_key_pair().move_as_ok(); + + res.bob = Handshake::generate_key_pair().move_as_ok(); + res.shared_secret = Handshake::calc_shared_secret(res.alice.private_key, res.bob.public_key).move_as_ok(); + res.key = Handshake::expand_secret(res.shared_secret); + return res; +} + + +void run_test(const HandshakeTest &test) { + auto alice_secret = Handshake::calc_shared_secret(test.alice.private_key, test.bob.public_key).move_as_ok(); + auto bob_secret = Handshake::calc_shared_secret(test.bob.private_key, test.alice.public_key).move_as_ok(); + auto key = Handshake::expand_secret(alice_secret); + CHECK(alice_secret == bob_secret); + CHECK(alice_secret == test.shared_secret); + LOG(ERROR) << "Key\n\t" << td::base64url_encode(key) << "\n"; + CHECK(key == test.key); +} + +td::StringBuilder &operator<<(td::StringBuilder &sb, const Handshake::KeyPair &key_pair) { + sb << "\tpublic_key (base64url) = " << td::base64url_encode(key_pair.public_key) << "\n"; + sb << "\tprivate_key (base64url) = " << td::base64url_encode(key_pair.private_key) << "\n"; + sb << "\tprivate_key (pem) = \n" << Handshake::privateKeyToPem(key_pair.private_key).ok() << "\n"; + return sb; +} +td::StringBuilder &operator<<(td::StringBuilder &sb, const HandshakeTest &test) { + sb << "Alice\n" << test.alice; + sb << "Bob\n" << test.bob; + sb << "SharedSecret\n\t" << td::base64url_encode(test.shared_secret) << "\n"; + sb << "Key\n\t" << td::base64url_encode(test.key) << "\n"; + + std::string data = "hello world"; + sb << "encrypt(\"" << data << "\", X=0) = \n\t" << td::base64url_encode(encrypt(test.key, data, 1, 0)) << "\n"; + sb << "encrypt(\"" << data << "\", X=8) = \n\t" << td::base64url_encode(encrypt(test.key, data, 1, 8)) << "\n"; + return sb; +} + +HandshakeTest pregenerated_test() { + HandshakeTest test; + test.alice.public_key = td::base64url_decode_secure("QlCME5fXLyyQQWeYnBiGAZbmzuD4ayOuADCFgmioOBY").move_as_ok(); + test.alice.private_key = td::base64url_decode_secure("8NZGWKfRCJfiks74RG9_xHmYydarLiRsoq8VcJGPglg").move_as_ok(); + test.bob.public_key = td::base64url_decode_secure("I1yzfmMCZzlI7xwMj1FJ3O3I3_aEUtv6CxbHiDGzr18").move_as_ok(); + test.bob.private_key = td::base64url_decode_secure("YMGoowtnZ99roUM2y5JRwiQrwGaNJ-ZRE5boy-l4aHg").move_as_ok(); + test.shared_secret = td::base64url_decode_secure("0IIvKJuXEwmAp41fYhjUnWqLTYQJ7QeKZKYuCG8mFz8").move_as_ok(); + test.key = td::base64url_decode_secure( + "JHmD-XW9j-13G6KP0tArIhQNDRVbRkKxx0MG0pa2nOgwJHNfiggM0I0RiNIr23-1wRboRtan4WvqOHsxAt_cngYX15_" + "HYe8tJdEwHcmlnXq7LtprigzExaNJS7skfOo2irClj-7EL06-jMrhfwngSJFsak8JFSw8s6R4fwCsr50") + .move_as_ok(); + + + return test; +} + +int main() { + auto test = gen_test(); + run_test(test); + run_test(pregenerated_test()); + LOG(ERROR) << "\n" << pregenerated_test(); +} From 7ef577286113add3f6e427e1f5b8a11e667b31ab Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 10 Feb 2021 18:00:27 +0300 Subject: [PATCH 118/232] Add self to group call participant list before actual join. --- td/telegram/GroupCallManager.cpp | 19 +++++++++++++++---- td/telegram/GroupCallParticipant.h | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 0277f45cc..70a32339e 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1097,10 +1097,10 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup auto my_participant = get_group_call_participant(participants_it->second.get(), my_user_id); for (auto &participant : participants) { on_participant_speaking_in_group_call(input_group_call_id, participant); - if (participant.user_id == my_user_id && - (my_participant == nullptr || my_participant->joined_date < participant.joined_date || - (my_participant->joined_date <= participant.joined_date && - my_participant->audio_source != participant.audio_source))) { + if (participant.user_id == my_user_id && (my_participant == nullptr || my_participant->is_fake || + my_participant->joined_date < participant.joined_date || + (my_participant->joined_date <= participant.joined_date && + my_participant->audio_source != participant.audio_source))) { process_group_call_participant(input_group_call_id, std::move(participant)); } } @@ -1515,6 +1515,17 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, request->query_ref = td_->create_handler(std::move(query_promise)) ->send(input_group_call_id, json_payload, is_muted, generation); + if (group_call->is_inited) { + GroupCallParticipant group_call_participant; + group_call_participant.user_id = td_->contacts_manager_->get_my_id(); + group_call_participant.audio_source = audio_source; + group_call_participant.joined_date = G()->unix_time(); + group_call_participant.is_muted = is_muted; + group_call_participant.can_self_unmute = group_call->can_self_unmute || can_manage_group_call(input_group_call_id); + group_call_participant.is_fake = true; + process_group_call_participant(input_group_call_id, std::move(group_call_participant)); + } + try_load_group_call_administrators(input_group_call_id, group_call->dialog_id); } diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 87f07b45a..ddd010ef9 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -34,6 +34,7 @@ struct GroupCallParticipant { bool can_be_unmuted_only_for_self = false; bool is_min = false; + bool is_fake = false; bool is_just_joined = false; bool is_speaking = false; int32 local_active_date = 0; From 9554a42cd333b055d78fb9b44be4781d1f87969b Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 10 Feb 2021 18:09:42 +0300 Subject: [PATCH 119/232] Add cancel_join_group_call_request function. --- td/telegram/GroupCallManager.cpp | 28 +++++++++++++++++----------- td/telegram/GroupCallManager.h | 2 ++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 70a32339e..016541bd0 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1429,6 +1429,21 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou return diff; } +bool GroupCallManager::cancel_join_group_call_request(InputGroupCallId input_group_call_id) { + auto it = pending_join_requests_.find(input_group_call_id); + if (it == pending_join_requests_.end()) { + return false; + } + + CHECK(it->second != nullptr); + if (!it->second->query_ref.empty()) { + cancel_query(it->second->query_ref); + } + it->second->promise.set_error(Status::Error(200, "Cancelled")); + pending_join_requests_.erase(it); + return true; +} + void GroupCallManager::join_group_call(GroupCallId group_call_id, td_api::object_ptr &&payload, int32 audio_source, bool is_muted, @@ -1450,16 +1465,7 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, } group_call->is_being_left = false; - if (pending_join_requests_.count(input_group_call_id)) { - auto it = pending_join_requests_.find(input_group_call_id); - CHECK(it != pending_join_requests_.end()); - CHECK(it->second != nullptr); - if (!it->second->query_ref.empty()) { - cancel_query(it->second->query_ref); - } - it->second->promise.set_error(Status::Error(200, "Cancelled by another joinGroupCall request")); - pending_join_requests_.erase(it); - } + cancel_join_group_call_request(input_group_call_id); if (payload == nullptr) { return promise.set_error(Status::Error(400, "Payload must be non-empty")); @@ -1941,7 +1947,7 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise group_call->need_rejoin = false; send_update_group_call(group_call, "leave_group_call"); try_clear_group_call_participants(input_group_call_id); - promise.set_value(Unit()); + return promise.set_value(Unit()); } return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); } diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index bc81a205f..eb90cb4b4 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -163,6 +163,8 @@ class GroupCallManager : public Actor { void finish_load_group_call_administrators(InputGroupCallId input_group_call_id, Result &&result); + bool cancel_join_group_call_request(InputGroupCallId input_group_call_id); + bool on_join_group_call_response(InputGroupCallId input_group_call_id, string json_response); void finish_join_group_call(InputGroupCallId input_group_call_id, uint64 generation, Status error); From a80f99f5b8aa773da3038ac4a4f23ec9c12cc9d1 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 10 Feb 2021 18:18:01 +0300 Subject: [PATCH 120/232] Allow to leave being joined calls. --- td/telegram/GroupCallManager.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 016541bd0..c4933311b 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -854,10 +854,7 @@ bool GroupCallManager::need_group_call_participants(InputGroupCallId input_group if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) { return false; } - if (group_call->is_joined || group_call->need_rejoin) { - return true; - } - if (pending_join_requests_.count(input_group_call_id) != 0) { + if (group_call->is_joined || group_call->need_rejoin || pending_join_requests_.count(input_group_call_id) != 0) { return true; } return false; @@ -1943,6 +1940,10 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise auto *group_call = get_group_call(input_group_call_id); if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + if (cancel_join_group_call_request(input_group_call_id)) { + try_clear_group_call_participants(input_group_call_id); + return promise.set_value(Unit()); + } if (group_call->need_rejoin) { group_call->need_rejoin = false; send_update_group_call(group_call, "leave_group_call"); @@ -2013,11 +2014,14 @@ void GroupCallManager::on_update_group_call(tl_object_ptrcontacts_manager_->get_my_id()); + + auto participants_it = group_call_participants_.find(input_group_call_id); + if (participants_it == group_call_participants_.end()) { return; } From e7a686cc967dbdefacf2ee3900dc17ca6f47fb02 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 10 Feb 2021 19:27:30 +0300 Subject: [PATCH 121/232] Immediately change is_joined state to false after leaveGroupCall. --- td/telegram/GroupCallManager.cpp | 35 ++++++++++++++++++++++++-------- td/telegram/GroupCallManager.h | 2 +- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index c4933311b..fd98d1fda 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1426,10 +1426,10 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou return diff; } -bool GroupCallManager::cancel_join_group_call_request(InputGroupCallId input_group_call_id) { +int32 GroupCallManager::cancel_join_group_call_request(InputGroupCallId input_group_call_id) { auto it = pending_join_requests_.find(input_group_call_id); if (it == pending_join_requests_.end()) { - return false; + return 0; } CHECK(it->second != nullptr); @@ -1437,8 +1437,9 @@ bool GroupCallManager::cancel_join_group_call_request(InputGroupCallId input_gro cancel_query(it->second->query_ref); } it->second->promise.set_error(Status::Error(200, "Cancelled")); + auto audio_source = it->second->audio_source; pending_join_requests_.erase(it); - return true; + return audio_source; } void GroupCallManager::join_group_call(GroupCallId group_call_id, @@ -1449,7 +1450,7 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr); - if (group_call->is_joined) { + if (group_call->is_joined && !group_call->is_being_left) { CHECK(group_call->is_inited); return promise.set_error(Status::Error(400, "Group call is already joined")); } @@ -1460,10 +1461,12 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, group_call->need_rejoin = false; send_update_group_call(group_call, "join_group_call"); } - group_call->is_being_left = false; cancel_join_group_call_request(input_group_call_id); + if (audio_source == 0) { + return promise.set_error(Status::Error(400, "Audio source must be non-zero")); + } if (payload == nullptr) { return promise.set_error(Status::Error(400, "Payload must be non-empty")); } @@ -1488,6 +1491,13 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, } } + if (group_call->is_being_left) { + group_call->is_being_left = false; + if (group_call->is_inited) { + send_update_group_call(group_call, "join_group_call"); + } + } + auto json_payload = json_encode(json_object([&payload, audio_source](auto &o) { o("ufrag", payload->ufrag_); o("pwd", payload->pwd_); @@ -1939,8 +1949,9 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); auto *group_call = get_group_call(input_group_call_id); - if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { - if (cancel_join_group_call_request(input_group_call_id)) { + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined || + group_call->is_being_left) { + if (cancel_join_group_call_request(input_group_call_id) != 0) { try_clear_group_call_participants(input_group_call_id); return promise.set_value(Unit()); } @@ -1952,8 +1963,13 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise } return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); } - auto audio_source = group_call->audio_source; + auto audio_source = cancel_join_group_call_request(input_group_call_id); + if (audio_source == 0) { + audio_source = group_call->audio_source; + } group_call->is_being_left = true; + group_call->need_rejoin = false; + send_update_group_call(group_call, "leave_group_call"); auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, audio_source, promise = std::move(promise)](Result &&result) mutable { @@ -2422,8 +2438,9 @@ tl_object_ptr GroupCallManager::get_group_call_object( CHECK(group_call != nullptr); CHECK(group_call->is_inited); + bool is_joined = group_call->is_joined && !group_call->is_being_left; return td_api::make_object( - group_call->group_call_id.get(), group_call->is_active, group_call->is_joined, group_call->need_rejoin, + group_call->group_call_id.get(), group_call->is_active, is_joined, group_call->need_rejoin, group_call->can_self_unmute, group_call->can_be_managed, group_call->participant_count, group_call->loaded_all_participants, std::move(recent_speakers), group_call->mute_new_participants, group_call->allowed_change_mute_new_participants, group_call->duration); diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index eb90cb4b4..b70f5d620 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -163,7 +163,7 @@ class GroupCallManager : public Actor { void finish_load_group_call_administrators(InputGroupCallId input_group_call_id, Result &&result); - bool cancel_join_group_call_request(InputGroupCallId input_group_call_id); + int32 cancel_join_group_call_request(InputGroupCallId input_group_call_id); bool on_join_group_call_response(InputGroupCallId input_group_call_id, string json_response); From 1c52ee815ea39bacc3cf61c095121c70ed7c4258 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 11 Feb 2021 13:43:59 +0300 Subject: [PATCH 122/232] Improve groupCall.can_unmute_self synchronization. --- td/telegram/GroupCallManager.cpp | 35 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index fd98d1fda..3767a77ba 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1364,10 +1364,12 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou if (participant.user_id == td_->contacts_manager_->get_my_id()) { auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); - if (group_call->is_joined && group_call->is_active && participant.audio_source == group_call->audio_source && - participant.is_muted && group_call->can_self_unmute != participant.can_self_unmute) { - group_call->can_self_unmute = participant.can_self_unmute; - send_update_group_call(group_call, "process_group_call_participant"); + if (group_call->is_joined && group_call->is_active) { + auto can_self_unmute = !participant.is_muted || participant.can_self_unmute; + if (can_self_unmute != group_call->can_self_unmute) { + group_call->can_self_unmute = can_self_unmute; + send_update_group_call(group_call, "process_group_call_participant"); + } } } @@ -1534,6 +1536,8 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, group_call_participant.audio_source = audio_source; group_call_participant.joined_date = G()->unix_time(); group_call_participant.is_muted = is_muted; + // if can_self_unmute has never been inited from self-participant, + // it contains reasonable default "!call.mute_new_participants || call.can_be_managed" group_call_participant.can_self_unmute = group_call->can_self_unmute || can_manage_group_call(input_group_call_id); group_call_participant.is_fake = true; process_group_call_participant(input_group_call_id, std::move(group_call_participant)); @@ -1997,7 +2001,6 @@ void GroupCallManager::on_group_call_left_impl(GroupCall *group_call, bool need_ group_call->need_rejoin = need_rejoin && !group_call->is_being_left; group_call->is_being_left = false; group_call->is_speaking = false; - group_call->can_self_unmute = false; group_call->can_be_managed = false; group_call->joined_date = 0; group_call->audio_source = 0; @@ -2105,7 +2108,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrgroup_call_id; call.dialog_id = dialog_id.is_valid() ? dialog_id : group_call->dialog_id; call.can_be_managed = call.is_active && can_manage_group_calls(call.dialog_id).is_ok(); - call.can_self_unmute = (call.is_active && !call.mute_new_participants) || call.can_be_managed; + call.can_self_unmute = call.is_active && (!call.mute_new_participants || call.can_be_managed); if (!group_call->dialog_id.is_valid()) { group_call->dialog_id = dialog_id; } @@ -2116,12 +2119,12 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrneed_rejoin; call.is_being_left = group_call->is_being_left; call.is_speaking = group_call->is_speaking; - call.can_self_unmute = group_call->can_self_unmute; call.syncing_participants = group_call->syncing_participants; call.loaded_all_participants = group_call->loaded_all_participants; call.audio_source = group_call->audio_source; *group_call = std::move(call); + need_update = true; if (need_group_call_participants(input_group_call_id, group_call)) { // init version group_call->version = call.version; @@ -2132,12 +2135,11 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrversion = -1; } - need_update = true; } else { if (!group_call->is_active) { // never update ended calls } else if (!call.is_active) { - // always update to an ended call, droping also is_joined and is_speaking flags + // always update to an ended call, droping also is_joined, is_speaking and other flags *group_call = std::move(call); need_update = true; } else { @@ -2165,10 +2167,6 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrcan_self_unmute != call.can_self_unmute) { - group_call->can_self_unmute = call.can_self_unmute; - need_update = true; - } group_call->version = call.version; if (process_pending_group_call_participant_updates(input_group_call_id)) { need_update = false; @@ -2439,11 +2437,12 @@ tl_object_ptr GroupCallManager::get_group_call_object( CHECK(group_call->is_inited); bool is_joined = group_call->is_joined && !group_call->is_being_left; - return td_api::make_object( - group_call->group_call_id.get(), group_call->is_active, is_joined, group_call->need_rejoin, - group_call->can_self_unmute, group_call->can_be_managed, group_call->participant_count, - group_call->loaded_all_participants, std::move(recent_speakers), group_call->mute_new_participants, - group_call->allowed_change_mute_new_participants, group_call->duration); + bool can_self_unmute = is_joined && group_call->can_self_unmute; + return td_api::make_object(group_call->group_call_id.get(), group_call->is_active, is_joined, + group_call->need_rejoin, can_self_unmute, group_call->can_be_managed, + group_call->participant_count, group_call->loaded_all_participants, + std::move(recent_speakers), group_call->mute_new_participants, + group_call->allowed_change_mute_new_participants, group_call->duration); } tl_object_ptr GroupCallManager::get_update_group_call_object( From 631dde0939211a1c122f20f45eafbe99133d1733 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 11 Feb 2021 20:34:24 +0300 Subject: [PATCH 123/232] Simpify handling of groupCallParticipant.is_muted flags. --- td/telegram/GroupCallManager.cpp | 7 ++-- td/telegram/GroupCallParticipant.cpp | 56 +++++++++++++++++----------- td/telegram/GroupCallParticipant.h | 8 ++-- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 3767a77ba..03e1a61b4 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1365,7 +1365,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); if (group_call->is_joined && group_call->is_active) { - auto can_self_unmute = !participant.is_muted || participant.can_self_unmute; + auto can_self_unmute = !participant.server_is_muted_by_admin; if (can_self_unmute != group_call->can_self_unmute) { group_call->can_self_unmute = can_self_unmute; send_update_group_call(group_call, "process_group_call_participant"); @@ -1535,10 +1535,11 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, group_call_participant.user_id = td_->contacts_manager_->get_my_id(); group_call_participant.audio_source = audio_source; group_call_participant.joined_date = G()->unix_time(); - group_call_participant.is_muted = is_muted; // if can_self_unmute has never been inited from self-participant, // it contains reasonable default "!call.mute_new_participants || call.can_be_managed" - group_call_participant.can_self_unmute = group_call->can_self_unmute || can_manage_group_call(input_group_call_id); + group_call_participant.server_is_muted_by_admin = + !group_call->can_self_unmute && !can_manage_group_call(input_group_call_id); + group_call_participant.server_is_muted_by_themselves = is_muted && !group_call_participant.server_is_muted_by_admin; group_call_participant.is_fake = true; process_group_call_participant(input_group_call_id, std::move(group_call_participant)); } diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index 1f95f005f..2fa88bedd 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -16,9 +16,9 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptruser_id_); audio_source = participant->source_; - is_muted = participant->muted_; - can_self_unmute = participant->can_self_unmute_; - is_muted_only_for_self = participant->muted_by_you_; + server_is_muted_by_themselves = participant->can_self_unmute_; + server_is_muted_by_admin = participant->muted_ && !participant->can_self_unmute_; + server_is_muted_locally = participant->muted_by_you_; if ((participant->flags_ & telegram_api::groupCallParticipant::VOLUME_MASK) != 0) { volume_level = participant->volume_; if (volume_level < MIN_VOLUME_LEVEL || volume_level > MAX_VOLUME_LEVEL) { @@ -46,6 +46,10 @@ bool GroupCallParticipant::is_versioned_update(const tl_object_ptrjust_joined_ || participant->left_ || participant->versioned_; } +bool GroupCallParticipant::get_is_muted() const { + return server_is_muted_by_themselves || server_is_muted_by_admin || server_is_muted_locally; +} + int32 GroupCallParticipant::get_volume_level() const { return pending_volume_level != 0 ? pending_volume_level : volume_level; } @@ -62,38 +66,45 @@ void GroupCallParticipant::update_from(const GroupCallParticipant &old_participa local_active_date = old_participant.local_active_date; is_speaking = old_participant.is_speaking; if (is_min) { - is_muted_only_for_self = old_participant.is_muted_only_for_self; - } - if (old_participant.is_volume_level_local && !is_volume_level_local) { - is_volume_level_local = true; - volume_level = old_participant.volume_level; + server_is_muted_locally = old_participant.server_is_muted_locally; + + if (old_participant.is_volume_level_local && !is_volume_level_local) { + is_volume_level_local = true; + volume_level = old_participant.volume_level; + } } + is_min = false; + pending_volume_level = old_participant.pending_volume_level; pending_volume_level_generation = old_participant.pending_volume_level_generation; - is_min = false; } bool GroupCallParticipant::update_can_be_muted(bool can_manage, bool is_self, bool is_admin) { + bool is_muted = get_is_muted(); + bool is_muted_by_admin = server_is_muted_by_admin; + bool is_muted_by_themselves = server_is_muted_by_themselves; + bool is_muted_locally = server_is_muted_locally; + bool new_can_be_muted_for_all_users = false; bool new_can_be_unmuted_for_all_users = false; - bool new_can_be_muted_only_for_self = !can_manage && !is_muted_only_for_self; - bool new_can_be_unmuted_only_for_self = !can_manage && is_muted_only_for_self; + bool new_can_be_muted_only_for_self = !can_manage && !is_muted_locally; + bool new_can_be_unmuted_only_for_self = !can_manage && is_muted_locally; if (is_self) { - // current user can be muted if !is_muted; after that is_muted && can_self_unmute - // current user can be unmuted if is_muted && can_self_unmute; after that !is_muted + // current user can be muted if !is_muted; after that is_muted_by_themselves + // current user can be unmuted if is_muted_by_themselves; after that !is_muted new_can_be_muted_for_all_users = !is_muted; - new_can_be_unmuted_for_all_users = is_muted && can_self_unmute; + new_can_be_unmuted_for_all_users = is_muted_by_themselves; new_can_be_muted_only_for_self = false; new_can_be_unmuted_only_for_self = false; } else if (is_admin) { - // admin user can be muted if can_manage && !is_muted; after that is_muted && can_self_unmute + // admin user can be muted if can_manage && !is_muted; after that is_muted_by_themselves // admin user can't be unmuted new_can_be_muted_for_all_users = can_manage && !is_muted; } else { - // other users can be muted if can_manage; after that is_muted && !can_self_unmute - // other users can be unmuted if can_manage && is_muted && !can_self_unmute; after that is_muted && can_self_unmute - new_can_be_muted_for_all_users = can_manage && (!is_muted || can_self_unmute); - new_can_be_unmuted_for_all_users = can_manage && is_muted && !can_self_unmute; + // other users can be muted if can_manage && !is_muted_by_admin; after that is_muted_by_admin + // other users can be unmuted if can_manage && is_muted_by_admin; after that is_muted_by_themselves + new_can_be_muted_for_all_users = can_manage && !is_muted_by_admin; + new_can_be_unmuted_for_all_users = can_manage && is_muted_by_admin; } if (new_can_be_muted_for_all_users != can_be_muted_for_all_users || new_can_be_unmuted_for_all_users != can_be_unmuted_for_all_users || @@ -117,7 +128,7 @@ td_api::object_ptr GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), audio_source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, - can_be_unmuted_only_for_self, is_muted, can_self_unmute, get_volume_level(), order); + can_be_unmuted_only_for_self, get_is_muted(), server_is_muted_by_themselves, get_volume_level(), order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { @@ -125,8 +136,9 @@ bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs lhs.can_be_muted_for_all_users == rhs.can_be_muted_for_all_users && lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && - lhs.can_be_unmuted_only_for_self == rhs.can_be_unmuted_only_for_self && lhs.is_muted == rhs.is_muted && - lhs.can_self_unmute == rhs.can_self_unmute && lhs.is_speaking == rhs.is_speaking && + lhs.can_be_unmuted_only_for_self == rhs.can_be_unmuted_only_for_self && + lhs.get_is_muted() == rhs.get_is_muted() && + lhs.server_is_muted_by_themselves == rhs.server_is_muted_by_themselves && lhs.is_speaking == rhs.is_speaking && lhs.get_volume_level() == rhs.get_volume_level() && lhs.order == rhs.order; } diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index ddd010ef9..794642c68 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -24,9 +24,9 @@ struct GroupCallParticipant { int32 active_date = 0; int32 volume_level = 10000; bool is_volume_level_local = false; - bool is_muted = false; - bool can_self_unmute = false; - bool is_muted_only_for_self = false; + bool server_is_muted_by_themselves = false; + bool server_is_muted_by_admin = false; + bool server_is_muted_locally = false; bool can_be_muted_for_all_users = false; bool can_be_unmuted_for_all_users = false; @@ -64,6 +64,8 @@ struct GroupCallParticipant { return user_id.is_valid(); } + bool get_is_muted() const; + int32 get_volume_level() const; td_api::object_ptr get_group_call_participant_object( From 1412e833635eddf59696abadabd1884eaf03133b Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 11 Feb 2021 20:47:46 +0300 Subject: [PATCH 124/232] Immrove groupCallParticipant mute-related flags. --- td/generate/scheme/td_api.tl | 11 ++++++----- td/generate/scheme/td_api.tlo | Bin 194220 -> 194288 bytes td/telegram/GroupCallParticipant.cpp | 6 ++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index aadf6ff40..f66f151ff 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2128,13 +2128,14 @@ groupCallJoinResponse payload:groupCallPayload candidates:vectorWV=J_ zWMq>Q^Wu|I<8w<(Qd8p7@{8h=ON)w9^Gf1Ni>GBW$_UFt6_n<|l}wkP#i+_6mRTG> tdEX(4$qNpNNJs)XaCtN%Cf6J;0XwPr%i--`4m19+1oO6k_{+HG2LR4wOXUCn delta 141 zcmezHmV3=x?hQG@ES^R=A2$~WH>_g=u|(3RU--)?!p*<{;@vzZ GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), audio_source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, - can_be_unmuted_only_for_self, get_is_muted(), server_is_muted_by_themselves, get_volume_level(), order); + can_be_unmuted_only_for_self, server_is_muted_by_admin, server_is_muted_locally, server_is_muted_by_themselves, + get_volume_level(), order); } bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { @@ -137,7 +138,8 @@ bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && lhs.can_be_unmuted_only_for_self == rhs.can_be_unmuted_only_for_self && - lhs.get_is_muted() == rhs.get_is_muted() && + lhs.server_is_muted_by_admin == rhs.server_is_muted_by_admin && + lhs.server_is_muted_locally == rhs.server_is_muted_locally && lhs.server_is_muted_by_themselves == rhs.server_is_muted_by_themselves && lhs.is_speaking == rhs.is_speaking && lhs.get_volume_level() == rhs.get_volume_level() && lhs.order == rhs.order; } From ca64ad144a1ac5379a3e3357563442bd1a05601f Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 15:41:20 +0300 Subject: [PATCH 125/232] Immediately send updateGroupCallParticipant when is_muted changes. --- td/telegram/GroupCallManager.cpp | 71 ++++++++++++++++- td/telegram/GroupCallManager.h | 5 ++ td/telegram/GroupCallParticipant.cpp | 110 +++++++++++++++++++++++---- td/telegram/GroupCallParticipant.h | 16 +++- 4 files changed, 183 insertions(+), 19 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 03e1a61b4..20293cf5c 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1365,7 +1365,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); if (group_call->is_joined && group_call->is_active) { - auto can_self_unmute = !participant.server_is_muted_by_admin; + auto can_self_unmute = !participant.get_is_muted_by_admin(); if (can_self_unmute != group_call->can_self_unmute) { group_call->can_self_unmute = can_self_unmute; send_update_group_call(group_call, "process_group_call_participant"); @@ -1841,11 +1841,73 @@ void GroupCallManager::toggle_group_call_participant_is_muted(GroupCallId group_ return promise.set_error(Status::Error(400, "Have no access to the user")); } - if (user_id == td_->contacts_manager_->get_my_id() && !is_muted && !group_call->can_self_unmute) { - return promise.set_error(Status::Error(400, "Can't unmute self")); + auto participants = add_group_call_participants(input_group_call_id); + auto participant = get_group_call_participant(participants, user_id); + if (participant == nullptr) { + return promise.set_error(Status::Error(400, "Can't find group call participant")); } - td_->create_handler(std::move(promise))->send(input_group_call_id, user_id, is_muted, 0); + bool is_self = user_id == td_->contacts_manager_->get_my_id(); + bool can_manage = can_manage_group_call(input_group_call_id); + bool is_admin = td::contains(participants->administrator_user_ids, user_id); + + auto participant_copy = *participant; + if (!participant_copy.set_pending_is_muted(is_muted, can_manage, is_self, is_admin)) { + return promise.set_error(Status::Error(400, PSLICE() << "Can't " << (is_muted ? "" : "un") << "mute user")); + } + if (participant_copy == *participant) { + return promise.set_value(Unit()); + } + *participant = std::move(participant_copy); + + participant->pending_is_muted_generation = ++toggle_is_muted_generation_; + if (participant->order != 0) { + send_update_group_call_participant(input_group_call_id, *participant); + } + + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, user_id, + generation = participant->pending_is_muted_generation, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &GroupCallManager::on_toggle_group_call_participant_is_muted, input_group_call_id, user_id, + generation, std::move(promise)); + } + }); + td_->create_handler(std::move(query_promise)) + ->send(input_group_call_id, user_id, is_muted, 0); +} + +void GroupCallManager::on_toggle_group_call_participant_is_muted(InputGroupCallId input_group_call_id, UserId user_id, + uint64 generation, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_value(Unit()); + } + + auto *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + return promise.set_value(Unit()); + } + + auto participant = get_group_call_participant(add_group_call_participants(input_group_call_id), user_id); + if (participant == nullptr || participant->pending_is_muted_generation != generation) { + return promise.set_value(Unit()); + } + + CHECK(participant->have_pending_is_muted); + if (participant->server_is_muted_by_themselves != participant->pending_is_muted_by_themselves || + participant->server_is_muted_by_admin != participant->pending_is_muted_by_admin || + participant->server_is_muted_locally != participant->pending_is_muted_locally) { + LOG(ERROR) << "Failed to mute/unmute " << user_id << " in " << input_group_call_id; + participant->have_pending_is_muted = false; + if (participant->order != 0) { + send_update_group_call_participant(input_group_call_id, *participant); + } + } else { + participant->have_pending_is_muted = false; + } + promise.set_value(Unit()); } void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group_call_id, UserId user_id, @@ -1912,6 +1974,7 @@ void GroupCallManager::on_set_group_call_participant_volume_level(InputGroupCall return promise.set_value(Unit()); } + CHECK(participant->pending_volume_level != 0); if (participant->volume_level != participant->pending_volume_level) { LOG(ERROR) << "Failed to set volume level of " << user_id << " in " << input_group_call_id; participant->pending_volume_level = 0; diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index b70f5d620..a90633fdb 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -174,6 +174,9 @@ class GroupCallManager : public Actor { static GroupCallParticipant *get_group_call_participant(GroupCallParticipants *group_call_participants, UserId user_id); + void on_toggle_group_call_participant_is_muted(InputGroupCallId input_group_call_id, UserId user_id, + uint64 generation, Promise &&promise); + void on_set_group_call_participant_volume_level(InputGroupCallId input_group_call_id, UserId user_id, uint64 generation, Promise &&promise); @@ -243,6 +246,8 @@ class GroupCallManager : public Actor { uint64 set_volume_level_generation_ = 0; + uint64 toggle_is_muted_generation_ = 0; + MultiTimeout check_group_call_is_joined_timeout_{"CheckGroupCallIsJoinedTimeout"}; MultiTimeout pending_send_speaking_action_timeout_{"PendingSendSpeakingActionTimeout"}; MultiTimeout recent_speaker_update_timeout_{"RecentSpeakerUpdateTimeout"}; diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index 660bd0580..6403f248e 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -46,8 +46,20 @@ bool GroupCallParticipant::is_versioned_update(const tl_object_ptrjust_joined_ || participant->left_ || participant->versioned_; } -bool GroupCallParticipant::get_is_muted() const { - return server_is_muted_by_themselves || server_is_muted_by_admin || server_is_muted_locally; +bool GroupCallParticipant::get_is_muted_by_themselves() const { + return have_pending_is_muted ? pending_is_muted_by_themselves : server_is_muted_by_themselves; +} + +bool GroupCallParticipant::get_is_muted_by_admin() const { + return have_pending_is_muted ? pending_is_muted_by_admin : server_is_muted_by_admin; +} + +bool GroupCallParticipant::get_is_muted_locally() const { + return have_pending_is_muted ? pending_is_muted_locally : server_is_muted_locally; +} + +bool GroupCallParticipant::get_is_muted_for_all_users() const { + return get_is_muted_by_admin() || get_is_muted_by_themselves(); } int32 GroupCallParticipant::get_volume_level() const { @@ -77,35 +89,45 @@ void GroupCallParticipant::update_from(const GroupCallParticipant &old_participa pending_volume_level = old_participant.pending_volume_level; pending_volume_level_generation = old_participant.pending_volume_level_generation; + + have_pending_is_muted = old_participant.have_pending_is_muted; + pending_is_muted_by_themselves = old_participant.pending_is_muted_by_themselves; + pending_is_muted_by_admin = old_participant.pending_is_muted_by_admin; + pending_is_muted_locally = old_participant.pending_is_muted_locally; + pending_is_muted_generation = old_participant.pending_is_muted_generation; } bool GroupCallParticipant::update_can_be_muted(bool can_manage, bool is_self, bool is_admin) { - bool is_muted = get_is_muted(); - bool is_muted_by_admin = server_is_muted_by_admin; - bool is_muted_by_themselves = server_is_muted_by_themselves; - bool is_muted_locally = server_is_muted_locally; + bool is_muted_by_admin = get_is_muted_by_admin(); + bool is_muted_by_themselves = get_is_muted_by_themselves(); + bool is_muted_locally = get_is_muted_locally(); + + CHECK(!is_muted_by_admin || !is_muted_by_themselves); bool new_can_be_muted_for_all_users = false; bool new_can_be_unmuted_for_all_users = false; bool new_can_be_muted_only_for_self = !can_manage && !is_muted_locally; bool new_can_be_unmuted_only_for_self = !can_manage && is_muted_locally; if (is_self) { - // current user can be muted if !is_muted; after that is_muted_by_themselves + // current user can be muted if !is_muted_by_themselves && !is_muted_by_admin; after that is_muted_by_themselves // current user can be unmuted if is_muted_by_themselves; after that !is_muted - new_can_be_muted_for_all_users = !is_muted; + new_can_be_muted_for_all_users = !is_muted_by_themselves && !is_muted_by_admin; new_can_be_unmuted_for_all_users = is_muted_by_themselves; new_can_be_muted_only_for_self = false; new_can_be_unmuted_only_for_self = false; } else if (is_admin) { - // admin user can be muted if can_manage && !is_muted; after that is_muted_by_themselves + // admin user can be muted if can_manage && !is_muted_by_themselves; after that is_muted_by_themselves // admin user can't be unmuted - new_can_be_muted_for_all_users = can_manage && !is_muted; + new_can_be_muted_for_all_users = can_manage && !is_muted_by_themselves; } else { // other users can be muted if can_manage && !is_muted_by_admin; after that is_muted_by_admin // other users can be unmuted if can_manage && is_muted_by_admin; after that is_muted_by_themselves new_can_be_muted_for_all_users = can_manage && !is_muted_by_admin; new_can_be_unmuted_for_all_users = can_manage && is_muted_by_admin; } + CHECK(static_cast(new_can_be_muted_for_all_users) + static_cast(new_can_be_unmuted_for_all_users) + + static_cast(new_can_be_muted_only_for_self) + static_cast(new_can_be_unmuted_only_for_self) <= + 1); if (new_can_be_muted_for_all_users != can_be_muted_for_all_users || new_can_be_unmuted_for_all_users != can_be_unmuted_for_all_users || new_can_be_muted_only_for_self != can_be_muted_only_for_self || @@ -119,6 +141,66 @@ bool GroupCallParticipant::update_can_be_muted(bool can_manage, bool is_self, bo return false; } +bool GroupCallParticipant::set_pending_is_muted(bool is_muted, bool can_manage, bool is_self, bool is_admin) { + update_can_be_muted(can_manage, is_self, is_admin); + if (is_muted) { + if (!can_be_muted_for_all_users && !can_be_muted_only_for_self) { + return false; + } + CHECK(!can_be_muted_for_all_users || !can_be_muted_only_for_self); + } else { + if (!can_be_unmuted_for_all_users && !can_be_unmuted_only_for_self) { + return false; + } + CHECK(!can_be_unmuted_for_all_users || !can_be_unmuted_only_for_self); + } + + if (is_self) { + pending_is_muted_by_themselves = is_muted; + pending_is_muted_by_admin = false; + pending_is_muted_locally = false; + } else { + pending_is_muted_by_themselves = get_is_muted_by_themselves(); + pending_is_muted_by_admin = get_is_muted_by_admin(); + pending_is_muted_locally = get_is_muted_locally(); + if (is_muted) { + if (can_be_muted_only_for_self) { + // local mute + pending_is_muted_locally = true; + } else { + // admin mute + CHECK(can_be_muted_for_all_users); + CHECK(can_manage); + if (is_admin) { + CHECK(!pending_is_muted_by_themselves); + pending_is_muted_by_admin = false; + pending_is_muted_by_themselves = true; + } else { + CHECK(!pending_is_muted_by_admin); + pending_is_muted_by_admin = true; + pending_is_muted_by_themselves = false; + } + } + } else { + if (can_be_unmuted_only_for_self) { + // local unmute + pending_is_muted_locally = false; + } else { + // admin unmute + CHECK(can_be_unmuted_for_all_users); + CHECK(can_manage); + CHECK(!is_admin); + pending_is_muted_by_admin = false; + pending_is_muted_by_themselves = true; + } + } + } + + have_pending_is_muted = true; + update_can_be_muted(can_manage, is_self, is_admin); + return true; +} + td_api::object_ptr GroupCallParticipant::get_group_call_participant_object( ContactsManager *contacts_manager) const { if (!is_valid()) { @@ -128,7 +210,7 @@ td_api::object_ptr GroupCallParticipant::get_group return td_api::make_object( contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), audio_source, is_speaking, can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self, - can_be_unmuted_only_for_self, server_is_muted_by_admin, server_is_muted_locally, server_is_muted_by_themselves, + can_be_unmuted_only_for_self, get_is_muted_for_all_users(), get_is_muted_locally(), get_is_muted_by_themselves(), get_volume_level(), order); } @@ -138,9 +220,9 @@ bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs lhs.can_be_unmuted_for_all_users == rhs.can_be_unmuted_for_all_users && lhs.can_be_muted_only_for_self == rhs.can_be_muted_only_for_self && lhs.can_be_unmuted_only_for_self == rhs.can_be_unmuted_only_for_self && - lhs.server_is_muted_by_admin == rhs.server_is_muted_by_admin && - lhs.server_is_muted_locally == rhs.server_is_muted_locally && - lhs.server_is_muted_by_themselves == rhs.server_is_muted_by_themselves && lhs.is_speaking == rhs.is_speaking && + lhs.get_is_muted_for_all_users() == rhs.get_is_muted_for_all_users() && + lhs.get_is_muted_locally() == rhs.get_is_muted_locally() && + lhs.get_is_muted_by_themselves() == rhs.get_is_muted_by_themselves() && lhs.is_speaking == rhs.is_speaking && lhs.get_volume_level() == rhs.get_volume_level() && lhs.order == rhs.order; } diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index 794642c68..3d665d8d4 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -43,6 +43,12 @@ struct GroupCallParticipant { int32 pending_volume_level = 0; uint64 pending_volume_level_generation = 0; + bool have_pending_is_muted = false; + bool pending_is_muted_by_themselves = false; + bool pending_is_muted_by_admin = false; + bool pending_is_muted_locally = false; + uint64 pending_is_muted_generation = 0; + static constexpr int32 MIN_VOLUME_LEVEL = 1; static constexpr int32 MAX_VOLUME_LEVEL = 20000; @@ -56,6 +62,8 @@ struct GroupCallParticipant { bool update_can_be_muted(bool can_manage, bool is_self, bool is_admin); + bool set_pending_is_muted(bool is_muted, bool can_manage, bool is_self, bool is_admin); + int64 get_real_order() const { return (static_cast(max(active_date, local_active_date)) << 32) + joined_date; } @@ -64,7 +72,13 @@ struct GroupCallParticipant { return user_id.is_valid(); } - bool get_is_muted() const; + bool get_is_muted_by_themselves() const; + + bool get_is_muted_by_admin() const; + + bool get_is_muted_locally() const; + + bool get_is_muted_for_all_users() const; int32 get_volume_level() const; From 18bbb47ecdb74854b24a0fff34e4c5b770f66af8 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 15:50:01 +0300 Subject: [PATCH 126/232] Fix groupCall version updating and dropping. --- td/telegram/GroupCallManager.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 20293cf5c..e854ae804 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -2068,8 +2068,6 @@ void GroupCallManager::on_group_call_left_impl(GroupCall *group_call, bool need_ group_call->can_be_managed = false; group_call->joined_date = 0; group_call->audio_source = 0; - group_call->loaded_all_participants = false; - group_call->version = -1; check_group_call_is_joined_timeout_.cancel_timeout(group_call->group_call_id.get()); try_clear_group_call_participants(get_input_group_call_id(group_call->group_call_id).ok()); } @@ -2191,7 +2189,6 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrversion = call.version; if (process_pending_group_call_participant_updates(input_group_call_id)) { need_update = false; } @@ -2229,7 +2226,8 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrparticipant_count = call.participant_count; need_update = true; } - if (need_group_call_participants(input_group_call_id, group_call) && !join_params.empty()) { + if (need_group_call_participants(input_group_call_id, group_call) && !join_params.empty() && + group_call->version == -1) { LOG(INFO) << "Init " << call.group_call_id << " version to " << call.version; group_call->version = call.version; if (process_pending_group_call_participant_updates(input_group_call_id)) { From ba91f7f6ced8e961ca3fdccfb2a0f693c223ddda Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 15:50:52 +0300 Subject: [PATCH 127/232] Don't send speaking typings more often than once in 4 seconds. --- td/telegram/GroupCallManager.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index e854ae804..c3d853c26 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1821,8 +1821,6 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_ group_call->is_speaking = is_speaking; if (is_speaking) { pending_send_speaking_action_timeout_.add_timeout_in(group_call_id.get(), 0.0); - } else { - pending_send_speaking_action_timeout_.cancel_timeout(group_call_id.get()); } } From f21e3154568674d0e3d06ab95ac8eea40ec3b212 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 16:46:00 +0300 Subject: [PATCH 128/232] Remove need_rejoin for inaccessible voice chats. --- td/telegram/GroupCallManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index c3d853c26..c0824ec4e 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -2061,6 +2061,10 @@ void GroupCallManager::on_group_call_left_impl(GroupCall *group_call, bool need_ CHECK(group_call != nullptr && group_call->is_inited && group_call->is_joined); group_call->is_joined = false; group_call->need_rejoin = need_rejoin && !group_call->is_being_left; + if (need_rejoin && group_call->dialog_id.is_valid() && + !td_->messages_manager_->have_input_peer(group_call->dialog_id, AccessRights::Read)) { + need_rejoin = false; + } group_call->is_being_left = false; group_call->is_speaking = false; group_call->can_be_managed = false; From 22289fe68bb4c025b767d35604c9c89a3a1e846c Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 16:53:40 +0300 Subject: [PATCH 129/232] Move struct definition back to header to fix SplitSource-build. --- td/telegram/ContactsManager.cpp | 340 ------------------------------- td/telegram/ContactsManager.h | 349 +++++++++++++++++++++++++++++++- 2 files changed, 340 insertions(+), 349 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index f3bd2725a..448f4caf5 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3136,346 +3136,6 @@ class ContactsManager::UploadProfilePhotoCallback : public FileManager::UploadCa } }; -struct ContactsManager::User { - string first_name; - string last_name; - string username; - string phone_number; - int64 access_hash = -1; - - ProfilePhoto photo; - - vector restriction_reasons; - string inline_query_placeholder; - int32 bot_info_version = -1; - - int32 was_online = 0; - int32 local_was_online = 0; - - string language_code; - - std::unordered_set photo_ids; - - std::unordered_map online_member_dialogs; // id -> time - - static constexpr uint32 CACHE_VERSION = 4; - uint32 cache_version = 0; - - bool is_min_access_hash = true; - bool is_received = false; - bool is_verified = false; - bool is_support = false; - bool is_deleted = true; - bool is_bot = true; - bool can_join_groups = true; - bool can_read_all_group_messages = true; - bool is_inline_bot = false; - bool need_location_bot = false; - bool is_scam = false; - bool is_fake = false; - bool is_contact = false; - bool is_mutual_contact = false; - bool need_apply_min_photo = false; - - bool is_photo_inited = false; - - bool is_repaired = false; // whether cached value is rechecked - - bool is_name_changed = true; - bool is_username_changed = true; - bool is_photo_changed = true; - bool is_is_contact_changed = true; - bool is_is_deleted_changed = true; - bool is_default_permissions_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - bool is_status_changed = true; - bool is_online_status_changed = true; // whether online/offline has changed - bool is_update_user_sent = false; - - bool is_saved = false; // is current user version being saved/is saved to the database - bool is_being_saved = false; // is current user being saved to the database - bool is_status_saved = false; // is current user status being saved/is saved to the database - - bool is_received_from_server = false; // true, if the user was received from the server and not the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -struct ContactsManager::BotInfo { - int32 version = -1; - string description; - vector> commands; - bool is_changed = true; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -// do not forget to update drop_user_full and on_get_user_full -struct ContactsManager::UserFull { - Photo photo; - - string about; - - int32 common_chat_count = 0; - - bool is_blocked = false; - bool can_be_called = false; - bool supports_video_calls = false; - bool has_private_calls = false; - bool can_pin_messages = true; - bool need_phone_number_privacy_exception = false; - - bool is_common_chat_count_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_send_update = true; // have new changes that need only to be sent to the client - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - double expires_at = 0.0; - - bool is_expired() const { - return expires_at < Time::now(); - } - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -struct ContactsManager::Chat { - string title; - DialogPhoto photo; - int32 participant_count = 0; - int32 date = 0; - int32 version = -1; - int32 default_permissions_version = -1; - int32 pinned_message_version = -1; - ChannelId migrated_to_channel_id; - - DialogParticipantStatus status = DialogParticipantStatus::Banned(0); - RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; - - static constexpr uint32 CACHE_VERSION = 3; - uint32 cache_version = 0; - - bool is_active = false; - - bool is_title_changed = true; - bool is_photo_changed = true; - bool is_default_permissions_changed = true; - bool is_is_active_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - bool is_update_basic_group_sent = false; - - bool is_repaired = false; // whether cached value is rechecked - - bool is_saved = false; // is current chat version being saved/is saved to the database - bool is_being_saved = false; // is current chat being saved to the database - - bool is_received_from_server = false; // true, if the chat was received from the server and not the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -// do not forget to update drop_chat_full and on_get_chat_full -struct ContactsManager::ChatFull { - int32 version = -1; - UserId creator_user_id; - vector participants; - - Photo photo; - vector registered_photo_file_ids; - FileSourceId file_source_id; - - string description; - - DialogInviteLink invite_link; - - bool can_set_username = false; - - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_send_update = true; // have new changes that need only to be sent to the client - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -struct ContactsManager::Channel { - int64 access_hash = 0; - string title; - DialogPhoto photo; - string username; - vector restriction_reasons; - DialogParticipantStatus status = DialogParticipantStatus::Banned(0); - RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; - int32 date = 0; - int32 participant_count = 0; - - static constexpr uint32 CACHE_VERSION = 7; - uint32 cache_version = 0; - - bool has_linked_channel = false; - bool has_location = false; - bool sign_messages = false; - bool is_slow_mode_enabled = false; - - bool is_megagroup = false; - bool is_verified = false; - bool is_scam = false; - bool is_fake = false; - - bool is_title_changed = true; - bool is_username_changed = true; - bool is_photo_changed = true; - bool is_default_permissions_changed = true; - bool is_status_changed = true; - bool had_read_access = true; - bool was_member = false; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - bool is_update_supergroup_sent = false; - - bool is_repaired = false; // whether cached value is rechecked - - bool is_saved = false; // is current channel version being saved/is saved to the database - bool is_being_saved = false; // is current channel being saved to the database - - bool is_received_from_server = false; // true, if the channel was received from the server and not the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -// do not forget to update invalidate_channel_full and on_get_chat_full -struct ContactsManager::ChannelFull { - Photo photo; - vector registered_photo_file_ids; - FileSourceId file_source_id; - - string description; - int32 participant_count = 0; - int32 administrator_count = 0; - int32 restricted_count = 0; - int32 banned_count = 0; - - DialogInviteLink invite_link; - - uint32 speculative_version = 1; - uint32 repair_request_version = 0; - - StickerSetId sticker_set_id; - - ChannelId linked_channel_id; - - DialogLocation location; - - DcId stats_dc_id; - - int32 slow_mode_delay = 0; - int32 slow_mode_next_send_date = 0; - - MessageId migrated_from_max_message_id; - ChatId migrated_from_chat_id; - - vector bot_user_ids; - - bool can_get_participants = false; - bool can_set_username = false; - bool can_set_sticker_set = false; - bool can_set_location = false; - bool can_view_statistics = false; - bool is_can_view_statistics_inited = false; - bool is_all_history_available = true; - - bool is_slow_mode_next_send_date_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_send_update = true; // have new changes that need only to be sent to the client - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - double expires_at = 0.0; - - bool is_expired() const { - return expires_at < Time::now(); - } - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -struct ContactsManager::SecretChat { - int64 access_hash = 0; - UserId user_id; - SecretChatState state; - string key_hash; - int32 ttl = 0; - int32 date = 0; - int32 layer = 0; - FolderId initial_folder_id; - - bool is_outbound = false; - - bool is_state_changed = true; - bool is_changed = true; // have new changes that need to be sent to the client and database - bool need_save_to_database = true; // have new changes that need only to be saved to the database - - bool is_saved = false; // is current secret chat version being saved/is saved to the database - bool is_being_saved = false; // is current secret chat being saved to the database - - uint64 log_event_id = 0; - - template - void store(StorerT &storer) const; - - template - void parse(ParserT &parser); -}; - -struct ContactsManager::InviteLinkInfo { - // known dialog - DialogId dialog_id; - - // unknown dialog - string title; - Photo photo; - int32 participant_count = 0; - vector participant_user_ids; - bool is_chat = false; - bool is_channel = false; - bool is_public = false; - bool is_megagroup = false; -}; - ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { upload_profile_photo_callback_ = std::make_shared(); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 6f8b376fa..713d98c48 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -15,6 +15,8 @@ #include "td/telegram/Contact.h" #include "td/telegram/DialogAdministrator.h" #include "td/telegram/DialogId.h" +#include "td/telegram/DialogInviteLink.h" +#include "td/telegram/DialogLocation.h" #include "td/telegram/DialogParticipant.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" @@ -26,6 +28,7 @@ #include "td/telegram/Photo.h" #include "td/telegram/PublicDialogType.h" #include "td/telegram/QueryCombiner.h" +#include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/StickerSetId.h" #include "td/telegram/UserId.h" @@ -575,17 +578,345 @@ class ContactsManager : public Actor { tl_object_ptr obj); private: - struct User; - struct UserFull; - struct Chat; - struct ChatFull; - struct Channel; - struct ChannelFull; - struct SecretChat; + struct User { + string first_name; + string last_name; + string username; + string phone_number; + int64 access_hash = -1; - struct BotInfo; + ProfilePhoto photo; - struct InviteLinkInfo; + vector restriction_reasons; + string inline_query_placeholder; + int32 bot_info_version = -1; + + int32 was_online = 0; + int32 local_was_online = 0; + + string language_code; + + std::unordered_set photo_ids; + + std::unordered_map online_member_dialogs; // id -> time + + static constexpr uint32 CACHE_VERSION = 4; + uint32 cache_version = 0; + + bool is_min_access_hash = true; + bool is_received = false; + bool is_verified = false; + bool is_support = false; + bool is_deleted = true; + bool is_bot = true; + bool can_join_groups = true; + bool can_read_all_group_messages = true; + bool is_inline_bot = false; + bool need_location_bot = false; + bool is_scam = false; + bool is_fake = false; + bool is_contact = false; + bool is_mutual_contact = false; + bool need_apply_min_photo = false; + + bool is_photo_inited = false; + + bool is_repaired = false; // whether cached value is rechecked + + bool is_name_changed = true; + bool is_username_changed = true; + bool is_photo_changed = true; + bool is_is_contact_changed = true; + bool is_is_deleted_changed = true; + bool is_default_permissions_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + bool is_status_changed = true; + bool is_online_status_changed = true; // whether online/offline has changed + bool is_update_user_sent = false; + + bool is_saved = false; // is current user version being saved/is saved to the database + bool is_being_saved = false; // is current user being saved to the database + bool is_status_saved = false; // is current user status being saved/is saved to the database + + bool is_received_from_server = false; // true, if the user was received from the server and not the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + // do not forget to update drop_user_full and on_get_user_full + struct UserFull { + Photo photo; + + string about; + + int32 common_chat_count = 0; + + bool is_blocked = false; + bool can_be_called = false; + bool supports_video_calls = false; + bool has_private_calls = false; + bool can_pin_messages = true; + bool need_phone_number_privacy_exception = false; + + bool is_common_chat_count_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + double expires_at = 0.0; + + bool is_expired() const { + return expires_at < Time::now(); + } + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + struct Chat { + string title; + DialogPhoto photo; + int32 participant_count = 0; + int32 date = 0; + int32 version = -1; + int32 default_permissions_version = -1; + int32 pinned_message_version = -1; + ChannelId migrated_to_channel_id; + + DialogParticipantStatus status = DialogParticipantStatus::Banned(0); + RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; + + static constexpr uint32 CACHE_VERSION = 3; + uint32 cache_version = 0; + + bool is_active = false; + + bool is_title_changed = true; + bool is_photo_changed = true; + bool is_default_permissions_changed = true; + bool is_is_active_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + bool is_update_basic_group_sent = false; + + bool is_repaired = false; // whether cached value is rechecked + + bool is_saved = false; // is current chat version being saved/is saved to the database + bool is_being_saved = false; // is current chat being saved to the database + + bool is_received_from_server = false; // true, if the chat was received from the server and not the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + // do not forget to update drop_chat_full and on_get_chat_full + struct ChatFull { + int32 version = -1; + UserId creator_user_id; + vector participants; + + Photo photo; + vector registered_photo_file_ids; + FileSourceId file_source_id; + + string description; + + DialogInviteLink invite_link; + + bool can_set_username = false; + + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + struct Channel { + int64 access_hash = 0; + string title; + DialogPhoto photo; + string username; + vector restriction_reasons; + DialogParticipantStatus status = DialogParticipantStatus::Banned(0); + RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; + int32 date = 0; + int32 participant_count = 0; + + static constexpr uint32 CACHE_VERSION = 7; + uint32 cache_version = 0; + + bool has_linked_channel = false; + bool has_location = false; + bool sign_messages = false; + bool is_slow_mode_enabled = false; + + bool is_megagroup = false; + bool is_verified = false; + bool is_scam = false; + bool is_fake = false; + + bool is_title_changed = true; + bool is_username_changed = true; + bool is_photo_changed = true; + bool is_default_permissions_changed = true; + bool is_status_changed = true; + bool had_read_access = true; + bool was_member = false; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + bool is_update_supergroup_sent = false; + + bool is_repaired = false; // whether cached value is rechecked + + bool is_saved = false; // is current channel version being saved/is saved to the database + bool is_being_saved = false; // is current channel being saved to the database + + bool is_received_from_server = false; // true, if the channel was received from the server and not the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + // do not forget to update invalidate_channel_full and on_get_chat_full + struct ChannelFull { + Photo photo; + vector registered_photo_file_ids; + FileSourceId file_source_id; + + string description; + int32 participant_count = 0; + int32 administrator_count = 0; + int32 restricted_count = 0; + int32 banned_count = 0; + + DialogInviteLink invite_link; + + uint32 speculative_version = 1; + uint32 repair_request_version = 0; + + StickerSetId sticker_set_id; + + ChannelId linked_channel_id; + + DialogLocation location; + + DcId stats_dc_id; + + int32 slow_mode_delay = 0; + int32 slow_mode_next_send_date = 0; + + MessageId migrated_from_max_message_id; + ChatId migrated_from_chat_id; + + vector bot_user_ids; + + bool can_get_participants = false; + bool can_set_username = false; + bool can_set_sticker_set = false; + bool can_set_location = false; + bool can_view_statistics = false; + bool is_can_view_statistics_inited = false; + bool is_all_history_available = true; + + bool is_slow_mode_next_send_date_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + double expires_at = 0.0; + + bool is_expired() const { + return expires_at < Time::now(); + } + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + struct SecretChat { + int64 access_hash = 0; + UserId user_id; + SecretChatState state; + string key_hash; + int32 ttl = 0; + int32 date = 0; + int32 layer = 0; + FolderId initial_folder_id; + + bool is_outbound = false; + + bool is_state_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + bool is_saved = false; // is current secret chat version being saved/is saved to the database + bool is_being_saved = false; // is current secret chat being saved to the database + + uint64 log_event_id = 0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + struct BotInfo { + int32 version = -1; + string description; + vector> commands; + bool is_changed = true; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + struct InviteLinkInfo { + // known dialog + DialogId dialog_id; + + // unknown dialog + string title; + Photo photo; + int32 participant_count = 0; + vector participant_user_ids; + bool is_chat = false; + bool is_channel = false; + bool is_public = false; + bool is_megagroup = false; + }; struct UserPhotos { vector photos; From 155d83c58f2249f9baad17834f31c2cc944f6c14 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 17:27:07 +0300 Subject: [PATCH 130/232] Support top-level comments in SplitSource.php. --- SplitSource.php | 14 +++++++++++++- td/telegram/Td.cpp | 2 -- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/SplitSource.php b/SplitSource.php index 605ee0121..35117f779 100644 --- a/SplitSource.php +++ b/SplitSource.php @@ -75,6 +75,7 @@ function split_file($file, $chunks, $undo) { $target_depth = 1 + $is_generated; $is_static = false; $in_define = false; + $in_comment = false; $current = ''; $common = ''; $functions = array(); @@ -113,6 +114,17 @@ function split_file($file, $chunks, $undo) { continue; } + if ($in_comment && strpos($line, '*/') === 0) { + $in_comment = false; + continue; + } + if (strpos($line, '/*') === 0) { + $in_comment = true; + } + if ($in_comment) { + continue; + } + if ($depth !== $target_depth) { $common .= $line; continue; @@ -285,7 +297,7 @@ function split_file($file, $chunks, $undo) { '[>](td_db[(][)]|get_td_db_impl[(])|TdDb[^A-Za-z]' => 'TdDb', 'TopDialogCategory|get_top_dialog_category' => 'TopDialogCategory', 'top_dialog_manager[_(-][^.]|TopDialogManager' => 'TopDialogManager', - 'updates_manager[_(-][^.]|UpdatesManager|get_difference[)]' => 'UpdatesManager', + 'updates_manager[_(-][^.]|UpdatesManager|get_difference[)]|updateSentMessage|dummyUpdate' => 'UpdatesManager', 'WebPageId(Hash)?' => 'WebPageId', 'web_pages_manager[_(-][^.]|WebPagesManager' => 'WebPagesManager'); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index a6fd56ab6..f02c8d1d7 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -522,8 +522,6 @@ class SaveAppLogQuery : public Td::ResultHandler { } }; -/*** Td ***/ -/** Td queries **/ class TestQuery : public Td::ResultHandler { public: explicit TestQuery(uint64 request_id) : request_id_(request_id) { From 3a657d9072b324aad1143c6abe20a802f3b92cd5 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 17:49:20 +0300 Subject: [PATCH 131/232] Fix need_rejoin. --- td/telegram/GroupCallManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index c0824ec4e..1b69c5535 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -2061,9 +2061,9 @@ void GroupCallManager::on_group_call_left_impl(GroupCall *group_call, bool need_ CHECK(group_call != nullptr && group_call->is_inited && group_call->is_joined); group_call->is_joined = false; group_call->need_rejoin = need_rejoin && !group_call->is_being_left; - if (need_rejoin && group_call->dialog_id.is_valid() && + if (group_call->need_rejoin && group_call->dialog_id.is_valid() && !td_->messages_manager_->have_input_peer(group_call->dialog_id, AccessRights::Read)) { - need_rejoin = false; + group_call->need_rejoin = false; } group_call->is_being_left = false; group_call->is_speaking = false; From 025cb9b6a4945562c7578c3edb11ed6af0ae3526 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 13 Feb 2021 21:49:24 +0300 Subject: [PATCH 132/232] Don't create request promise early. --- td/telegram/Td.cpp | 66 +++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index f02c8d1d7..e735c6b7b 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5216,10 +5216,10 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) { } void Td::on_request(uint64 id, td_api::getNetworkStatistics &request) { - CREATE_REQUEST_PROMISE(); if (!request.only_current_ && G()->shared_config().get_option_boolean("disable_persistent_network_statistics")) { return send_error_raw(id, 400, "Persistent network statistics is disabled"); } + CREATE_REQUEST_PROMISE(); auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { if (result.is_error()) { promise.set_error(result.move_as_error()); @@ -5302,23 +5302,23 @@ void Td::on_request(uint64 id, const td_api::getAutoDownloadSettingsPresets &req void Td::on_request(uint64 id, const td_api::setAutoDownloadSettings &request) { CHECK_IS_USER(); - CREATE_OK_REQUEST_PROMISE(); if (request.settings_ == nullptr) { return send_error_raw(id, 400, "New settings must be non-empty"); } + CREATE_OK_REQUEST_PROMISE(); set_auto_download_settings(this, get_net_type(request.type_), get_auto_download_settings(request.settings_), std::move(promise)); } void Td::on_request(uint64 id, td_api::getTopChats &request) { CHECK_IS_USER(); - CREATE_REQUEST_PROMISE(); if (request.category_ == nullptr) { - return promise.set_error(Status::Error(400, "Top chat category must be non-empty")); + return send_error_raw(id, 400, "Top chat category must be non-empty"); } if (request.limit_ <= 0) { - return promise.set_error(Status::Error(400, "Limit must be positive")); + return send_error_raw(id, 400, "Limit must be positive"); } + CREATE_REQUEST_PROMISE(); auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result> result) mutable { if (result.is_error()) { promise.set_error(result.move_as_error()); @@ -5881,6 +5881,21 @@ void Td::on_request(uint64 id, td_api::createNewSecretChat &request) { void Td::on_request(uint64 id, const td_api::createCall &request) { CHECK_IS_USER(); + + if (request.protocol_ == nullptr) { + return send_error_raw(id, 400, "Call protocol must be non-empty"); + } + + UserId user_id(request.user_id_); + auto input_user = contacts_manager_->get_input_user(user_id); + if (input_user == nullptr) { + return send_error_raw(id, 400, "User not found"); + } + + if (!G()->shared_config().get_option_boolean("calls_enabled")) { + return send_error_raw(id, 400, "Calls are not enabled for the current user"); + } + CREATE_REQUEST_PROMISE(); auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { if (result.is_error()) { @@ -5889,31 +5904,16 @@ void Td::on_request(uint64 id, const td_api::createCall &request) { promise.set_value(result.ok().get_call_id_object()); } }); - - if (!request.protocol_) { - return query_promise.set_error(Status::Error(5, "Call protocol must be non-empty")); - } - - UserId user_id(request.user_id_); - auto input_user = contacts_manager_->get_input_user(user_id); - if (input_user == nullptr) { - return query_promise.set_error(Status::Error(6, "User not found")); - } - - if (!G()->shared_config().get_option_boolean("calls_enabled")) { - return query_promise.set_error(Status::Error(7, "Calls are not enabled for the current user")); - } - send_closure(G()->call_manager(), &CallManager::create_call, user_id, std::move(input_user), CallProtocol(*request.protocol_), request.is_video_, std::move(query_promise)); } void Td::on_request(uint64 id, const td_api::acceptCall &request) { CHECK_IS_USER(); - CREATE_OK_REQUEST_PROMISE(); - if (!request.protocol_) { - return promise.set_error(Status::Error(5, "Call protocol must be non-empty")); + if (request.protocol_ == nullptr) { + return send_error_raw(id, 400, "Call protocol must be non-empty"); } + CREATE_OK_REQUEST_PROMISE(); send_closure(G()->call_manager(), &CallManager::accept_call, CallId(request.call_id_), CallProtocol(*request.protocol_), std::move(promise)); } @@ -6274,8 +6274,8 @@ void Td::on_request(uint64 id, const td_api::canTransferOwnership &request) { void Td::on_request(uint64 id, td_api::transferChatOwnership &request) { CHECK_IS_USER(); - CREATE_OK_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.password_); + CREATE_OK_REQUEST_PROMISE(); contacts_manager_->transfer_dialog_ownership(DialogId(request.chat_id_), UserId(request.user_id_), request.password_, std::move(promise)); } @@ -6316,38 +6316,38 @@ void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { } void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { - CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.invite_link_); + CREATE_REQUEST_PROMISE(); contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, request.expire_date_, request.member_limit_, false, std::move(promise)); } void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { - CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.invite_link_); + CREATE_REQUEST_PROMISE(); contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, 0, 0, true, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { - CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.offset_invite_link_); + CREATE_REQUEST_PROMISE(); contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.administrator_user_id_), request.is_revoked_, request.offset_date_, request.offset_invite_link_, request.limit_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { - CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.invite_link_); + CREATE_REQUEST_PROMISE(); contacts_manager_->get_dialog_invite_link_users(DialogId(request.chat_id_), request.invite_link_, std::move(request.offset_member_), request.limit_, std::move(promise)); } void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { - CREATE_OK_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.invite_link_); + CREATE_OK_REQUEST_PROMISE(); contacts_manager_->delete_revoked_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, std::move(promise)); } @@ -6954,8 +6954,8 @@ void Td::on_request(uint64 id, td_api::reportChat &request) { void Td::on_request(uint64 id, td_api::getChatStatisticsUrl &request) { CHECK_IS_USER(); - CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.parameters_); + CREATE_REQUEST_PROMISE(); messages_manager_->get_dialog_statistics_url(DialogId(request.chat_id_), request.parameters_, request.is_dark_, std::move(promise)); } @@ -6975,8 +6975,8 @@ void Td::on_request(uint64 id, const td_api::getMessageStatistics &request) { void Td::on_request(uint64 id, td_api::getStatisticalGraph &request) { CHECK_IS_USER(); - CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.token_); + CREATE_REQUEST_PROMISE(); contacts_manager_->load_statistics_graph(DialogId(request.chat_id_), request.token_, request.x_, std::move(promise)); } @@ -7636,11 +7636,11 @@ void Td::on_request(uint64 id, td_api::getAllPassportElements &request) { void Td::on_request(uint64 id, td_api::setPassportElement &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.password_); - CREATE_REQUEST_PROMISE(); auto r_secure_value = get_secure_value(file_manager_.get(), std::move(request.element_)); if (r_secure_value.is_error()) { - return promise.set_error(r_secure_value.move_as_error()); + return send_error_raw(id, 400, r_secure_value.error().message()); } + CREATE_REQUEST_PROMISE(); send_closure(secure_manager_, &SecureManager::set_secure_value, std::move(request.password_), r_secure_value.move_as_ok(), std::move(promise)); } From 65b381f7fd3612a2392cd97f53e83848b730ab35 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 13 Feb 2021 22:45:11 +0300 Subject: [PATCH 133/232] Add fast path in fix_formatted_text. --- td/telegram/MessageEntity.cpp | 37 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index 9a4c0be62..61e5f69da 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -3753,23 +3753,32 @@ static void merge_new_entities(vector &entities, vector &entities, bool allow_empty, bool skip_new_entities, bool skip_bot_commands, bool for_draft) { - if (!check_utf8(text)) { - return Status::Error(400, "Strings must be encoded in UTF-8"); - } - - for (auto &entity : entities) { - if (entity.offset < 0 || entity.offset > 1000000) { - return Status::Error(400, PSLICE() << "Receive an entity with incorrect offset " << entity.offset); + string result; + if (entities.empty()) { + // fast path + if (!clean_input_string(text)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); } - if (entity.length < 0 || entity.length > 1000000) { - return Status::Error(400, PSLICE() << "Receive an entity with incorrect length " << entity.length); + result = std::move(text); + } else { + if (!check_utf8(text)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); } + + for (auto &entity : entities) { + if (entity.offset < 0 || entity.offset > 1000000) { + return Status::Error(400, PSLICE() << "Receive an entity with incorrect offset " << entity.offset); + } + if (entity.length < 0 || entity.length > 1000000) { + return Status::Error(400, PSLICE() << "Receive an entity with incorrect length " << entity.length); + } + } + td::remove_if(entities, [](const MessageEntity &entity) { return entity.length == 0; }); + + fix_entities(entities); + + TRY_RESULT_ASSIGN(result, clean_input_string_with_entities(text, entities)); } - td::remove_if(entities, [](const MessageEntity &entity) { return entity.length == 0; }); - - fix_entities(entities); - - TRY_RESULT(result, clean_input_string_with_entities(text, entities)); // now entities are still sorted by offset and length, but not type, // because some characters could be deleted and after that some entities begin to share a common end From 578b8a4dec5618f4571122d6596470ad61f76607 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 14 Feb 2021 01:25:17 +0300 Subject: [PATCH 134/232] Minor performance improvements. --- td/telegram/MessageEntity.cpp | 1 + td/telegram/MessagesManager.cpp | 25 ++++++++++++++++++------- td/telegram/MessagesManager.h | 1 + 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index 61e5f69da..0c2469459 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -3029,6 +3029,7 @@ Result> get_message_entities(const ContactsManager *contac vector> &&input_entities, bool allow_all) { vector entities; + entities.reserve(input_entities.size()); for (auto &entity : input_entities) { if (entity == nullptr || entity->type_ == nullptr) { continue; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 86bd13c95..46a25ce1e 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -10752,13 +10752,16 @@ void MessagesManager::unload_dialog(DialogId dialog_id) { if (G()->close_flag()) { return; } - if (!is_message_unload_enabled()) { - // just in case - return; - } Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); + CHECK(d->has_unload_timeout); + + if (!is_message_unload_enabled()) { + // just in case + d->has_unload_timeout = false; + return; + } vector to_unload_message_ids; int32 left_to_unload = 0; @@ -10784,6 +10787,8 @@ void MessagesManager::unload_dialog(DialogId dialog_id) { if (left_to_unload > 0) { LOG(DEBUG) << "Need to unload " << left_to_unload << " messages more in " << dialog_id; pending_unload_dialog_timeout_.add_timeout_in(d->dialog_id.get(), get_unload_dialog_delay()); + } else { + d->has_unload_timeout = false; } } @@ -19397,8 +19402,11 @@ void MessagesManager::open_dialog(Dialog *d) { } } - LOG(INFO) << "Cancel unload timeout for " << dialog_id; - pending_unload_dialog_timeout_.cancel_timeout(dialog_id.get()); + if (d->has_unload_timeout) { + LOG(INFO) << "Cancel unload timeout for " << dialog_id; + pending_unload_dialog_timeout_.cancel_timeout(dialog_id.get()); + d->has_unload_timeout = false; + } if (d->new_secret_chat_notification_id.is_valid()) { remove_new_secret_chat_notification(d, true); @@ -19500,6 +19508,8 @@ void MessagesManager::close_dialog(Dialog *d) { } if (is_message_unload_enabled()) { + CHECK(!d->has_unload_timeout); + d->has_unload_timeout = true; pending_unload_dialog_timeout_.set_timeout_in(dialog_id.get(), get_unload_dialog_delay()); } @@ -31783,9 +31793,10 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq on_dialog_updated(dialog_id, "drop have_full_history"); } - if (!d->is_opened && d->messages != nullptr && is_message_unload_enabled()) { + if (!d->is_opened && d->messages != nullptr && is_message_unload_enabled() && !d->has_unload_timeout) { LOG(INFO) << "Schedule unload of " << dialog_id; pending_unload_dialog_timeout_.add_timeout_in(dialog_id.get(), get_unload_dialog_delay()); + d->has_unload_timeout = true; } if (message->ttl > 0 && message->ttl_expires_at != 0) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index a4b1b89c8..37d9e973a 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -1227,6 +1227,7 @@ class MessagesManager : public Actor { bool increment_view_counter = false; bool is_update_new_chat_sent = false; + bool has_unload_timeout = false; int32 pts = 0; // for channels only std::multimap postponed_channel_updates; // for channels only From c8157edf5711b2133f571b7ce7ee095f2b1cca56 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 14 Feb 2021 01:35:40 +0300 Subject: [PATCH 135/232] Don't save message file source IDs for bots. --- td/telegram/MessagesManager.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 46a25ce1e..1599e79a2 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1085,7 +1085,7 @@ class InitHistoryImportQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!td->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) { + if (FileReferenceManager::is_file_reference_error(status)) { LOG(ERROR) << "Receive file reference error " << status; } if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) { @@ -21214,6 +21214,10 @@ void MessagesManager::on_message_live_location_viewed_on_server(int64 task_id) { } FileSourceId MessagesManager::get_message_file_source_id(FullMessageId full_message_id) { + if (td_->auth_manager_->is_bot()) { + return FileSourceId(); + } + auto dialog_id = full_message_id.get_dialog_id(); auto message_id = full_message_id.get_message_id(); if (!dialog_id.is_valid() || !(message_id.is_valid() || message_id.is_valid_scheduled()) || @@ -21229,6 +21233,10 @@ FileSourceId MessagesManager::get_message_file_source_id(FullMessageId full_mess } void MessagesManager::add_message_file_sources(DialogId dialog_id, const Message *m) { + if (td_->auth_manager_->is_bot()) { + return; + } + if (dialog_id.get_type() != DialogType::SecretChat && m->is_content_secret) { // return; } @@ -21248,6 +21256,10 @@ void MessagesManager::add_message_file_sources(DialogId dialog_id, const Message } void MessagesManager::remove_message_file_sources(DialogId dialog_id, const Message *m) { + if (td_->auth_manager_->is_bot()) { + return; + } + auto file_ids = get_message_content_file_ids(m->content.get(), td_); if (file_ids.empty()) { return; From d519040cd9e9a40b72f08fbb0288686ce77450d9 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 15:28:58 +0300 Subject: [PATCH 136/232] Make flag in td_api::groupCall more useful. --- td/generate/scheme/td_api.tl | 6 +++--- td/generate/scheme/td_api.tlo | Bin 194288 -> 194284 bytes td/telegram/GroupCallManager.cpp | 10 +++++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index f66f151ff..dc7e6486c 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2104,9 +2104,9 @@ groupCallRecentSpeaker user_id:int32 is_speaking:Bool = GroupCallRecentSpeaker; //@loaded_all_participants True, if all group call participants are loaded //@recent_speakers Recently speaking users in the group call //@mute_new_participants True, if only group call administrators can unmute new participants -//@allowed_change_mute_new_participants True, if group call administrators can enable or disable mute_new_participants setting +//@can_change_mute_new_participants True, if the current user can enable or disable mute_new_participants setting //@duration Call duration; for ended calls only -groupCall id:int32 is_active:Bool is_joined:Bool need_rejoin:Bool can_unmute_self:Bool can_be_managed:Bool participant_count:int32 loaded_all_participants:Bool recent_speakers:vector mute_new_participants:Bool allowed_change_mute_new_participants:Bool duration:int32 = GroupCall; +groupCall id:int32 is_active:Bool is_joined:Bool need_rejoin:Bool can_unmute_self:Bool can_be_managed:Bool participant_count:int32 loaded_all_participants:Bool recent_speakers:vector mute_new_participants:Bool can_change_mute_new_participants:Bool duration:int32 = GroupCall; //@description Describes a payload fingerprint for interaction with tgcalls @hash Value of the field hash @setup Value of the field setup @fingerprint Value of the field fingerprint groupCallPayloadFingerprint hash:string setup:string fingerprint:string = GroupCallPayloadFingerprint; @@ -4455,7 +4455,7 @@ getGroupCall group_call_id:int32 = GroupCall; //@description Joins a group call @group_call_id Group call identifier @payload Group join payload, received from tgcalls. Use null to cancel previous joinGroupCall request @source Caller synchronization source identifier; received from tgcalls @is_muted True, if the user's microphone is muted joinGroupCall group_call_id:int32 payload:groupCallPayload source:int32 is_muted:Bool = GroupCallJoinResponse; -//@description Toggles whether new participants of a group call can be unmuted only by administrators of the group call. Requires groupCall.can_be_managed and allowed_change_mute_mew_participants group call flag +//@description Toggles whether new participants of a group call can be unmuted only by administrators of the group call. Requires groupCall.can_change_mute_new_participants group call flag //@group_call_id Group call identifier @mute_new_participants New value of the mute_new_participants setting toggleGroupCallMuteNewParticipants group_call_id:int32 mute_new_participants:Bool = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 4a27257c92f7034e3faa1eafd23a061c31cefa79..6b9cbc125e714b7d576cc624925712f482c94faf 100644 GIT binary patch delta 131 zcmezHmix_H?hR{%SY~Ui>)X6RNMV&Eh{c&+lwVrloS2itzyRXkJSODBGdZzBdAfri zBg|4XwXC&sOr^ZiC+$TMG`#xK+NzFg@ZU3>4afuz6w|&81#*T6@ Rb9%yDCJTsQMk!NEBLE;qKsf*a diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 1b69c5535..eb862a7c8 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1729,6 +1729,12 @@ void GroupCallManager::toggle_group_call_mute_new_participants(GroupCallId group Promise &&promise) { TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); + auto *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->can_be_managed || + !group_call->allowed_change_mute_new_participants) { + return promise.set_error(Status::Error(400, "Can't change mute_new_participant setting")); + } + int32 flags = telegram_api::phone_toggleGroupCallSettings::JOIN_MUTED_MASK; td_->create_handler(std::move(promise)) ->send(flags, input_group_call_id, mute_new_participants); @@ -2502,11 +2508,13 @@ tl_object_ptr GroupCallManager::get_group_call_object( bool is_joined = group_call->is_joined && !group_call->is_being_left; bool can_self_unmute = is_joined && group_call->can_self_unmute; + bool can_change_mute_new_participants = + group_call->is_active && group_call->can_be_managed && group_call->allowed_change_mute_new_participants; return td_api::make_object(group_call->group_call_id.get(), group_call->is_active, is_joined, group_call->need_rejoin, can_self_unmute, group_call->can_be_managed, group_call->participant_count, group_call->loaded_all_participants, std::move(recent_speakers), group_call->mute_new_participants, - group_call->allowed_change_mute_new_participants, group_call->duration); + can_change_mute_new_participants, group_call->duration); } tl_object_ptr GroupCallManager::get_update_group_call_object( From 4f41d58c502c3eca416103426efc3508ed78321d Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 15:37:36 +0300 Subject: [PATCH 137/232] Don't treat GROUPCALL_NOT_MODIFIED as an error. --- td/telegram/GroupCallManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index eb862a7c8..429fb6c5d 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -242,6 +242,10 @@ class ToggleGroupCallSettingsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { + if (status.message() == "GROUPCALL_NOT_MODIFIED") { + promise_.set_value(Unit()); + return; + } promise_.set_error(std::move(status)); } }; From 70b81f81d4b7943ae54ec8facaa3e2339f98b765 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 16:06:07 +0300 Subject: [PATCH 138/232] Drop pending groupCallParticipant fields in case of an error. --- td/telegram/GroupCallManager.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 429fb6c5d..cc6df6d1a 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1878,10 +1878,9 @@ void GroupCallManager::toggle_group_call_participant_is_muted(GroupCallId group_ promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { promise.set_error(result.move_as_error()); - } else { - send_closure(actor_id, &GroupCallManager::on_toggle_group_call_participant_is_muted, input_group_call_id, user_id, - generation, std::move(promise)); } + send_closure(actor_id, &GroupCallManager::on_toggle_group_call_participant_is_muted, input_group_call_id, user_id, + generation, std::move(promise)); }); td_->create_handler(std::move(query_promise)) ->send(input_group_call_id, user_id, is_muted, 0); @@ -1957,10 +1956,9 @@ void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { promise.set_error(result.move_as_error()); - } else { - send_closure(actor_id, &GroupCallManager::on_set_group_call_participant_volume_level, input_group_call_id, - user_id, generation, std::move(promise)); } + send_closure(actor_id, &GroupCallManager::on_set_group_call_participant_volume_level, input_group_call_id, user_id, + generation, std::move(promise)); }); td_->create_handler(std::move(query_promise)) ->send(input_group_call_id, user_id, false, volume_level); From 5f54e7244499e32d7f74ddd3ee959492ac5151f4 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 18:12:50 +0300 Subject: [PATCH 139/232] Process toggleGroupCallMuteNewParticipants immediately and sync in background. --- td/telegram/GroupCallManager.cpp | 91 ++++++++++++++++++++++++++++---- td/telegram/GroupCallManager.h | 8 +++ td/telegram/MessagesManager.cpp | 1 - 3 files changed, 89 insertions(+), 11 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index cc6df6d1a..ac6202c75 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -425,6 +425,9 @@ struct GroupCallManager::GroupCall { int32 duration = 0; int32 audio_source = 0; int32 joined_date = 0; + + bool have_pending_mute_new_participants = false; + bool pending_mute_new_participants = false; }; struct GroupCallManager::GroupCallParticipants { @@ -849,6 +852,11 @@ void GroupCallManager::finish_check_group_call_is_joined(InputGroupCallId input_ check_group_call_is_joined_timeout_.set_timeout_in(group_call->group_call_id.get(), next_timeout); } +bool GroupCallManager::get_group_call_mute_new_participants(const GroupCall *group_call) { + return group_call->have_pending_mute_new_participants ? group_call->pending_mute_new_participants + : group_call->mute_new_participants; +} + bool GroupCallManager::need_group_call_participants(InputGroupCallId input_group_call_id) const { return need_group_call_participants(input_group_call_id, get_group_call(input_group_call_id)); } @@ -1739,11 +1747,71 @@ void GroupCallManager::toggle_group_call_mute_new_participants(GroupCallId group return promise.set_error(Status::Error(400, "Can't change mute_new_participant setting")); } + if (mute_new_participants == get_group_call_mute_new_participants(group_call)) { + return promise.set_value(Unit()); + } + + // there is no reason to save promise; we will send an update with actual value anyway + + group_call->pending_mute_new_participants = mute_new_participants; + if (!group_call->have_pending_mute_new_participants) { + group_call->have_pending_mute_new_participants = true; + send_toggle_group_call_mute_new_participants_query(input_group_call_id, mute_new_participants); + } + send_update_group_call(group_call, "toggle_group_call_mute_new_participants"); + promise.set_value(Unit()); +} + +void GroupCallManager::send_toggle_group_call_mute_new_participants_query(InputGroupCallId input_group_call_id, + bool mute_new_participants) { + auto promise = PromiseCreator::lambda( + [actor_id = actor_id(this), input_group_call_id, mute_new_participants](Result result) { + send_closure(actor_id, &GroupCallManager::on_toggle_group_call_mute_new_participants, input_group_call_id, + mute_new_participants, std::move(result)); + }); int32 flags = telegram_api::phone_toggleGroupCallSettings::JOIN_MUTED_MASK; td_->create_handler(std::move(promise)) ->send(flags, input_group_call_id, mute_new_participants); } +void GroupCallManager::on_toggle_group_call_mute_new_participants(InputGroupCallId input_group_call_id, + bool mute_new_participants, Result &&result) { + if (G()->close_flag()) { + return; + } + + auto *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) { + return; + } + + CHECK(group_call->have_pending_mute_new_participants); + if (result.is_error()) { + group_call->have_pending_mute_new_participants = false; + if (group_call->can_be_managed && group_call->allowed_change_mute_new_participants) { + LOG(ERROR) << "Failed to set mute_new_participants to " << mute_new_participants << " in " << input_group_call_id + << ": " << result.error(); + } + group_call->have_pending_mute_new_participants = false; + if (group_call->pending_mute_new_participants != group_call->mute_new_participants) { + send_update_group_call(group_call, "on_toggle_group_call_mute_new_participants failed"); + } + } else { + if (group_call->pending_mute_new_participants != mute_new_participants) { + // need to send another request + send_toggle_group_call_mute_new_participants_query(input_group_call_id, + group_call->pending_mute_new_participants); + return; + } + + group_call->have_pending_mute_new_participants = false; + if (group_call->mute_new_participants != mute_new_participants) { + LOG(ERROR) << "Failed to set mute_new_participants to " << mute_new_participants << " in " << input_group_call_id; + send_update_group_call(group_call, "on_toggle_group_call_mute_new_participants failed 2"); + } + } +} + void GroupCallManager::invite_group_call_participants(GroupCallId group_call_id, vector &&user_ids, Promise &&promise) { TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); @@ -1903,16 +1971,14 @@ void GroupCallManager::on_toggle_group_call_participant_is_muted(InputGroupCallI } CHECK(participant->have_pending_is_muted); + participant->have_pending_is_muted = false; if (participant->server_is_muted_by_themselves != participant->pending_is_muted_by_themselves || participant->server_is_muted_by_admin != participant->pending_is_muted_by_admin || participant->server_is_muted_locally != participant->pending_is_muted_locally) { LOG(ERROR) << "Failed to mute/unmute " << user_id << " in " << input_group_call_id; - participant->have_pending_is_muted = false; if (participant->order != 0) { send_update_group_call_participant(input_group_call_id, *participant); } - } else { - participant->have_pending_is_muted = false; } promise.set_value(Unit()); } @@ -2210,7 +2276,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptris_active) { // never update ended calls } else if (!call.is_active) { - // always update to an ended call, droping also is_joined, is_speaking and other flags + // always update to an ended call, droping also is_joined, is_speaking and other local flags *group_call = std::move(call); need_update = true; } else { @@ -2218,9 +2284,14 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrmute_new_participants || call.allowed_change_mute_new_participants != group_call->allowed_change_mute_new_participants; if (mute_flags_changed && call.version >= group_call->version) { + auto old_mute_new_participants = get_group_call_mute_new_participants(group_call); + need_update |= (call.allowed_change_mute_new_participants && call.can_be_managed) != + (group_call->allowed_change_mute_new_participants && group_call->can_be_managed); group_call->mute_new_participants = call.mute_new_participants; group_call->allowed_change_mute_new_participants = call.allowed_change_mute_new_participants; - need_update = true; + if (old_mute_new_participants != get_group_call_mute_new_participants(group_call)) { + need_update = true; + } } if (call.can_be_managed != group_call->can_be_managed) { group_call->can_be_managed = call.can_be_managed; @@ -2510,13 +2581,13 @@ tl_object_ptr GroupCallManager::get_group_call_object( bool is_joined = group_call->is_joined && !group_call->is_being_left; bool can_self_unmute = is_joined && group_call->can_self_unmute; + bool mute_new_participants = get_group_call_mute_new_participants(group_call); bool can_change_mute_new_participants = group_call->is_active && group_call->can_be_managed && group_call->allowed_change_mute_new_participants; - return td_api::make_object(group_call->group_call_id.get(), group_call->is_active, is_joined, - group_call->need_rejoin, can_self_unmute, group_call->can_be_managed, - group_call->participant_count, group_call->loaded_all_participants, - std::move(recent_speakers), group_call->mute_new_participants, - can_change_mute_new_participants, group_call->duration); + return td_api::make_object( + group_call->group_call_id.get(), group_call->is_active, is_joined, group_call->need_rejoin, can_self_unmute, + group_call->can_be_managed, group_call->participant_count, group_call->loaded_all_participants, + std::move(recent_speakers), mute_new_participants, can_change_mute_new_participants, group_call->duration); } tl_object_ptr GroupCallManager::get_update_group_call_object( diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index a90633fdb..f75941b94 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -135,6 +135,8 @@ class GroupCallManager : public Actor { void finish_check_group_call_is_joined(InputGroupCallId input_group_call_id, int32 audio_source, Result &&result); + static bool get_group_call_mute_new_participants(const GroupCall *group_call); + bool need_group_call_participants(InputGroupCallId input_group_call_id) const; bool need_group_call_participants(InputGroupCallId input_group_call_id, const GroupCall *group_call) const; @@ -174,6 +176,12 @@ class GroupCallManager : public Actor { static GroupCallParticipant *get_group_call_participant(GroupCallParticipants *group_call_participants, UserId user_id); + void send_toggle_group_call_mute_new_participants_query(InputGroupCallId input_group_call_id, + bool mute_new_participants); + + void on_toggle_group_call_mute_new_participants(InputGroupCallId input_group_call_id, bool mute_new_participants, + Result &&result); + void on_toggle_group_call_participant_is_muted(InputGroupCallId input_group_call_id, UserId user_id, uint64 generation, Promise &&promise); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 1599e79a2..284a44299 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -29528,7 +29528,6 @@ void MessagesManager::on_update_dialog_group_call(DialogId dialog_id, bool has_a is_group_call_empty = false; } if (d->has_active_group_call == has_active_group_call && d->is_group_call_empty == is_group_call_empty) { - LOG(INFO) << "Nothing changed in " << dialog_id; return; } From aa2f645d74fd5b33fe58eccdff1e9d4b6eaaf131 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 18:18:34 +0300 Subject: [PATCH 140/232] Simplify get_group_call_participant usage. --- td/telegram/GroupCallManager.cpp | 13 +++++++++---- td/telegram/GroupCallManager.h | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index ac6202c75..bc0934e6c 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -976,6 +976,11 @@ GroupCallManager::GroupCallParticipants *GroupCallManager::add_group_call_partic return participants.get(); } +GroupCallParticipant *GroupCallManager::get_group_call_participant(InputGroupCallId input_group_call_id, + UserId user_id) { + return get_group_call_participant(add_group_call_participants(input_group_call_id), user_id); +} + GroupCallParticipant *GroupCallManager::get_group_call_participant(GroupCallParticipants *group_call_participants, UserId user_id) { for (auto &group_call_participant : group_call_participants->participants) { @@ -1965,7 +1970,7 @@ void GroupCallManager::on_toggle_group_call_participant_is_muted(InputGroupCallI return promise.set_value(Unit()); } - auto participant = get_group_call_participant(add_group_call_participants(input_group_call_id), user_id); + auto participant = get_group_call_participant(input_group_call_id, user_id); if (participant == nullptr || participant->pending_is_muted_generation != generation) { return promise.set_value(Unit()); } @@ -2002,7 +2007,7 @@ void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group return promise.set_error(Status::Error(400, "Can't change self volume level")); } - auto participant = get_group_call_participant(add_group_call_participants(input_group_call_id), user_id); + auto participant = get_group_call_participant(input_group_call_id, user_id); if (participant == nullptr) { return promise.set_error(Status::Error(400, "Can't find group call participant")); } @@ -2041,7 +2046,7 @@ void GroupCallManager::on_set_group_call_participant_volume_level(InputGroupCall return promise.set_value(Unit()); } - auto participant = get_group_call_participant(add_group_call_participants(input_group_call_id), user_id); + auto participant = get_group_call_participant(input_group_call_id, user_id); if (participant == nullptr || participant->pending_volume_level_generation != generation) { return promise.set_value(Unit()); } @@ -2394,7 +2399,7 @@ void GroupCallManager::on_user_speaking_in_group_call(GroupCallId group_call_id, if (!td_->contacts_manager_->have_user_force(user_id) || (!recursive && need_group_call_participants(input_group_call_id, group_call) && - get_group_call_participant(add_group_call_participants(input_group_call_id), user_id) == nullptr)) { + get_group_call_participant(input_group_call_id, user_id) == nullptr)) { if (recursive) { LOG(ERROR) << "Failed to find speaking " << user_id << " from " << input_group_call_id; } else { diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index f75941b94..9c75e6de6 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -173,6 +173,8 @@ class GroupCallManager : public Actor { GroupCallParticipants *add_group_call_participants(InputGroupCallId input_group_call_id); + GroupCallParticipant *get_group_call_participant(InputGroupCallId input_group_call_id, UserId user_id); + static GroupCallParticipant *get_group_call_participant(GroupCallParticipants *group_call_participants, UserId user_id); From f9ef4c0c36a5b45b3ca753dc3b4e2022424bc767 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 19:27:45 +0300 Subject: [PATCH 141/232] Don't rejoin voice chats in left basic groups. --- td/telegram/GroupCallManager.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index bc0934e6c..7121c0043 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -2140,9 +2140,13 @@ void GroupCallManager::on_group_call_left_impl(GroupCall *group_call, bool need_ CHECK(group_call != nullptr && group_call->is_inited && group_call->is_joined); group_call->is_joined = false; group_call->need_rejoin = need_rejoin && !group_call->is_being_left; - if (group_call->need_rejoin && group_call->dialog_id.is_valid() && - !td_->messages_manager_->have_input_peer(group_call->dialog_id, AccessRights::Read)) { - group_call->need_rejoin = false; + if (group_call->need_rejoin && group_call->dialog_id.is_valid()) { + auto dialog_id = group_call->dialog_id; + if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read) || + (dialog_id.get_type() == DialogType::Chat && + !td_->contacts_manager_->get_chat_status(dialog_id.get_chat_id()).is_member())) { + group_call->need_rejoin = false; + } } group_call->is_being_left = false; group_call->is_speaking = false; From 70ee36a493409a28d57920d64d3b49287d6063f1 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 23:00:30 +0300 Subject: [PATCH 142/232] Improve join_group_call. --- td/telegram/GroupCallManager.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 7121c0043..c7c57c8b3 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1476,9 +1476,11 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, if (group_call->is_inited && !group_call->is_active) { return promise.set_error(Status::Error(400, "Group call is finished")); } + bool need_update = false; + bool is_rejoin = group_call->need_rejoin; if (group_call->need_rejoin) { group_call->need_rejoin = false; - send_update_group_call(group_call, "join_group_call"); + need_update = true; } cancel_join_group_call_request(input_group_call_id); @@ -1512,9 +1514,7 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, if (group_call->is_being_left) { group_call->is_being_left = false; - if (group_call->is_inited) { - send_update_group_call(group_call, "join_group_call"); - } + need_update |= group_call->is_joined; } auto json_payload = json_encode(json_object([&payload, audio_source](auto &o) { @@ -1557,8 +1557,19 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, group_call_participant.server_is_muted_by_admin = !group_call->can_self_unmute && !can_manage_group_call(input_group_call_id); group_call_participant.server_is_muted_by_themselves = is_muted && !group_call_participant.server_is_muted_by_admin; + group_call_participant.is_just_joined = !is_rejoin; group_call_participant.is_fake = true; - process_group_call_participant(input_group_call_id, std::move(group_call_participant)); + int diff = process_group_call_participant(input_group_call_id, std::move(group_call_participant)); + if (diff != 0) { + CHECK(diff == 1); + group_call->participant_count++; + need_update = true; + update_group_call_dialog(group_call, "join_group_call"); + } + + if (need_update) { + send_update_group_call(group_call, "join_group_call"); + } } try_load_group_call_administrators(input_group_call_id, group_call->dialog_id); From d84aa0927ffac182d621348ae425822ef5ba3668 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 15 Feb 2021 23:16:39 +0300 Subject: [PATCH 143/232] Don't add self-participant before join group call if don't have self-user info. --- td/telegram/GroupCallManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index c7c57c8b3..f8b1b25e6 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -1547,7 +1547,7 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, request->query_ref = td_->create_handler(std::move(query_promise)) ->send(input_group_call_id, json_payload, is_muted, generation); - if (group_call->is_inited) { + if (group_call->is_inited && td_->contacts_manager_->have_user_force(td_->contacts_manager_->get_my_id())) { GroupCallParticipant group_call_participant; group_call_participant.user_id = td_->contacts_manager_->get_my_id(); group_call_participant.audio_source = audio_source; @@ -1566,10 +1566,10 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, need_update = true; update_group_call_dialog(group_call, "join_group_call"); } + } - if (need_update) { - send_update_group_call(group_call, "join_group_call"); - } + if (group_call->is_inited && need_update) { + send_update_group_call(group_call, "join_group_call"); } try_load_group_call_administrators(input_group_call_id, group_call->dialog_id); From 4b2db994d0743a1ca4e1f7333b473b5eeb3c0236 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 16 Feb 2021 13:08:58 +0300 Subject: [PATCH 144/232] Postpone group call actions till join if possible. --- td/telegram/GroupCallManager.cpp | 96 ++++++++++++++++++++++++++++++-- td/telegram/GroupCallManager.h | 2 + 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index f8b1b25e6..53de8a21d 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -425,6 +425,7 @@ struct GroupCallManager::GroupCall { int32 duration = 0; int32 audio_source = 0; int32 joined_date = 0; + vector> after_join; bool have_pending_mute_new_participants = false; bool pending_mute_new_participants = false; @@ -1739,6 +1740,7 @@ bool GroupCallManager::on_join_group_call_response(InputGroupCallId input_group_ } pending_join_requests_.erase(it); try_clear_group_call_participants(input_group_call_id); + process_group_call_after_join_requests(input_group_call_id); return need_update; } @@ -1751,6 +1753,34 @@ void GroupCallManager::finish_join_group_call(InputGroupCallId input_group_call_ it->second->promise.set_error(std::move(error)); pending_join_requests_.erase(it); try_clear_group_call_participants(input_group_call_id); + process_group_call_after_join_requests(input_group_call_id); +} + +void GroupCallManager::process_group_call_after_join_requests(InputGroupCallId input_group_call_id) { + GroupCall *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited) { + return; + } + if (pending_join_requests_.count(input_group_call_id) != 0 || group_call->need_rejoin) { + LOG(ERROR) << "Failed to process after-join requests: " << pending_join_requests_.count(input_group_call_id) << " " + << group_call->need_rejoin; + return; + } + if (group_call->after_join.empty()) { + return; + } + + auto promises = std::move(group_call->after_join); + reset_to_empty(group_call->after_join); + if (!group_call->is_active || !group_call->is_joined) { + for (auto &promise : promises) { + promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); + } + } else { + for (auto &promise : promises) { + promise.set_value(Unit()); + } + } } void GroupCallManager::toggle_group_call_mute_new_participants(GroupCallId group_call_id, bool mute_new_participants, @@ -1863,7 +1893,23 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_ TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); auto *group_call = get_group_call(input_group_call_id); - if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) { + return promise.set_value(Unit()); + } + if (!group_call->is_joined) { + if (pending_join_requests_.count(input_group_call_id) || group_call->need_rejoin) { + group_call->after_join.push_back( + PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, audio_source, is_speaking, + promise = std::move(promise), date](Result &&result) mutable { + if (result.is_error()) { + promise.set_value(Unit()); + } else { + send_closure(actor_id, &GroupCallManager::set_group_call_participant_is_speaking, group_call_id, + audio_source, is_speaking, std::move(promise), date); + } + })); + return; + } return promise.set_value(Unit()); } if (audio_source == 0) { @@ -1926,7 +1972,23 @@ void GroupCallManager::toggle_group_call_participant_is_muted(GroupCallId group_ TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); auto *group_call = get_group_call(input_group_call_id); - if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) { + return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); + } + if (!group_call->is_joined) { + if (pending_join_requests_.count(input_group_call_id) || group_call->need_rejoin) { + group_call->after_join.push_back( + PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, user_id, is_muted, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); + } else { + send_closure(actor_id, &GroupCallManager::toggle_group_call_participant_is_muted, group_call_id, user_id, + is_muted, std::move(promise)); + } + })); + return; + } return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); } if (!td_->contacts_manager_->have_input_user(user_id)) { @@ -2007,7 +2069,23 @@ void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group } auto *group_call = get_group_call(input_group_call_id); - if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) { + if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) { + return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); + } + if (!group_call->is_joined) { + if (pending_join_requests_.count(input_group_call_id) || group_call->need_rejoin) { + group_call->after_join.push_back( + PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, user_id, volume_level, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); + } else { + send_closure(actor_id, &GroupCallManager::set_group_call_participant_volume_level, group_call_id, user_id, + volume_level, std::move(promise)); + } + })); + return; + } return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); } if (!td_->contacts_manager_->have_input_user(user_id)) { @@ -2109,12 +2187,14 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise group_call->is_being_left) { if (cancel_join_group_call_request(input_group_call_id) != 0) { try_clear_group_call_participants(input_group_call_id); + process_group_call_after_join_requests(input_group_call_id); return promise.set_value(Unit()); } if (group_call->need_rejoin) { group_call->need_rejoin = false; send_update_group_call(group_call, "leave_group_call"); try_clear_group_call_participants(input_group_call_id); + process_group_call_after_join_requests(input_group_call_id); return promise.set_value(Unit()); } return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); @@ -2127,6 +2207,8 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise group_call->need_rejoin = false; send_update_group_call(group_call, "leave_group_call"); + process_group_call_after_join_requests(input_group_call_id); + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, audio_source, promise = std::move(promise)](Result &&result) mutable { if (result.is_ok()) { @@ -2165,7 +2247,9 @@ void GroupCallManager::on_group_call_left_impl(GroupCall *group_call, bool need_ group_call->joined_date = 0; group_call->audio_source = 0; check_group_call_is_joined_timeout_.cancel_timeout(group_call->group_call_id.get()); - try_clear_group_call_participants(get_input_group_call_id(group_call->group_call_id).ok()); + auto input_group_call_id = get_input_group_call_id(group_call->group_call_id).ok(); + try_clear_group_call_participants(input_group_call_id); + process_group_call_after_join_requests(input_group_call_id); } void GroupCallManager::discard_group_call(GroupCallId group_call_id, Promise &&promise) { @@ -2297,6 +2381,10 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrafter_join); + for (auto &promise : promises) { + promise.set_error(Status::Error(400, "Group call ended")); + } *group_call = std::move(call); need_update = true; } else { diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 9c75e6de6..316b1cbab 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -171,6 +171,8 @@ class GroupCallManager : public Actor { void finish_join_group_call(InputGroupCallId input_group_call_id, uint64 generation, Status error); + void process_group_call_after_join_requests(InputGroupCallId input_group_call_id); + GroupCallParticipants *add_group_call_participants(InputGroupCallId input_group_call_id); GroupCallParticipant *get_group_call_participant(InputGroupCallId input_group_call_id, UserId user_id); From cf5eb35ece8f262f296fa37d1d52064050ccccbe Mon Sep 17 00:00:00 2001 From: aCLr Date: Thu, 18 Feb 2021 13:20:40 +0300 Subject: [PATCH 145/232] change rust-tdlib link (#1421) --- example/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/README.md b/example/README.md index 17ecd1646..f5f6adf94 100644 --- a/example/README.md +++ b/example/README.md @@ -172,9 +172,11 @@ TDLib can be used from the Rust programming language through the [JSON](https:// See [tdlib-rs](https://github.com/agnipau/tdlib-rs), which contains automatically generated classes for all TDLib API methods and objects. See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures), -[tdlib-sys](https://github.com/nuxeh/tdlib-sys), [rust-tdlib](https://github.com/lattenwald/rust-tdlib), or +[tdlib-sys](https://github.com/nuxeh/tdlib-sys), or [tdjson-rs](https://github.com/mersinvald/tdjson-rs) for examples of TDLib Rust bindings. +You can use [rust-tdlib](https://github.com/aCLr/rust-tdlib) if you need high-level client implementation. + ## Using TDLib in Erlang projects From 9c478e21e761e2cecb87c9b4df38f3fb0d551b7a Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 18 Feb 2021 13:24:01 +0300 Subject: [PATCH 146/232] Improve example README. --- example/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/example/README.md b/example/README.md index f5f6adf94..2e7273645 100644 --- a/example/README.md +++ b/example/README.md @@ -169,14 +169,12 @@ See [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](ht TDLib can be used from the Rust programming language through the [JSON](https://github.com/tdlib/td#using-json) interface. -See [tdlib-rs](https://github.com/agnipau/tdlib-rs), which contains automatically generated classes for all TDLib API methods and objects. +See [rust-tdlib](https://github.com/aCLr/rust-tdlib) or [tdlib-rs](https://github.com/agnipau/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects. See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures), [tdlib-sys](https://github.com/nuxeh/tdlib-sys), or [tdjson-rs](https://github.com/mersinvald/tdjson-rs) for examples of TDLib Rust bindings. -You can use [rust-tdlib](https://github.com/aCLr/rust-tdlib) if you need high-level client implementation. - ## Using TDLib in Erlang projects From 143846f09beb292211ac33d4e45cd2dd63b7f2e0 Mon Sep 17 00:00:00 2001 From: Arseny Smirnov Date: Wed, 24 Feb 2021 18:43:09 +0300 Subject: [PATCH 147/232] cryptography draft fixes --- test/crypto.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index ffbba3cd8..198f02243 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -15,7 +15,6 @@ #include "td/mtproto/KDF.h" #include "td/mtproto/Transport.h" - class Handshake { public: struct KeyPair { @@ -184,18 +183,24 @@ void KDF2(Slice auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt1 aes_iv_slice.substr(4).copy_from(sha256_a.substr(8, 8)); aes_iv_slice.substr(12).copy_from(sha256_b.substr(24, 4)); } -} +} // namespace td -td::SecureString encrypt(td::Slice key, td::Slice data, td::int32 seqno, int X) { +td::SecureString encrypt(td::Slice key, td::Slice data, td::int32 seqno, int X) { td::SecureString res(data.size() + 4 + 16); res.as_mutable_slice().substr(20).copy_from(data); - td::TlStorerUnsafe storer(res.as_mutable_slice().substr(16).ubegin()); - storer.store_int(seqno); + + // big endian + td::uint8 *ptr = res.as_mutable_slice().substr(16).ubegin(); + ptr[0] = (seqno >> 24) & 255; + ptr[1] = (seqno >> 16) & 255; + ptr[2] = (seqno >> 8) & 255; + ptr[3] = (seqno)&255; + td::mtproto::AuthKey auth_key(0, key.str()); auto payload = res.as_mutable_slice().substr(16); - td::UInt128 msg_key = td::mtproto::Transport::calc_message_key2(auth_key, X, payload).second; - td::UInt256 aes_key; - td::UInt128 aes_iv; + td::UInt128 msg_key = td::mtproto::Transport::calc_message_key2(auth_key, X, payload).second; + td::UInt256 aes_key; + td::UInt128 aes_iv; td::KDF2(key, msg_key, X, &aes_key, &aes_iv); td::AesCtrState aes; aes.init(aes_key.as_slice(), aes_iv.as_slice()); @@ -214,7 +219,6 @@ HandshakeTest gen_test() { return res; } - void run_test(const HandshakeTest &test) { auto alice_secret = Handshake::calc_shared_secret(test.alice.private_key, test.bob.public_key).move_as_ok(); auto bob_secret = Handshake::calc_shared_secret(test.bob.private_key, test.alice.public_key).move_as_ok(); @@ -255,7 +259,6 @@ HandshakeTest pregenerated_test() { "HYe8tJdEwHcmlnXq7LtprigzExaNJS7skfOo2irClj-7EL06-jMrhfwngSJFsak8JFSw8s6R4fwCsr50") .move_as_ok(); - return test; } From 642a9a9efbb9b833f49c46b77231df541e699880 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Feb 2021 02:00:28 +0300 Subject: [PATCH 148/232] Update layer to 124. --- td/generate/scheme/td_api.tl | 37 +++++++++++++++ td/generate/scheme/td_api.tlo | Bin 194284 -> 195528 bytes td/generate/scheme/telegram_api.tl | 70 +++++++++++++++++++++------- td/generate/scheme/telegram_api.tlo | Bin 227148 -> 233316 bytes td/telegram/ConfigManager.cpp | 3 +- td/telegram/ContactsManager.cpp | 32 ++++++------- td/telegram/ContactsManager.h | 4 +- td/telegram/DialogParticipant.cpp | 2 +- td/telegram/MessagesManager.cpp | 54 ++++++++++++--------- td/telegram/MessagesManager.h | 4 +- td/telegram/Td.cpp | 4 +- td/telegram/Td.h | 4 +- td/telegram/UpdatesManager.cpp | 17 ++++++- td/telegram/UpdatesManager.h | 12 ++++- td/telegram/Version.h | 2 +- td/telegram/cli.cpp | 2 - 16 files changed, 171 insertions(+), 76 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index dc7e6486c..0f89c6d6c 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4419,6 +4419,43 @@ importMessages chat_id:int53 message_file:InputFile attached_files:vectorLQ&MwMOHv(ka)MIJ^0QM@ zP)#VF-eAP!vVDm+(*hQSdLH$O(h&7n6;FR~mdS8(t*7vG13M;>X}6gKx4Q-~nX||o z{`Ky@P-;qM398{x$9)4juEtXYq={#8zyj9EGb`QUF0Bb+DiGtCCAsaWM0#q;2J>AJg_l!b+$24{l9H9iLzu+#JR zGMP>;*vL6~Wi9{qH(5+y+!a1*L@ admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; +channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#f3474af6 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; -channelFull#7a7de4f7 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall = ChatFull; +chatFull#f06c4018 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int = ChatFull; +channelFull#2548c037 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -128,8 +128,8 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto; messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; -message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector = Message; -messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message; +message#bce383d2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector ttl_period:flags.25?int = Message; +messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message; messageMediaEmpty#3ded6320 = MessageMedia; messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia; @@ -171,6 +171,7 @@ messageActionContactSignUp#f3f25f76 = MessageAction; messageActionGeoProximityReached#98e0d697 from_id:Peer to_id:Peer distance:int = MessageAction; messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int = MessageAction; messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector = MessageAction; +messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction; dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; @@ -213,12 +214,12 @@ inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; inputReportReasonPornography#2e59d922 = ReportReason; inputReportReasonChildAbuse#adf44ee3 = ReportReason; -inputReportReasonOther#e1746d0a text:string = ReportReason; +inputReportReasonOther#c1e4a2b1 = ReportReason; inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; inputReportReasonFake#f5ddd6e7 = ReportReason; -userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull; +userFull#139a9a77 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int = UserFull; contact#f911c994 user_id:int mutual:Bool = Contact; @@ -345,7 +346,6 @@ updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update; updateDialogFilterOrder#a5d72105 order:Vector = Update; updateDialogFilters#3504914f = Update; updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update; -updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update; updateChannelMessageForwards#6e8a84df channel_id:int id:int forwards:int = Update; updateReadChannelDiscussionInbox#1cc7de54 flags:# channel_id:int top_msg_id:int read_max_id:int broadcast_id:flags.0?int broadcast_post:flags.0?int = Update; updateReadChannelDiscussionOutbox#4638a26c channel_id:int top_msg_id:int read_max_id:int = Update; @@ -356,6 +356,10 @@ updatePinnedChannelMessages#8588878b flags:# pinned:flags.0?true channel_id:int updateChat#1330a196 chat_id:int = Update; updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector version:int = Update; updateGroupCall#a45eb99b chat_id:int call:GroupCall = Update; +updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update; +updateChatParticipant#439b2b39 flags:# chat_id:int date:int user_id:int prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update; +updateChannelParticipant#961adf2 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update; +updateBotStopped#7f9488a user_id:int date:int stopped:Bool qts:int = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -365,12 +369,12 @@ updates.differenceSlice#a8fb1981 new_messages:Vector new_encrypted_mess updates.differenceTooLong#4afe8f6d pts:int = updates.Difference; updatesTooLong#e317af7e = Updates; -updateShortMessage#2296d2c8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector = Updates; -updateShortChatMessage#402d5dbb flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector = Updates; +updateShortMessage#faeff833 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector ttl_period:flags.25?int = Updates; +updateShortChatMessage#1157b858 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector ttl_period:flags.25?int = Updates; updateShort#78d4dec1 update:Update date:int = Updates; updatesCombined#725b04c3 updates:Vector users:Vector chats:Vector date:int seq_start:int seq:int = Updates; updates#74ae4240 updates:Vector users:Vector chats:Vector date:int seq:int = Updates; -updateShortSentMessage#11f1331c flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector = Updates; +updateShortSentMessage#9015e101 flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector ttl_period:flags.25?int = Updates; photos.photos#8dca6aa5 photos:Vector users:Vector = photos.Photos; photos.photosSlice#15051f54 count:int photos:Vector users:Vector = photos.Photos; @@ -873,12 +877,18 @@ channelAdminLogEventActionDiscardGroupCall#db9f9140 call:InputGroupCall = Channe channelAdminLogEventActionParticipantMute#f92424d2 participant:GroupCallParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionParticipantUnmute#e64429c0 participant:GroupCallParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionToggleGroupCallSetting#56d6a247 join_muted:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantJoinByInvite#5cdada77 invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteDelete#5a50fca4 invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction; channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent; channels.adminLogResults#ed8af74d events:Vector chats:Vector users:Vector = channels.AdminLogResults; -channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true = ChannelAdminLogEventsFilter; +channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true = ChannelAdminLogEventsFilter; popularContact#5ce14175 client_id:long importers:int = PopularContact; @@ -1036,7 +1046,7 @@ chatOnlines#f041e250 onlines:int = ChatOnlines; statsURL#47a971e0 url:string = StatsURL; -chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true = ChatAdminRights; +chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true = ChatAdminRights; chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights; @@ -1201,6 +1211,21 @@ messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector = messages.AffectedFoundMessages; +chatInviteImporter#1e3e6680 user_id:int date:int = ChatInviteImporter; + +messages.exportedChatInvites#bdc62dcc count:int invites:Vector users:Vector = messages.ExportedChatInvites; + +messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector = messages.ExportedChatInvite; +messages.exportedChatInviteReplaced#222600ef invite:ExportedChatInvite new_invite:ExportedChatInvite users:Vector = messages.ExportedChatInvite; + +messages.chatInviteImporters#81b6b00a count:int importers:Vector users:Vector = messages.ChatInviteImporters; + +chatAdminWithInvites#dfd2330f admin_id:int invites_count:int revoked_invites_count:int = ChatAdminWithInvites; + +messages.chatAdminsWithInvites#b69b72d7 admins:Vector users:Vector = messages.ChatAdminsWithInvites; + +messages.checkedHistoryImportPeer#a24de717 confirm_text:string = messages.CheckedHistoryImportPeer; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1238,7 +1263,7 @@ account.resetNotifySettings#db7e1747 = Bool; account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User; account.updateStatus#6628562c offline:Bool = Bool; account.getWallPapers#aabb1763 hash:int = account.WallPapers; -account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool; +account.reportPeer#c5ba3d86 peer:InputPeer reason:ReportReason message:string = Bool; account.checkUsername#2714d86c username:string = Bool; account.updateUsername#3e0bdd7c username:string = User; account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules; @@ -1297,6 +1322,7 @@ account.getContentSettings#8b9b4dae = account.ContentSettings; account.getMultiWallPapers#65ad71dc wallpapers:Vector = Vector; account.getGlobalPrivacySettings#eb2b4cf6 = GlobalPrivacySettings; account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = GlobalPrivacySettings; +account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1337,7 +1363,7 @@ messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer schedule_date:flags.10?int = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; -messages.report#bd82b658 peer:InputPeer id:Vector reason:ReportReason = Bool; +messages.report#8953ab4e peer:InputPeer id:Vector reason:ReportReason message:string = Bool; messages.getChats#3c6aa187 id:Vector = messages.Chats; messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; messages.editChatTitle#dc452855 chat_id:int title:string = Updates; @@ -1360,7 +1386,7 @@ messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; +messages.exportChatInvite#14b9bcd7 flags:# legacy_revoke_permanent:flags.2?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1456,6 +1482,15 @@ messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImport messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool; +messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.getExportedChatInvite#73746f5c peer:InputPeer link:string = messages.ExportedChatInvite; +messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; +messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:InputUser = Bool; +messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; +messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites; +messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; +messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates; +messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1495,7 +1530,7 @@ help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo; help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector = help.UserInfo; help.getPromoData#c0977421 = help.PromoData; help.hidePromoData#1e251c95 peer:InputPeer = Bool; -help.dismissSuggestion#77fa99f suggestion:string = Bool; +help.dismissSuggestion#f50dbaa1 peer:InputPeer suggestion:string = Bool; help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; @@ -1533,6 +1568,7 @@ channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:In channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool; channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates; channels.getInactiveChannels#11e831ee = messages.InactiveChats; +channels.convertToGigagroup#b290c69 channel:InputChannel = Updates; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index 450582c74b0a77d74a08f9b491af26f24e219f58..f9bb1d3b8a9c98a42f80d4fdad8825228ff6b206 100644 GIT binary patch delta 9368 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 diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 6dcc93712..80515e326 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -1097,7 +1097,8 @@ void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, P queries.push_back(std::move(promise)); if (queries.size() == 1) { G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create(telegram_api::help_dismissSuggestion(action_str)), + G()->net_query_creator().create( + telegram_api::help_dismissSuggestion(make_tl_object(), action_str)), actor_shared(this, 100 + static_cast(suggested_action))); } } diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 448f4caf5..e97ce4311 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1582,9 +1582,7 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - CHECK(is_permanent); - /* int32 flags = 0; if (expire_date > 0) { flags |= telegram_api::messages_exportChatInvite::EXPIRE_DATE_MASK; @@ -1595,11 +1593,9 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { if (is_permanent) { flags |= telegram_api::messages_exportChatInvite::LEGACY_REVOKE_PERMANENT_MASK; } - */ - send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite(std::move(input_peer)))); - // send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( - // flags, false /*ignored*/, std::move(input_peer), expire_date, usage_limit))); + send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( + flags, false /*ignored*/, std::move(input_peer), expire_date, usage_limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1629,7 +1625,7 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { promise_.set_error(std::move(status)); } }; -/* + class EditChatInviteLinkQuery : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -1652,7 +1648,7 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { flags |= telegram_api::messages_editExportedChatInvite::REVOKED_MASK; } send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( - flags, false / *ignored* /, std::move(input_peer), invite_link, expire_date, usage_limit))); + flags, false /*ignored*/, std::move(input_peer), invite_link, expire_date, usage_limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1661,7 +1657,7 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - auto result = result_ptr.move_as_ok(); + auto result = move_tl_object_as(result_ptr.move_as_ok()); LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(result); td->contacts_manager_->on_get_users(std::move(result->users_), "EditChatInviteLinkQuery"); @@ -1697,11 +1693,9 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { } auto input_user = td->contacts_manager_->get_input_user(administrator_user_id); + CHECK(input_user != nullptr); int32 flags = 0; - if (input_user != nullptr) { - flags |= telegram_api::messages_getExportedChatInvites::ADMIN_ID_MASK; - } if (!offset_invite_link.empty() || offset_date != 0) { flags |= telegram_api::messages_getExportedChatInvites::OFFSET_DATE_MASK; flags |= telegram_api::messages_getExportedChatInvites::OFFSET_LINK_MASK; @@ -1710,7 +1704,7 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { flags |= telegram_api::messages_getExportedChatInvites::REVOKED_MASK; } send_query(G()->net_query_creator().create( - telegram_api::messages_getExportedChatInvites(flags, false / *ignored* /, std::move(input_peer), + telegram_api::messages_getExportedChatInvites(flags, false /*ignored*/, std::move(input_peer), std::move(input_user), offset_date, offset_invite_link, limit))); } @@ -1859,8 +1853,8 @@ class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query(G()->net_query_creator().create( - telegram_api::messages_deleteRevokedExportedChatInvites(std::move(input_peer)))); + send_query(G()->net_query_creator().create(telegram_api::messages_deleteRevokedExportedChatInvites( + std::move(input_peer), make_tl_object()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1877,7 +1871,7 @@ class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { promise_.set_error(std::move(status)); } }; -*/ + class CheckDialogInviteLinkQuery : public Td::ResultHandler { Promise promise_; string invite_link_; @@ -6881,7 +6875,7 @@ void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 e td_->create_handler(std::move(promise)) ->send(dialog_id, expire_date, usage_limit, is_permanent); } -/* + void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise) { @@ -6900,7 +6894,7 @@ void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId adminis Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - if (administrator_user_id != UserId() && !have_input_user(administrator_user_id)) { + if (!have_input_user(administrator_user_id)) { return promise.set_error(Status::Error(400, "Administrator user not found")); } @@ -6944,7 +6938,7 @@ void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, td_->create_handler(std::move(promise))->send(dialog_id); } -*/ + void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { if (invite_link_infos_.count(invite_link) > 0) { return promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 713d98c48..cca40271f 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -385,7 +385,7 @@ class ContactsManager : public Actor { void export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent, Promise> &&promise); - /* + void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise); @@ -400,7 +400,7 @@ class ContactsManager : public Actor { void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); void delete_all_revoked_dialog_invite_links(DialogId dialog_id, Promise &&promise); - */ + void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; void import_dialog_invite_link(const string &invite_link, Promise &&promise); diff --git a/td/telegram/DialogParticipant.cpp b/td/telegram/DialogParticipant.cpp index 65ff4c06a..7bd29a4e8 100644 --- a/td/telegram/DialogParticipant.cpp +++ b/td/telegram/DialogParticipant.cpp @@ -171,7 +171,7 @@ tl_object_ptr DialogParticipantStatus::get_chat_a LOG(INFO) << "Create chat admin rights " << flags; return make_tl_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/); + false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/); } tl_object_ptr DialogParticipantStatus::get_chat_banned_rights() const { diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 284a44299..3fccaf369 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -2805,16 +2805,19 @@ class SendMessageActor : public NetActorOnce { std::move(sent_message->entities_)); auto message_id = MessageId(ServerMessageId(sent_message->id_)); + auto ttl_period = (sent_message->flags_ & telegram_api::updateShortSentMessage::TTL_PERIOD_MASK) != 0 + ? sent_message->ttl_period_ + : 0; + auto update = make_tl_object(random_id_, message_id, sent_message->date_, ttl_period); if (dialog_id_.get_type() == DialogType::Channel) { - td->messages_manager_->add_pending_channel_update( - dialog_id_, make_tl_object(random_id_, message_id, sent_message->date_), - sent_message->pts_, sent_message->pts_count_, Promise(), "send message actor"); + td->messages_manager_->add_pending_channel_update(dialog_id_, std::move(update), sent_message->pts_, + sent_message->pts_count_, Promise(), + "send message actor"); return; } - td->updates_manager_->add_pending_pts_update( - make_tl_object(random_id_, message_id, sent_message->date_), sent_message->pts_, - sent_message->pts_count_, Promise(), "send message actor"); + td->updates_manager_->add_pending_pts_update(std::move(update), sent_message->pts_, sent_message->pts_count_, + Promise(), "send message actor"); } void on_error(uint64 id, Status status) override { @@ -4354,10 +4357,11 @@ class ReportPeerQuery : public Td::ResultHandler { if (message_ids.empty()) { send_query(G()->net_query_creator().create( - telegram_api::account_reportPeer(std::move(input_peer), std::move(report_reason)))); + telegram_api::account_reportPeer(std::move(input_peer), std::move(report_reason), string()))); } else { - send_query(G()->net_query_creator().create(telegram_api::messages_report( - std::move(input_peer), MessagesManager::get_server_message_ids(message_ids), std::move(report_reason)))); + send_query(G()->net_query_creator().create( + telegram_api::messages_report(std::move(input_peer), MessagesManager::get_server_message_ids(message_ids), + std::move(report_reason), string()))); } } @@ -6385,7 +6389,8 @@ void MessagesManager::skip_old_pending_pts_update(tl_object_ptrrandom_id_, update_sent_message->message_id_, - update_sent_message->date_, FileId(), "process old updateSentMessage"); + update_sent_message->date_, update_sent_message->ttl_period_, FileId(), + "process old updateSentMessage"); return; } else { LOG(ERROR) << "Receive awaited sent " << update_sent_message->message_id_ << " from " << source << " with pts " @@ -7241,7 +7246,8 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p if (being_sent_messages_.count(update_sent_message->random_id_) > 0) { // apply sent channel message on_send_message_success(update_sent_message->random_id_, update_sent_message->message_id_, - update_sent_message->date_, FileId(), "process old updateSentChannelMessage"); + update_sent_message->date_, update_sent_message->ttl_period_, FileId(), + "process old updateSentChannelMessage"); promise.set_value(Unit()); return; } @@ -7314,10 +7320,11 @@ void MessagesManager::process_pts_update(tl_object_ptr &&u break; } case updateSentMessage::ID: { - auto send_message_success_update = move_tl_object_as(update); - LOG(INFO) << "Process updateSentMessage " << send_message_success_update->random_id_; - on_send_message_success(send_message_success_update->random_id_, send_message_success_update->message_id_, - send_message_success_update->date_, FileId(), "process updateSentMessage"); + auto update_sent_message = move_tl_object_as(update); + LOG(INFO) << "Process updateSentMessage " << update_sent_message->random_id_; + on_send_message_success(update_sent_message->random_id_, update_sent_message->message_id_, + update_sent_message->date_, update_sent_message->ttl_period_, FileId(), + "process updateSentMessage"); break; } case telegram_api::updateReadMessagesContents::ID: { @@ -7388,10 +7395,11 @@ void MessagesManager::process_channel_update(tl_object_ptr LOG(INFO) << "Process dummyUpdate"; break; case updateSentMessage::ID: { - auto send_message_success_update = move_tl_object_as(update); - LOG(INFO) << "Process updateSentMessage " << send_message_success_update->random_id_; - on_send_message_success(send_message_success_update->random_id_, send_message_success_update->message_id_, - send_message_success_update->date_, FileId(), "process updateSentChannelMessage"); + auto update_sent_message = move_tl_object_as(update); + LOG(INFO) << "Process updateSentMessage " << update_sent_message->random_id_; + on_send_message_success(update_sent_message->random_id_, update_sent_message->message_id_, + update_sent_message->date_, update_sent_message->ttl_period_, FileId(), + "process updateSentChannelMessage"); break; } case telegram_api::updateNewChannelMessage::ID: { @@ -8187,7 +8195,7 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr(text); + report_reason = make_tl_object(); break; } default: @@ -12607,7 +12615,7 @@ void MessagesManager::on_send_secret_message_success(int64 random_id, MessageId } } - on_send_message_success(random_id, message_id, date, new_file_id, "process send_secret_message_success"); + on_send_message_success(random_id, message_id, date, 0, new_file_id, "process send_secret_message_success"); } void MessagesManager::delete_secret_messages(SecretChatId secret_chat_id, std::vector random_ids, @@ -28453,7 +28461,7 @@ void MessagesManager::check_send_message_result(int64 random_id, DialogId dialog } FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageId new_message_id, int32 date, - FileId new_file_id, const char *source) { + int32 ttl_period, FileId new_file_id, const char *source) { CHECK(source != nullptr); // do not try to run getDifference from this function if (DROP_SEND_MESSAGE_UPDATES) { @@ -30876,7 +30884,7 @@ tl_object_ptr MessagesManager::get_ch return make_tl_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/); + false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/); } int64 MessagesManager::get_dialog_event_log(DialogId dialog_id, const string &query, int64 from_event_id, int32 limit, diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 37d9e973a..641a68c50 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -799,8 +799,8 @@ class MessagesManager : public Actor { void check_send_message_result(int64 random_id, DialogId dialog_id, const telegram_api::Updates *updates_ptr, const char *source); - FullMessageId on_send_message_success(int64 random_id, MessageId new_message_id, int32 date, FileId new_file_id, - const char *source); + FullMessageId on_send_message_success(int64 random_id, MessageId new_message_id, int32 date, int32 ttl_period, + FileId new_file_id, const char *source); void on_send_message_file_part_missing(int64 random_id, int bad_part); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index e735c6b7b..ac9c5fc3a 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6308,7 +6308,7 @@ void Td::on_request(uint64 id, const td_api::replacePermanentChatInviteLink &req CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), 0, 0, true, std::move(promise)); } -/* + void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.member_limit_, @@ -6356,7 +6356,7 @@ void Td::on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &re CREATE_OK_REQUEST_PROMISE(); contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), std::move(promise)); } -*/ + void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index d46ca943f..789112c81 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -793,7 +793,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatAdministrators &request); void on_request(uint64 id, const td_api::replacePermanentChatInviteLink &request); - /* + void on_request(uint64 id, const td_api::createChatInviteLink &request); void on_request(uint64 id, td_api::editChatInviteLink &request); @@ -807,7 +807,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request); void on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request); - */ + void on_request(uint64 id, td_api::checkChatInviteLink &request); void on_request(uint64 id, td_api::joinChatByInviteLink &request); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 1dbd0d80e..a68797ee3 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -788,7 +788,8 @@ void UpdatesManager::on_get_updates(tl_object_ptr &&updat false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, update->id_, make_tl_object(from_id), make_tl_object(update->user_id_), std::move(update->fwd_from_), update->via_bot_id_, std::move(update->reply_to_), update->date_, - update->message_, nullptr, nullptr, std::move(update->entities_), 0, 0, nullptr, 0, string(), 0, Auto()); + update->message_, nullptr, nullptr, std::move(update->entities_), 0, 0, nullptr, 0, string(), 0, Auto(), + update->ttl_period_); on_pending_update( make_tl_object(std::move(message), update->pts_, update->pts_count_), 0, std::move(promise), "telegram_api::updatesShortMessage"); @@ -812,7 +813,7 @@ void UpdatesManager::on_get_updates(tl_object_ptr &&updat make_tl_object(update->from_id_), make_tl_object(update->chat_id_), std::move(update->fwd_from_), update->via_bot_id_, std::move(update->reply_to_), update->date_, update->message_, nullptr, nullptr, std::move(update->entities_), - 0, 0, nullptr, 0, string(), 0, Auto()); + 0, 0, nullptr, 0, string(), 0, Auto(), update->ttl_period_); on_pending_update( make_tl_object(std::move(message), update->pts_, update->pts_count_), 0, std::move(promise), "telegram_api::updatesShortChatMessage"); @@ -2845,8 +2846,20 @@ void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + promise.set_value(Unit()); +} + +void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + promise.set_value(Unit()); +} + void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { promise.set_value(Unit()); } +void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + promise.set_value(Unit()); +} + } // namespace td diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 945e36d53..0ee5d820f 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -59,9 +59,10 @@ class updateSentMessage : public telegram_api::Update { int64 random_id_; MessageId message_id_; int32 date_; + int32 ttl_period_; - updateSentMessage(int64 random_id, MessageId message_id, int32 date) - : random_id_(random_id), message_id_(message_id), date_(date) { + updateSentMessage(int64 random_id, MessageId message_id, int32 date, int32 ttl_period) + : random_id_(random_id), message_id_(message_id), date_(date), ttl_period_(ttl_period) { } static constexpr int32 ID = 1234567890; @@ -82,6 +83,7 @@ class updateSentMessage : public telegram_api::Update { s.store_field("random_id", random_id_); s.store_field("message_id", message_id_.get()); s.store_field("date", date_); + s.store_field("ttl_period", ttl_period_); s.store_class_end(); } }; @@ -452,7 +454,13 @@ class UpdatesManager : public Actor { // unsupported updates + void on_update(tl_object_ptr update, Promise &&promise); + + void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); + + void on_update(tl_object_ptr update, Promise &&promise); }; } // namespace td diff --git a/td/telegram/Version.h b/td/telegram/Version.h index af435abeb..11d98bf11 100644 --- a/td/telegram/Version.h +++ b/td/telegram/Version.h @@ -8,7 +8,7 @@ namespace td { -constexpr int32 MTPROTO_LAYER = 123; +constexpr int32 MTPROTO_LAYER = 124; enum class Version : int32 { Initial, // 0 diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 6d7c4c258..7321d6a21 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2696,7 +2696,6 @@ class CliClient final : public Actor { } else if (op == "rpcil") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id))); - /* } else if (op == "ccilt") { string chat_id; int32 expire_date; @@ -2744,7 +2743,6 @@ class CliClient final : public Actor { } else if (op == "darcil") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id))); - */ } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From 0cdb0388ee2e5f21eb4d3babc15921f278b91194 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Feb 2021 02:33:48 +0300 Subject: [PATCH 149/232] Improve TTL spelling. --- td/telegram/MessagesManager.cpp | 18 +++++++++--------- td/telegram/Photo.cpp | 2 +- td/telegram/Td.cpp | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 3fccaf369..80e736f8d 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -8572,7 +8572,7 @@ void MessagesManager::do_send_media(DialogId dialog_id, Message *m, FileId file_ bool have_input_thumbnail = input_thumbnail != nullptr; LOG(INFO) << "Do send media file " << file_id << " with thumbnail " << thumbnail_file_id << ", have_input_file = " << have_input_file << ", have_input_thumbnail = " << have_input_thumbnail - << ", ttl = " << m->ttl; + << ", TTL = " << m->ttl; MessageContent *content = nullptr; if (m->message_id.is_any_server()) { @@ -12906,13 +12906,13 @@ void MessagesManager::on_secret_chat_screenshot_taken(SecretChatId secret_chat_i void MessagesManager::on_secret_chat_ttl_changed(SecretChatId secret_chat_id, UserId user_id, MessageId message_id, int32 date, int32 ttl, int64 random_id, Promise<> promise) { - LOG(DEBUG) << "On ttl set in " << secret_chat_id << " to " << ttl; + LOG(DEBUG) << "On TTL set in " << secret_chat_id << " to " << ttl; CHECK(secret_chat_id.is_valid()); CHECK(user_id.is_valid()); CHECK(message_id.is_valid()); CHECK(date > 0); if (ttl < 0) { - LOG(WARNING) << "Receive wrong ttl = " << ttl; + LOG(WARNING) << "Receive wrong TTL = " << ttl; promise.set_value(Unit()); return; } @@ -13227,7 +13227,7 @@ std::pair> MessagesManager::creat int32 ttl = message_info.ttl; bool is_content_secret = is_secret_message_content(ttl, content_type); // should be calculated before TTL is adjusted if (ttl < 0) { - LOG(ERROR) << "Wrong ttl = " << ttl << " received in " << message_id << " in " << dialog_id; + LOG(ERROR) << "Wrong TTL = " << ttl << " received in " << message_id << " in " << dialog_id; ttl = 0; } else if (ttl > 0) { ttl = max(ttl, get_message_content_duration(message_info.content.get(), td_) + 1); @@ -25146,7 +25146,7 @@ void MessagesManager::edit_inline_message_media(const string &inline_message_id, } InputMessageContent content = r_input_message_content.move_as_ok(); if (content.ttl > 0) { - LOG(ERROR) << "Have message content with ttl " << content.ttl; + LOG(ERROR) << "Have message content with TTL " << content.ttl; return promise.set_error(Status::Error(5, "Can't enable self-destruction for media")); } @@ -26262,14 +26262,14 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v Result MessagesManager::send_dialog_set_ttl_message(DialogId dialog_id, int32 ttl) { if (dialog_id.get_type() != DialogType::SecretChat) { - return Status::Error(5, "Can't set chat ttl in non-secret chat"); + return Status::Error(5, "Can't set message TTL in non-secret chat"); } if (ttl < 0) { - return Status::Error(5, "Message ttl can't be negative"); + return Status::Error(5, "Message TTL can't be negative"); } - LOG(INFO) << "Begin to set ttl in " << dialog_id << " to " << ttl; + LOG(INFO) << "Begin to set message TTL in " << dialog_id << " to " << ttl; Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -32223,7 +32223,7 @@ MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialo return nullptr; } if (message->ttl != 0 || message->ttl_expires_at != 0) { - LOG(ERROR) << "Tried to add " << message_id << " with ttl " << message->ttl << "/" << message->ttl_expires_at + LOG(ERROR) << "Tried to add " << message_id << " with TTL " << message->ttl << "/" << message->ttl_expires_at << " to " << dialog_id << " from " << source; debug_add_message_to_dialog_fail_reason_ = "skip adding secret scheduled message"; return nullptr; diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index 3d0950f0d..923f0f9a9 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -834,7 +834,7 @@ tl_object_ptr photo_get_input_media(FileManager *file_ if (ttl != 0) { flags |= telegram_api::inputMediaPhotoExternal::TTL_SECONDS_MASK; } - LOG(INFO) << "Create inputMediaPhotoExternal with a URL " << file_view.url() << " and ttl " << ttl; + LOG(INFO) << "Create inputMediaPhotoExternal with a URL " << file_view.url() << " and TTL " << ttl; return make_tl_object(flags, file_view.url(), ttl); } if (input_file == nullptr) { diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index ac9c5fc3a..88e715880 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1869,7 +1869,7 @@ class CreateNewSecretChatRequest : public RequestActor { // But since the update may still be on its way, we will update essential fields here. td->contacts_manager_->on_update_secret_chat( secret_chat_id_, 0 /* no access_hash */, user_id_, SecretChatState::Unknown, true /* it is outbound chat */, - -1 /* unknown ttl */, 0 /* unknown creation date */, "" /* no key_hash */, 0, FolderId()); + -1 /* unknown TTL */, 0 /* unknown creation date */, "" /* no key_hash */, 0, FolderId()); DialogId dialog_id(secret_chat_id_); td->messages_manager_->force_create_dialog(dialog_id, "create new secret chat", true); send_result(td->messages_manager_->get_chat_object(dialog_id)); From f2f68aa3b3b41c056e8db5e7a230e140213cb749 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Feb 2021 19:23:45 +0300 Subject: [PATCH 150/232] Add td_api::getChatInviteLinkCounts. --- td/generate/scheme/td_api.tl | 23 ++++++--- td/generate/scheme/td_api.tlo | Bin 195528 -> 195988 bytes td/telegram/ContactsManager.cpp | 80 ++++++++++++++++++++++++++++---- td/telegram/ContactsManager.h | 5 +- td/telegram/Td.cpp | 16 +++++-- td/telegram/Td.h | 4 +- td/telegram/cli.cpp | 3 ++ 7 files changed, 108 insertions(+), 23 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 0f89c6d6c..812ddb898 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -559,6 +559,12 @@ chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_da //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; +//@description Describes a chat administrator with a number of active chat invite links @user_id Administrator's user identifier @invite_link_count Number of active invite links +chatInviteLinkCount user_id:int32 invite_link_count:int32 = ChatInviteLinkCount; + +//@description Contains a list of chat invite link counts @invite_link_counts List of invite linkcounts +chatInviteLinkCounts invite_link_counts:vector = ChatInviteLinkCounts; + //@description Describes a chat member joined a chat by an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat chatInviteLinkMember user_id:int32 joined_chat_date:int32 = ChatInviteLinkMember; @@ -4425,35 +4431,38 @@ replacePermanentChatInviteLink chat_id:int53 = ChatInviteLink; //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatInviteLink; -//@description Edits a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat +//@description Edits a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links //@chat_id Chat identifier //@invite_link Invite link to be edited //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; -//@description Returns invite links for a chat created by specified administrator. Requires administrator privileges and can_invite_users right in the chat +//@description Returns list of chat administrators with number of active invite links. Requires owner privileges in the chat @chat_id Chat identifier +getChatInviteLinkCounts chat_id:int53 = ChatInviteLinkCounts; + +//@description Returns invite links for a chat created by specified administrator. Requires administrator privileges and can_invite_users right in the chat to get own links and owner privileges to get other links //@chat_id Chat identifier -//@administrator_user_id User identifier of a chat administrator +//@administrator_user_id User identifier of a chat administrator. Must be an identifier of the current user for non-owner //@is_revoked Pass true if revoked links needs to be returned instead of active or expired //@offset_date Creation date of an invite link starting after which to return invite links; use 0 to get results from the beginning //@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning //@limit Maximum number of invite links to return getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool offset_date:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; -//@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @invite_link Invite link for which to return chat members +//@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier @invite_link Invite link for which to return chat members //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return getChatInviteLinkMembers chat_id:int53 invite_link:string offset_member:chatInviteLinkMember limit:int32 = ChatInviteLinkMembers; -//@description Revokes a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat +//@description Revokes a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links //@chat_id Chat identifier //@invite_link Invite link to be revoked revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLink; -//@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier @invite_link Invite link to revoke +//@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier @invite_link Invite link to revoke deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; -//@description Deletes all revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat @chat_id Chat identifier +//@description Deletes all revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier deleteAllRevokedChatInviteLinks chat_id:int53 = Ok; //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index f0ce6efa03642172d6873c9fa58bc45d7fb353e9..87175023800172a7217154367a84f027981af640 100644 GIT binary patch delta 317 zcmX@{oqNh}Zr(?;^{p77KzAeWcXo-}FG_C+J7*-8c;=O5mZbV*=4CtQm*$mBJRl+Q zR`9l&2o|}?7qn$Ivv7FmFy?OdHF0*60UMHxX$VMsy2B$T9Uj38T}HyjBd-G5td(lK~5ewcR6^si6-5 D28Vgr delta 59 zcmbR8oBPCfZr(?;^{p77Kzk$aclOO093DE1*_$_-IJ-6fsowsln(@X4#yQ)s>M*@_ L+^&$%RMH0kfkqbL diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index e97ce4311..0f46266ca 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1578,7 +1578,7 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { void send(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent) { dialog_id_ = dialog_id; - auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } @@ -1637,7 +1637,7 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { void send(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit, bool is_revoked) { dialog_id_ = dialog_id; - auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } @@ -1687,7 +1687,7 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { void send(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, int32 offset_date, const string &offset_invite_link, int32 limit) { dialog_id_ = dialog_id; - auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } @@ -1743,6 +1743,55 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { } }; +class GetChatAdminWithInvitesQuery : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetChatAdminWithInvitesQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create(telegram_api::messages_getAdminsWithInvites(std::move(input_peer)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetChatAdminWithInvitesQuery: " << to_string(result); + + td->contacts_manager_->on_get_users(std::move(result->users_), "GetChatAdminWithInvitesQuery"); + + vector> invite_link_counts; + for (auto &admin : result->admins_) { + UserId user_id(admin->admin_id_); + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive invalid invite link creator " << user_id << " in " << dialog_id_; + continue; + } + invite_link_counts.push_back(td_api::make_object( + td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkCount"), admin->invites_count_)); + } + promise_.set_value(td_api::make_object(std::move(invite_link_counts))); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetChatAdminWithInvitesQuery"); + promise_.set_error(std::move(status)); + } +}; + class GetChatInviteImportersQuery : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -1754,7 +1803,7 @@ class GetChatInviteImportersQuery : public Td::ResultHandler { void send(DialogId dialog_id, const string &invite_link, int32 offset_date, UserId offset_user_id, int32 limit) { dialog_id_ = dialog_id; - auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } @@ -1814,7 +1863,7 @@ class DeleteExportedChatInviteQuery : public Td::ResultHandler { void send(DialogId dialog_id, const string &invite_link) { dialog_id_ = dialog_id; - auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } @@ -1848,7 +1897,7 @@ class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { void send(DialogId dialog_id) { dialog_id_ = dialog_id; - auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } @@ -6809,7 +6858,7 @@ void ContactsManager::transfer_channel_ownership( ->send(channel_id, user_id, std::move(input_check_password)); } -Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id) { +Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id, bool creator_only) { if (!td_->messages_manager_->have_dialog_force(dialog_id)) { return Status::Error(3, "Chat not found"); } @@ -6825,7 +6874,9 @@ Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id) { if (!c->is_active) { return Status::Error(3, "Chat is deactivated"); } - if (!get_chat_status(c).is_administrator() || !get_chat_status(c).can_invite_users()) { + auto status = get_chat_status(c); + bool have_rights = creator_only ? status.is_creator() : status.is_administrator() && status.can_invite_users(); + if (!have_rights) { return Status::Error(3, "Not enough rights to manage chat invite link"); } break; @@ -6835,7 +6886,9 @@ Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id) { if (c == nullptr) { return Status::Error(3, "Chat info not found"); } - if (!get_channel_status(c).is_administrator() || !get_channel_status(c).can_invite_users()) { + auto status = get_channel_status(c); + bool have_rights = creator_only ? status.is_creator() : status.is_administrator() && status.can_invite_users(); + if (!have_rights) { return Status::Error(3, "Not enough rights to manage chat invite link"); } break; @@ -6889,10 +6942,17 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & ->send(dialog_id, invite_link, expire_date, usage_limit, is_revoked); } +void ContactsManager::get_dialog_invite_link_counts( + DialogId dialog_id, Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, true)); + + td_->create_handler(std::move(promise))->send(dialog_id); +} + void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, int32 offset_date, const string &offset_invite_link, int32 limit, Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, administrator_user_id != get_my_id())); if (!have_input_user(administrator_user_id)) { return promise.set_error(Status::Error(400, "Administrator user not found")); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index cca40271f..4ce253294 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -389,6 +389,9 @@ class ContactsManager : public Actor { void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, bool is_revoked, Promise> &&promise); + void get_dialog_invite_link_counts(DialogId dialog_id, + Promise> &&promise); + void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, int32 offset_date, const string &offset_invite_link, int32 limit, Promise> &&promise); @@ -1371,7 +1374,7 @@ class ContactsManager : public Actor { void remove_dialog_access_by_invite_link(DialogId dialog_id); - Status can_manage_dialog_invite_links(DialogId dialog_id); + Status can_manage_dialog_invite_links(DialogId dialog_id, bool creator_only = false); bool update_permanent_invite_link(DialogInviteLink &invite_link, DialogInviteLink new_invite_link); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 88e715880..d5727752b 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6322,14 +6322,14 @@ void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { request.member_limit_, false, std::move(promise)); } -void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { - CLEAN_INPUT_STRING(request.invite_link_); +void Td::on_request(uint64 id, const td_api::getChatInviteLinkCounts &request) { + CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, 0, 0, true, - std::move(promise)); + contacts_manager_->get_dialog_invite_link_counts(DialogId(request.chat_id_), std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { + CHECK_IS_USER(); CLEAN_INPUT_STRING(request.offset_invite_link_); CREATE_REQUEST_PROMISE(); contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.administrator_user_id_), @@ -6338,6 +6338,7 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { } void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { + CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); contacts_manager_->get_dialog_invite_link_users(DialogId(request.chat_id_), request.invite_link_, @@ -6345,6 +6346,13 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { std::move(promise)); } +void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { + CLEAN_INPUT_STRING(request.invite_link_); + CREATE_REQUEST_PROMISE(); + contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, 0, 0, true, + std::move(promise)); +} + void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { CLEAN_INPUT_STRING(request.invite_link_); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 789112c81..7d960dd2a 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -798,12 +798,14 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::editChatInviteLink &request); - void on_request(uint64 id, td_api::revokeChatInviteLink &request); + void on_request(uint64 id, const td_api::getChatInviteLinkCounts &request); void on_request(uint64 id, td_api::getChatInviteLinks &request); void on_request(uint64 id, td_api::getChatInviteLinkMembers &request); + void on_request(uint64 id, td_api::revokeChatInviteLink &request); + void on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request); void on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 7321d6a21..6dd6c05bb 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2715,6 +2715,9 @@ class CliClient final : public Actor { string invite_link; get_args(args, chat_id, invite_link); send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); + } else if (op == "gcilc") { + string chat_id = args; + send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "gcil" || op == "gcilr") { string chat_id; string administrator_user_id; From 14038151485aadd8049e7cf656ba43077ddfc915 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Feb 2021 22:35:43 +0300 Subject: [PATCH 151/232] Support t.me/+ links. --- td/generate/scheme/td_api.tl | 4 ++-- td/telegram/DialogInviteLink.cpp | 7 +++++-- td/telegram/DialogInviteLink.h | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 812ddb898..a89cc441d 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4465,11 +4465,11 @@ deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; //@description Deletes all revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier deleteAllRevokedChatInviteLinks chat_id:int53 = Ok; -//@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" +//@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must have URL "t.me", "telegram.me", or "telegram.dog" and query beginning with "/joinchat/" or "/+" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; //@description Uses an invite link to add the current user to the chat if possible -//@invite_link Invite link to import; must begin with "https://t.me/joinchat/", "https://telegram.me/joinchat/", or "https://telegram.dog/joinchat/" +//@invite_link Invite link to import; must have URL "t.me", "telegram.me", or "telegram.dog" and query beginning with "/joinchat/" or "/+" joinChatByInviteLink invite_link:string = Chat; diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index 085c09c82..f3aa83061 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -14,8 +14,11 @@ namespace td { -const CSlice DialogInviteLink::INVITE_LINK_URLS[3] = {"t.me/joinchat/", "telegram.me/joinchat/", - "telegram.dog/joinchat/"}; +const CSlice DialogInviteLink::INVITE_LINK_URLS[12] = { + "t.me/joinchat/", "telegram.me/joinchat/", "telegram.dog/joinchat/", + "t.me/+", "telegram.me/+", "telegram.dog/+", + "t.me/ ", "telegram.me/ ", "telegram.dog/ ", + "t.me/%20", "telegram.me/%20", "telegram.dog/%20"}; DialogInviteLink::DialogInviteLink(tl_object_ptr exported_invite) { if (exported_invite == nullptr) { diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index 2ced9c838..d4e35cdd3 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -34,7 +34,7 @@ class DialogInviteLink { friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link); - static const CSlice INVITE_LINK_URLS[3]; + static const CSlice INVITE_LINK_URLS[12]; public: DialogInviteLink() = default; From 2193928cf68c7caf12e4651d1c5593c8c6621beb Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Feb 2021 14:36:11 +0300 Subject: [PATCH 152/232] Add getExternalLink method. --- td/generate/scheme/td_api.tl | 3 ++ td/generate/scheme/td_api.tlo | Bin 195988 -> 196076 bytes td/telegram/ConfigManager.cpp | 84 ++++++++++++++++++++++++++++++++++ td/telegram/ConfigManager.h | 6 +++ td/telegram/Td.cpp | 15 ++++++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 3 ++ 7 files changed, 113 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index a89cc441d..0dc373c4f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4179,6 +4179,9 @@ viewMessages chat_id:int53 message_thread_id:int53 message_ids:vector for //@description Informs TDLib that the message content has been opened (e.g., the user has opened a photo, video, document, location or venue, or has listened to an audio file or voice note message). An updateMessageContentOpened update will be generated if something has changed @chat_id Chat identifier of the message @message_id Identifier of the message with the opened content openMessageContent chat_id:int53 message_id:int53 = Ok; +//@description Returns an HTTP URL to open when user clicks on a given HTTP link. This method can be used to automatically login user on a Telegram site @link The HTTP link +getExternalLink link:string = HttpUrl; + //@description Marks all mentions in a chat as read @chat_id Chat identifier readAllChatMentions chat_id:int53 = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 87175023800172a7217154367a84f027981af640..240ac7ab63cd9768528e3b350231810cf0679218 100644 GIT binary patch delta 61 zcmV-D0K)&2`wQ&*3xI?Hv;x#D0iL(jECTmS80LmhiVtUHbVYb{WpZv|Y)olxYo>L# Thm$BiFqfc%0uZ;%cml|TRu3BZ delta 27 jcmaF!n|sP{?uHh|ElgK*80T)ks>Af%d%H>{(}@-UxV;R7 diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 80515e326..d392ffe35 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -45,6 +45,7 @@ #include "td/utils/common.h" #include "td/utils/crypto.h" #include "td/utils/format.h" +#include "td/utils/HttpUrl.h" #include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" #include "td/utils/misc.h" @@ -888,6 +889,8 @@ void ConfigManager::start_up() { expire_time_ = expire_time; set_timeout_in(expire_time_.in()); } + + autologin_update_time_ = Time::now() - 365 * 86400; } ActorShared<> ConfigManager::create_reference() { @@ -964,6 +967,59 @@ void ConfigManager::get_app_config(Promise } } +void ConfigManager::get_external_link(string &&link, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_value(std::move(link)); + } + + auto r_url = parse_url(link); + if (r_url.is_error()) { + return promise.set_value(std::move(link)); + } + + if (!td::contains(autologin_domains_, r_url.ok().host_)) { + return promise.set_value(std::move(link)); + } + + if (autologin_update_time_ < Time::now() - 10000) { + auto query_promise = PromiseCreator::lambda([link = std::move(link), promise = std::move(promise)]( + Result> &&result) mutable { + if (result.is_error()) { + return promise.set_value(std::move(link)); + } + send_closure(G()->config_manager(), &ConfigManager::get_external_link, std::move(link), std::move(promise)); + }); + return get_app_config(std::move(query_promise)); + } + + if (autologin_token_.empty()) { + return promise.set_value(std::move(link)); + } + + auto url = r_url.move_as_ok(); + url.protocol_ = HttpUrl::Protocol::Https; + Slice path = Slice(url.query_).truncate(url.query_.find_first_of("?#")); + Slice parameters_hash = Slice(url.query_).substr(path.size()); + Slice parameters = parameters_hash; + parameters.truncate(parameters.find('#')); + Slice hash = parameters_hash.substr(parameters.size()); + + string added_parameter; + if (parameters.empty()) { + added_parameter = '?'; + } else if (parameters.size() == 1) { + CHECK(parameters == "?"); + } else { + added_parameter = '&'; + } + added_parameter += "autologin_token="; + added_parameter += autologin_token_; + + url.query_ = PSTRING() << path << parameters << added_parameter << hash; + + promise.set_value(url.get_url()); +} + void ConfigManager::get_content_settings(Promise &&promise) { if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); @@ -1475,6 +1531,10 @@ void ConfigManager::process_app_config(tl_object_ptr &c const bool archive_and_mute = G()->shared_config().get_option_boolean("archive_and_mute_new_chats_from_unknown_users"); + autologin_token_.clear(); + autologin_domains_.clear(); + autologin_update_time_ = Time::now(); + vector> new_values; string ignored_restriction_reasons; vector dice_emojis; @@ -1640,6 +1700,30 @@ void ConfigManager::process_app_config(tl_object_ptr &c } continue; } + if (key == "autologin_token") { + if (value->get_id() == telegram_api::jsonString::ID) { + autologin_token_ = url_encode(static_cast(value)->value_); + } else { + LOG(ERROR) << "Receive unexpected autologin_token " << to_string(*value); + } + continue; + } + if (key == "autologin_domains") { + if (value->get_id() == telegram_api::jsonArray::ID) { + auto domains = std::move(static_cast(value)->value_); + for (auto &domain : domains) { + CHECK(domain != nullptr); + if (domain->get_id() == telegram_api::jsonString::ID) { + autologin_domains_.push_back(std::move(static_cast(domain.get())->value_)); + } else { + LOG(ERROR) << "Receive unexpected autologin domain " << to_string(domain); + } + } + } else { + LOG(ERROR) << "Receive unexpected autologin_domains " << to_string(*value); + } + continue; + } new_values.push_back(std::move(key_value)); } diff --git a/td/telegram/ConfigManager.h b/td/telegram/ConfigManager.h index 0b12e4c70..57f4a72a6 100644 --- a/td/telegram/ConfigManager.h +++ b/td/telegram/ConfigManager.h @@ -93,6 +93,8 @@ class ConfigManager : public NetQueryCallback { void get_app_config(Promise> &&promise); + void get_external_link(string &&link, Promise &&promise); + void get_content_settings(Promise &&promise); void set_content_settings(bool ignore_sensitive_content_restrictions, Promise &&promise); @@ -114,6 +116,10 @@ class ConfigManager : public NetQueryCallback { int ref_cnt_{1}; Timestamp expire_time_; + string autologin_token_; + vector autologin_domains_; + double autologin_update_time_ = 0.0; + FloodControlStrict lazy_request_flood_control_; vector>> get_app_config_queries_; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index d5727752b..17ad74bf1 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5460,6 +5460,21 @@ void Td::on_request(uint64 id, const td_api::openMessageContent &request) { id, messages_manager_->open_message_content({DialogId(request.chat_id_), MessageId(request.message_id_)})); } +void Td::on_request(uint64 id, td_api::getExternalLink &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.link_); + CREATE_REQUEST_PROMISE(); + auto query_promise = [promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(td_api::make_object(result.ok())); + } + }; + send_closure_later(G()->config_manager(), &ConfigManager::get_external_link, std::move(request.link_), + std::move(query_promise)); +} + void Td::on_request(uint64 id, const td_api::getChatHistory &request) { CHECK_IS_USER(); CREATE_REQUEST(GetChatHistoryRequest, request.chat_id_, request.from_message_id_, request.offset_, request.limit_, diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 7d960dd2a..dccf68f40 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -574,6 +574,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::openMessageContent &request); + void on_request(uint64 id, td_api::getExternalLink &request); + void on_request(uint64 id, const td_api::getChatHistory &request); void on_request(uint64 id, const td_api::deleteChatHistory &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 6dd6c05bb..4cf673229 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3841,6 +3841,9 @@ class CliClient final : public Actor { string message_id; get_args(args, chat_id, message_id); send_request(td_api::make_object(as_chat_id(chat_id), as_message_id(message_id))); + } else if (op == "gel") { + string link = args; + send_request(td_api::make_object(link)); } else if (op == "racm") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id))); From 45dfb58cc5f42faf0c631e8a44ae1bcae3cd872d Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 01:40:53 +0300 Subject: [PATCH 153/232] Save autologin domains between restarts. --- td/telegram/ConfigManager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index d392ffe35..7a1cc62b3 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -891,6 +891,7 @@ void ConfigManager::start_up() { } autologin_update_time_ = Time::now() - 365 * 86400; + autologin_domains_ = full_split(G()->td_db()->get_binlog_pmc()->get("autologin_domains"), '\xFF'); } ActorShared<> ConfigManager::create_reference() { @@ -1532,6 +1533,7 @@ void ConfigManager::process_app_config(tl_object_ptr &c G()->shared_config().get_option_boolean("archive_and_mute_new_chats_from_unknown_users"); autologin_token_.clear(); + auto old_autologin_domains = std::move(autologin_domains_); autologin_domains_.clear(); autologin_update_time_ = Time::now(); @@ -1732,6 +1734,10 @@ void ConfigManager::process_app_config(tl_object_ptr &c } config = make_tl_object(std::move(new_values)); + if (autologin_domains_ != old_autologin_domains) { + G()->td_db()->get_binlog_pmc()->set("autologin_domains", implode(autologin_domains_, '\xFF')); + } + ConfigShared &shared_config = G()->shared_config(); if (ignored_restriction_reasons.empty()) { From 4d8fad1e819d3c2cf10d177232d17ece5f391f4f Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 01:46:55 +0300 Subject: [PATCH 154/232] Use httpUrl as return type of getProxyLink. --- td/generate/scheme/td_api.tl | 2 +- td/generate/scheme/td_api.tlo | Bin 196076 -> 196076 bytes td/telegram/Td.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 0dc373c4f..522eaaf5f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -5111,7 +5111,7 @@ removeProxy proxy_id:int32 = Ok; getProxies = Proxies; //@description Returns an HTTPS link, which can be used to add a proxy. Available only for SOCKS5 and MTProto proxies. Can be called before authorization @proxy_id Proxy identifier -getProxyLink proxy_id:int32 = Text; +getProxyLink proxy_id:int32 = HttpUrl; //@description Computes time needed to receive a response from a Telegram server through a proxy. Can be called before authorization @proxy_id Proxy identifier. Use 0 to ping a Telegram server without a proxy pingProxy proxy_id:int32 = Seconds; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 240ac7ab63cd9768528e3b350231810cf0679218..5f4ea7f796701079b0fa4d3f4937ff3a61f3cddb 100644 GIT binary patch delta 53 zcmV-50LuUD`wQ&*3xI?Hv;vTl7IBA0+-0yLN4 LdIAuq?2-a&Tz3^J diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 17ad74bf1..dd605b014 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -8014,7 +8014,7 @@ void Td::on_request(uint64 id, const td_api::getProxyLink &request) { if (result.is_error()) { promise.set_error(result.move_as_error()); } else { - promise.set_value(make_tl_object(result.move_as_ok())); + promise.set_value(make_tl_object(result.move_as_ok())); } }); send_closure(G()->connection_creator(), &ConnectionCreator::get_proxy_link, request.proxy_id_, From 342b4e45ca622c327519ace63490510e06c0d5b7 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 02:17:12 +0300 Subject: [PATCH 155/232] Support deletion of all revoked invite links created by other administrator. --- td/generate/scheme/td_api.tl | 6 ++++-- td/generate/scheme/td_api.tlo | Bin 196076 -> 196124 bytes td/telegram/ContactsManager.cpp | 21 +++++++++++++++------ td/telegram/ContactsManager.h | 3 ++- td/telegram/Td.cpp | 3 ++- td/telegram/cli.cpp | 6 ++++-- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 522eaaf5f..6cbb99155 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4465,8 +4465,10 @@ revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLink; //@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier @invite_link Invite link to revoke deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; -//@description Deletes all revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier -deleteAllRevokedChatInviteLinks chat_id:int53 = Ok; +//@description Deletes all revoked chat invite links created by a given chat administrator. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links +//@chat_id Chat identifier +//@administrator_user_id User identifier of a chat administrator, which links will be deleted. Must be an identifier of the current user for non-owner +deleteAllRevokedChatInviteLinks chat_id:int53 administrator_user_id:int32 = Ok; //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must have URL "t.me", "telegram.me", or "telegram.dog" and query beginning with "/joinchat/" or "/+" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 5f4ea7f796701079b0fa4d3f4937ff3a61f3cddb..bd45e47f1f1ce93ccb32ce57e48e8f5323f45c6f 100644 GIT binary patch delta 43 zcmV+`0M!5N`wN`>3xI?Hv;w?M1YvO?`j@~>0w|a0)B-4zh+3xI?Hv;w?M1ZN?R$(O)Q0w|ZbqXIm)R89iOuM*u44FCWD diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 0f46266ca..ba8b0bb93 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1895,15 +1895,18 @@ class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { explicit DeleteRevokedExportedChatInvitesQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id) { + void send(DialogId dialog_id, UserId administrator_user_id) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query(G()->net_query_creator().create(telegram_api::messages_deleteRevokedExportedChatInvites( - std::move(input_peer), make_tl_object()))); + auto input_user = td->contacts_manager_->get_input_user(administrator_user_id); + CHECK(input_user != nullptr); + + send_query(G()->net_query_creator().create( + telegram_api::messages_deleteRevokedExportedChatInvites(std::move(input_peer), std::move(input_user)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -6993,10 +6996,16 @@ void ContactsManager::delete_revoked_dialog_invite_link(DialogId dialog_id, cons td_->create_handler(std::move(promise))->send(dialog_id, invite_link); } -void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, Promise &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); +void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, administrator_user_id != get_my_id())); - td_->create_handler(std::move(promise))->send(dialog_id); + if (!have_input_user(administrator_user_id)) { + return promise.set_error(Status::Error(400, "Administrator user not found")); + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, administrator_user_id); } void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 4ce253294..24837bf68 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -402,7 +402,8 @@ class ContactsManager : public Actor { void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); - void delete_all_revoked_dialog_invite_links(DialogId dialog_id, Promise &&promise); + void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, + Promise &&promise); void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index dd605b014..47bda0907 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6377,7 +6377,8 @@ void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { void Td::on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request) { CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), std::move(promise)); + contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), + UserId(request.administrator_user_id_), std::move(promise)); } void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 4cf673229..1d5bdcbab 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2744,8 +2744,10 @@ class CliClient final : public Actor { get_args(args, chat_id, invite_link); send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); } else if (op == "darcil") { - string chat_id = args; - send_request(td_api::make_object(as_chat_id(chat_id))); + string chat_id; + string administrator_user_id; + get_args(args, chat_id, administrator_user_id); + send_request(td_api::make_object(as_chat_id(chat_id), as_user_id(administrator_user_id))); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From fef8bd151cdaf98984a825e6dfee01621bb9889a Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 02:54:34 +0300 Subject: [PATCH 156/232] Support some new chat events. --- td/generate/scheme/td_api.tl | 12 ++++++++ td/generate/scheme/td_api.tlo | Bin 196124 -> 196592 bytes td/telegram/MessagesManager.cpp | 47 ++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 6cbb99155..c2df5e999 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2379,6 +2379,9 @@ chatEventMessageUnpinned message:message = ChatEventAction; //@description A new member joined the chat chatEventMemberJoined = ChatEventAction; +//@description A new member joined the chat by an invite link @invite_link Invite link used to join the chat +chatEventMemberJoinedByInviteLink invite_link:chatInviteLink = ChatEventAction; + //@description A member left the chat chatEventMemberLeft = ChatEventAction; @@ -2427,6 +2430,15 @@ chatEventLocationChanged old_location:chatLocation new_location:chatLocation = C //@description The is_all_history_available setting of a supergroup was toggled @is_all_history_available New value of is_all_history_available chatEventIsAllHistoryAvailableToggled is_all_history_available:Bool = ChatEventAction; +//@description A chat invite link was edited @old_invite_link Previous information about the invite link @new_invite_link New information about the invite link +chatEventInviteLinkEdited old_invite_link:chatInviteLink new_invite_link:chatInviteLink = ChatEventAction; + +//@description A chat invite link was revoked @invite_link The invite link +chatEventInviteLinkRevoked invite_link:chatInviteLink = ChatEventAction; + +//@description A revoked chat invite link was deleted @invite_link The invite link +chatEventInviteLinkDeleted invite_link:chatInviteLink = ChatEventAction; + //@description A voice chat was created @group_call_id Identifier of the voice chat. The voice chat can be received through the method getGroupCall chatEventVoiceChatCreated group_call_id:int32 = ChatEventAction; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index bd45e47f1f1ce93ccb32ce57e48e8f5323f45c6f..1982210e3e31d0db7047a5f9072de49ab45bb12b 100644 GIT binary patch delta 849 zcmbR9hx@~S?gi|krIWO{_?v(TY#@vmX%P^HOS%SxQIKu`VJyKAVaY&Olair>rt3t8MgYi)o5zHF7=WP2nN5eo zw(THD48#Y4t8!d1d#^tGpqLDEtZ!;=QfiS`er8^3ic_U$URh>Is!wKKHUk4l-}D-L zCWXmgJVhXOab&7U;Id+}OD4>XnE?>xM_?i!GAke=IayFw!$l;rp}JhMq2aM08>;3) zb^=6~Ne)ybB?l^U0w(eUCK8hiHK!vNYR(gwh(I1R$OH1A<}~DGKums<2MuzCe5goI zK2+pDz6C_hpM0n;odT$u83j<069rKB=oCT~XB4`)q6c^q@1!S^@Bl{yv1}-U<64&yRXTgpOX?l{o@HnWmf*Y)bjYr4?WdQ&<%b3|Mo&DEQSW9mgQ#yO+|@4 z4BhF8x7J|O?UI_43UU*QZb&d}D1>_Y0nD#5Md%_OMbMBg$kdtsA%ux#y2Cdn6^MR? hVh2u;u^^u^PA~k*WDOQ?UQyh>qL^{}iejdcE&vQj9g6?} delta 565 zcmezHpL@<9?gi|kdRO9w`JFQoOI*uR^GY0(OEUBGq$W>f65p)D-lEHxyLqF@XG0c{ zs>v(TY#@vmX%P^HOS%SxQIKu`VJyKAVaY&Olair>rt3t8h6Kopo5zHF7=WP2nN5eo zw(TI;+KNmS+>%=|p{75`41gGFk_8n>$f|&d+{uF42N&tchN{_;4fTmY4pfapP6E^? zFp(=T5sO@?nv7hinlrgjbAIGP%?ZhaiZtXw1M5H@RM&^R42a1w`Ov_ckPj940~0YQ zuz;w^DS+ylQvg-NQ3w^WD1_QRr_h2E7HW*s4SzD(L-e*3Lscr2Ku!CAAreypb>4$a io#_HUn4qryQsMwH!=e MessagesManager::get_chat_event_action_ob switch (action_ptr->get_id()) { case telegram_api::channelAdminLogEventActionParticipantJoin::ID: return make_tl_object(); + case telegram_api::channelAdminLogEventActionParticipantJoinByInvite::ID: { + auto action = move_tl_object_as(action_ptr); + DialogInviteLink invite_link(std::move(action->invite_)); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Wrong invite link: " << invite_link; + return nullptr; + } + return make_tl_object( + invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } case telegram_api::channelAdminLogEventActionParticipantLeave::ID: return make_tl_object(); case telegram_api::channelAdminLogEventActionParticipantInvite::ID: { @@ -31134,6 +31145,38 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob auto new_slow_mode_delay = clamp(action->new_value_, 0, 86400 * 366); return make_tl_object(old_slow_mode_delay, new_slow_mode_delay); } + case telegram_api::channelAdminLogEventActionExportedInviteEdit::ID: { + auto action = move_tl_object_as(action_ptr); + DialogInviteLink old_invite_link(std::move(action->prev_invite_)); + DialogInviteLink new_invite_link(std::move(action->new_invite_)); + if (!old_invite_link.is_valid() || !new_invite_link.is_valid()) { + LOG(ERROR) << "Wrong edited invite link: " << old_invite_link << " -> " << new_invite_link; + return nullptr; + } + return make_tl_object( + old_invite_link.get_chat_invite_link_object(td_->contacts_manager_.get()), + new_invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } + case telegram_api::channelAdminLogEventActionExportedInviteRevoke::ID: { + auto action = move_tl_object_as(action_ptr); + DialogInviteLink invite_link(std::move(action->invite_)); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Wrong revoked invite link: " << invite_link; + return nullptr; + } + return make_tl_object( + invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } + case telegram_api::channelAdminLogEventActionExportedInviteDelete::ID: { + auto action = move_tl_object_as(action_ptr); + DialogInviteLink invite_link(std::move(action->invite_)); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Wrong deleted invite link: " << invite_link; + return nullptr; + } + return make_tl_object( + invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } case telegram_api::channelAdminLogEventActionStartGroupCall::ID: { auto action = move_tl_object_as(action_ptr); auto input_group_call_id = InputGroupCallId(action->call_); @@ -31170,10 +31213,14 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob return make_tl_object( td_->contacts_manager_->get_user_id_object(participant.user_id, "LogEventActionParticipantUnmute"), false); } + case telegram_api::channelAdminLogEventActionParticipantVolume::ID: + return nullptr; case telegram_api::channelAdminLogEventActionToggleGroupCallSetting::ID: { auto action = move_tl_object_as(action_ptr); return make_tl_object(action->join_muted_); } + case telegram_api::channelAdminLogEventActionChangeHistoryTTL::ID: + return nullptr; default: UNREACHABLE(); return nullptr; From 9d9e0936409efde12e904f3f3a2173c753f0cd3d Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 03:04:16 +0300 Subject: [PATCH 157/232] Remove inviteLink.is_expired field. --- td/generate/scheme/td_api.tl | 4 ++-- td/generate/scheme/td_api.tlo | Bin 196592 -> 196556 bytes td/telegram/DialogInviteLink.cpp | 17 +---------------- td/telegram/DialogInviteLink.h | 4 ---- 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c2df5e999..4c5afaf70 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -553,8 +553,8 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@member_limit Maximum number of members, which can join the chat using the link simultaneously; 0 if not limited //@member_count Number of chat members, which joined the chat using the link //@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator with can_invite_users right at a given time -//@is_expired True, if the link is already expired @is_revoked True, if the link was revoked -chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_permanent:Bool is_expired:Bool is_revoked:Bool = ChatInviteLink; +//@is_revoked True, if the link was revoked +chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_permanent:Bool is_revoked:Bool = ChatInviteLink; //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 1982210e3e31d0db7047a5f9072de49ab45bb12b..ed8db895f5b827bd3f421b0fc6b2be37995e08ad 100644 GIT binary patch delta 256 zcmezHpZm;z?hQBCSu)a3=5M~kF5v`W{qW3?2eT#{q$_N0$^5_p;Z+o=Ko|>(QsDBF zHHuxpoXrKrOW0*WM)4(QB$jyQm1UNs`ef#1Gl0Zy9uxB6+-z8F&p5rHh)HI$gB!%q z&3kGjSit(5Z!|2w(ZJ{h=4`)niqWGE%$$DV38T*R5KpE6h|CI4rXT!JnV&$J3z1AE l5E+jsrhsUOOhPV`0Yvb^R;D$q5W$AsOd$}#8@rh#TmXm^Yia-h delta 276 zcmX@}pZmjq?hQBCS^Oj)FWh{GUBU^%`r(-&4`xj^NLSe0lKFuH!mB7!fiM;nrNHGU zYZSYHIhzZLm$1u%jN(hqNG$QpE6XfN^~uc3W&nxZJSODBwb`)Ro{=@Rq9C(qdSDTg z^yHix4T#S5&Ce7QF{C3Q;fQG5dHxtMxE&fo=gE? z?)DZ>rXTzenTANF1rR}%D5ijDh~NhgMgxf8nypN0SRsN6eN4L3*G4kQPcnn}Zu^$q HOcE{tn$K?j diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index f3aa83061..58bd468df 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -102,21 +102,6 @@ Slice DialogInviteLink::get_dialog_invite_link_hash(Slice invite_link) { return Slice(); } -bool DialogInviteLink::is_expired() const { - return (expire_date_ != 0 && G()->unix_time() >= expire_date_) || (usage_limit_ != 0 && usage_count_ >= usage_limit_); -} - -int32 DialogInviteLink::get_expire_time() const { - if (expire_date_ == 0) { - return 0; - } - if (usage_limit_ != 0 && usage_count_ >= usage_limit_) { - // already expired - return 0; - } - return td::max(expire_date_ - G()->unix_time(), 0); -} - td_api::object_ptr DialogInviteLink::get_chat_invite_link_object( const ContactsManager *contacts_manager) const { CHECK(contacts_manager != nullptr); @@ -126,7 +111,7 @@ td_api::object_ptr DialogInviteLink::get_chat_invite_lin return td_api::make_object( invite_link_, contacts_manager->get_user_id_object(administrator_user_id_, "get_chat_invite_link_object"), date_, - edit_date_, expire_date_, usage_limit_, usage_count_, is_permanent_, is_expired(), is_revoked_); + edit_date_, expire_date_, usage_limit_, usage_count_, is_permanent_, is_revoked_); } bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index d4e35cdd3..e7bde6044 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -55,10 +55,6 @@ class DialogInviteLink { return is_permanent_; } - bool is_expired() const; - - int32 get_expire_time() const; - const string &get_invite_link() const { return invite_link_; } From 313a58b952882cd043cc7d701ffb06af5e93589d Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 14:21:16 +0300 Subject: [PATCH 158/232] Replace td_api::sendChatSetTtlMessage with td_api::setChatMessageTtl. --- td/generate/scheme/td_api.tl | 6 +-- td/generate/scheme/td_api.tlo | Bin 196556 -> 196552 bytes td/telegram/MessagesManager.cpp | 92 ++++++++++++++++++++------------ td/telegram/MessagesManager.h | 2 +- td/telegram/Td.cpp | 18 +++---- td/telegram/Td.h | 4 +- td/telegram/cli.cpp | 13 ++--- 7 files changed, 77 insertions(+), 58 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 4c5afaf70..c72e36fcb 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3996,9 +3996,6 @@ forwardMessages chat_id:int53 from_chat_id:int53 message_ids:vector optio //@chat_id Identifier of the chat to send messages @message_ids Identifiers of the messages to resend. Message identifiers must be in a strictly increasing order resendMessages chat_id:int53 message_ids:vector = Messages; -//@description Changes the current TTL setting (sets a new self-destruct timer) in a secret chat and sends the corresponding message @chat_id Chat identifier @ttl New TTL value, in seconds -sendChatSetTtlMessage chat_id:int53 ttl:int32 = Message; - //@description Sends a notification about a screenshot taken in a chat. Supported only in private and secret chats @chat_id Chat identifier sendChatScreenshotTakenNotification chat_id:int53 = Ok; @@ -4266,6 +4263,9 @@ setChatTitle chat_id:int53 title:string = Ok; //@chat_id Chat identifier @photo New chat photo. Pass null to delete the chat photo setChatPhoto chat_id:int53 photo:InputChatPhoto = Ok; +//@description Changes the message TTL setting (sets a new self-destruct timer) in a chat. Requires can_delete_messages administrator right in basic groups, supergroups and channels @chat_id Chat identifier @ttl New TTL value, in seconds +setChatMessageTtl chat_id:int53 ttl:int32 = Ok; + //@description Changes the chat members permissions. Supported only for basic groups and supergroups. Requires can_restrict_members administrator right //@chat_id Chat identifier @permissions New non-administrator members permissions in the chat setChatPermissions chat_id:int53 permissions:chatPermissions = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index ed8db895f5b827bd3f421b0fc6b2be37995e08ad..e72770b94708a92996166e11d0175293fdb69387 100644 GIT binary patch delta 64 zcmV-G0Kfmt{|m_f3xI?Hv;u(3r~JzT4j6fE>!A^IWpqPmVRTJpb8}&5WmI%*005Ve WPXaKP&?NyFm%u#&7`Ob{0?;L<>l+LJ delta 67 zcmV-J0KEUm{|n6j3xI?Hv;u(38w|o?RTXn(Ze&AfVRTbvbX0U~O=WX)VP|Ckmmi1% ZF_+LK0T`E1U;-GYfXf07xA@rt&?Tyj8mj;R diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 53da24f4e..7b1a5c0b6 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -26261,40 +26261,6 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v return result; } -Result MessagesManager::send_dialog_set_ttl_message(DialogId dialog_id, int32 ttl) { - if (dialog_id.get_type() != DialogType::SecretChat) { - return Status::Error(5, "Can't set message TTL in non-secret chat"); - } - - if (ttl < 0) { - return Status::Error(5, "Message TTL can't be negative"); - } - - LOG(INFO) << "Begin to set message TTL in " << dialog_id << " to " << ttl; - - Dialog *d = get_dialog_force(dialog_id); - if (d == nullptr) { - return Status::Error(5, "Chat not found"); - } - - TRY_STATUS(can_send_message(dialog_id)); - bool need_update_dialog_pos = false; - Message *m = get_message_to_send(d, MessageId(), MessageId(), MessageSendOptions(), - create_chat_set_ttl_message_content(ttl), &need_update_dialog_pos); - - send_update_new_message(d, m); - if (need_update_dialog_pos) { - send_update_chat_last_message(d, "send_dialog_set_ttl_message"); - } - - int64 random_id = begin_send_message(dialog_id, m); - - send_closure(td_->secret_chats_manager_, &SecretChatsManager::send_set_ttl_message, dialog_id.get_secret_chat_id(), - ttl, random_id, Promise<>()); // TODO Promise - - return m->message_id; -} - Status MessagesManager::send_screenshot_taken_notification_message(DialogId dialog_id) { auto dialog_type = dialog_id.get_type(); if (dialog_type != DialogType::User && dialog_type != DialogType::SecretChat) { @@ -30630,6 +30596,64 @@ void MessagesManager::set_dialog_title(DialogId dialog_id, const string &title, td_->create_handler(std::move(promise))->send(dialog_id, new_title); } +void MessagesManager::set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Promise &&promise) { + if (ttl < 0) { + return promise.set_error(Status::Error(400, "Message TTL can't be negative")); + } + + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + LOG(INFO) << "Begin to set message TTL in " << dialog_id << " to " << ttl; + + TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); + + switch (dialog_id.get_type()) { + case DialogType::User: + break; + case DialogType::Chat: { + auto chat_id = dialog_id.get_chat_id(); + auto status = td_->contacts_manager_->get_chat_permissions(chat_id); + if (!status.can_delete_messages()) { + return promise.set_error(Status::Error(400, "Not enough rights to set message TTL in the chat")); + } + break; + } + case DialogType::Channel: { + auto status = td_->contacts_manager_->get_channel_permissions(dialog_id.get_channel_id()); + if (!status.can_change_info_and_settings()) { + return promise.set_error(Status::Error(400, "Not enough rights to set message TTL in the chat")); + } + break; + } + case DialogType::SecretChat: + break; + case DialogType::None: + default: + UNREACHABLE(); + } + + if (dialog_id.get_type() != DialogType::SecretChat) { + promise.set_error(Status::Error(5, "Can't set message TTL in non-secret chat")); + } else { + bool need_update_dialog_pos = false; + Message *m = get_message_to_send(d, MessageId(), MessageId(), MessageSendOptions(), + create_chat_set_ttl_message_content(ttl), &need_update_dialog_pos); + + send_update_new_message(d, m); + if (need_update_dialog_pos) { + send_update_chat_last_message(d, "send_dialog_set_ttl_message"); + } + + int64 random_id = begin_send_message(dialog_id, m); + + send_closure(td_->secret_chats_manager_, &SecretChatsManager::send_set_ttl_message, dialog_id.get_secret_chat_id(), + ttl, random_id, std::move(promise)); + } +} + void MessagesManager::set_dialog_permissions(DialogId dialog_id, const td_api::object_ptr &permissions, Promise &&promise) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 641a68c50..01c1ae85e 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -384,7 +384,7 @@ class MessagesManager : public Actor { Result> resend_messages(DialogId dialog_id, vector message_ids) TD_WARN_UNUSED_RESULT; - Result send_dialog_set_ttl_message(DialogId dialog_id, int32 ttl); + void set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Promise &&promise); Status send_screenshot_taken_notification_message(DialogId dialog_id); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 47bda0907..f5aa4a2d9 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5674,18 +5674,6 @@ void Td::on_request(uint64 id, td_api::sendInlineQueryResultMessage &request) { messages_manager_->get_message_object({dialog_id, r_new_message_id.ok()})); } -void Td::on_request(uint64 id, const td_api::sendChatSetTtlMessage &request) { - DialogId dialog_id(request.chat_id_); - auto r_new_message_id = messages_manager_->send_dialog_set_ttl_message(dialog_id, request.ttl_); - if (r_new_message_id.is_error()) { - return send_closure(actor_id(this), &Td::send_error, id, r_new_message_id.move_as_error()); - } - - CHECK(r_new_message_id.ok().is_valid()); - send_closure(actor_id(this), &Td::send_result, id, - messages_manager_->get_message_object({dialog_id, r_new_message_id.ok()})); -} - void Td::on_request(uint64 id, td_api::addLocalMessage &request) { CHECK_IS_USER(); @@ -6125,6 +6113,12 @@ void Td::on_request(uint64 id, const td_api::setChatPhoto &request) { messages_manager_->set_dialog_photo(DialogId(request.chat_id_), request.photo_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::setChatMessageTtl &request) { + DialogId dialog_id(request.chat_id_); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->set_dialog_message_ttl(dialog_id, request.ttl_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::setChatPermissions &request) { CREATE_OK_REQUEST_PROMISE(); messages_manager_->set_dialog_permissions(DialogId(request.chat_id_), request.permissions_, std::move(promise)); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index dccf68f40..7a66b29dd 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -624,8 +624,6 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::sendInlineQueryResultMessage &request); - void on_request(uint64 id, const td_api::sendChatSetTtlMessage &request); - void on_request(uint64 id, td_api::addLocalMessage &request); void on_request(uint64 id, td_api::editMessageText &request); @@ -742,6 +740,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::setChatPhoto &request); + void on_request(uint64 id, const td_api::setChatMessageTtl &request); + void on_request(uint64 id, const td_api::setChatPermissions &request); void on_request(uint64 id, td_api::setChatDraftMessage &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 1d5bdcbab..6a496b1f2 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2600,11 +2600,6 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_user_id(args))); } else if (op == "scstn") { send_request(td_api::make_object(as_chat_id(args))); - } else if (op == "sscttl" || op == "setSecretChatTtl") { - string chat_id; - int32 ttl; - get_args(args, chat_id, ttl); - send_request(td_api::make_object(as_chat_id(chat_id), ttl)); } else if (op == "closeSC" || op == "cancelSC") { send_request(td_api::make_object(as_secret_chat_id(args))); } else if (op == "cc" || op == "CreateCall") { @@ -2747,7 +2742,8 @@ class CliClient final : public Actor { string chat_id; string administrator_user_id; get_args(args, chat_id, administrator_user_id); - send_request(td_api::make_object(as_chat_id(chat_id), as_user_id(administrator_user_id))); + send_request(td_api::make_object(as_chat_id(chat_id), + as_user_id(administrator_user_id))); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { @@ -3539,6 +3535,11 @@ class CliClient final : public Actor { send_request(td_api::make_object( as_chat_id(chat_id), td_api::make_object(as_input_file(animation), to_double(main_frame_timestamp)))); + } else if (op == "scmt") { + string chat_id; + int32 ttl; + get_args(args, chat_id, ttl); + send_request(td_api::make_object(as_chat_id(chat_id), ttl)); } else if (op == "scperm") { string chat_id; string permissions; From 4cb8af05846ba28e27a70486cddd62c6957bc21c Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 18:56:44 +0300 Subject: [PATCH 159/232] Support revoking permanent chat invite links. --- td/generate/scheme/td_api.tl | 5 +- td/generate/scheme/td_api.tlo | Bin 196552 -> 196552 bytes td/telegram/ContactsManager.cpp | 101 ++++++++++++++++++++++++++++---- td/telegram/ContactsManager.h | 5 +- td/telegram/Td.cpp | 5 +- 5 files changed, 100 insertions(+), 16 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c72e36fcb..57afc9add 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4469,10 +4469,11 @@ getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool off //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return getChatInviteLinkMembers chat_id:int53 invite_link:string offset_member:chatInviteLinkMember limit:int32 = ChatInviteLinkMembers; -//@description Revokes a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links +//@description Revokes invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links. +//-If a permanent link is revoked, then additionally to the revoked link returns new permanent link //@chat_id Chat identifier //@invite_link Invite link to be revoked -revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLink; +revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLinks; //@description Deletes revoked chat invite links. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier @invite_link Invite link to revoke deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index e72770b94708a92996166e11d0175293fdb69387..fd3c01829d4de6429ef6d12130698613e4ca6b97 100644 GIT binary patch delta 52 zcmV-40L%Z#{|m_f3xI?Hv;sW41gTZG(U(BG0vH4%mbwO)AdUhpm#tX>ACfi*m*8>& K5Vyd)0tiG-tQ3a; delta 51 zcmV-30L=f${|m_f3xI?Hv;sW41o0?X1D8O%0vMJ_0tlBRjshu{tyuyek~RpJfJp)n Jx4^ps2t*7x5?BBL diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index ba8b0bb93..a8d7d18d0 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1635,7 +1635,7 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit, bool is_revoked) { + void send(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { @@ -1644,9 +1644,6 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { int32 flags = telegram_api::messages_editExportedChatInvite::EXPIRE_DATE_MASK | telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK; - if (is_revoked) { - flags |= telegram_api::messages_editExportedChatInvite::REVOKED_MASK; - } send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( flags, false /*ignored*/, std::move(input_peer), invite_link, expire_date, usage_limit))); } @@ -1657,12 +1654,18 @@ class EditChatInviteLinkQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - auto result = move_tl_object_as(result_ptr.move_as_ok()); - LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(result); + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for EditChatInviteLinkQuery: " << to_string(result); - td->contacts_manager_->on_get_users(std::move(result->users_), "EditChatInviteLinkQuery"); + if (result->get_id() != telegram_api::messages_exportedChatInvite::ID) { + return on_error(id, Status::Error(500, "Receive unexpected response from server")); + } - DialogInviteLink invite_link(std::move(result->invite_)); + auto invite = move_tl_object_as(result); + + td->contacts_manager_->on_get_users(std::move(invite->users_), "EditChatInviteLinkQuery"); + + DialogInviteLink invite_link(std::move(invite->invite_)); if (!invite_link.is_valid()) { return on_error(id, Status::Error(500, "Receive invalid invite link")); } @@ -1853,6 +1856,77 @@ class GetChatInviteImportersQuery : public Td::ResultHandler { } }; +class RevokeChatInviteLinkQuery : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit RevokeChatInviteLinkQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + int32 flags = telegram_api::messages_editExportedChatInvite::REVOKED_MASK; + send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( + flags, false /*ignored*/, std::move(input_peer), invite_link, 0, 0))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(result); + + vector> links; + switch (result->get_id()) { + case telegram_api::messages_exportedChatInvite::ID: { + auto invite = move_tl_object_as(result); + + td->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery"); + + DialogInviteLink invite_link(std::move(invite->invite_)); + if (!invite_link.is_valid()) { + return on_error(id, Status::Error(500, "Receive invalid invite link")); + } + links.push_back(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); + break; + } + case telegram_api::messages_exportedChatInviteReplaced::ID: { + auto invite = move_tl_object_as(result); + + td->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery"); + + DialogInviteLink invite_link(std::move(invite->invite_)); + DialogInviteLink new_invite_link(std::move(invite->new_invite_)); + if (!invite_link.is_valid() || !new_invite_link.is_valid()) { + return on_error(id, Status::Error(500, "Receive invalid invite link")); + } + links.push_back(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); + links.push_back(new_invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); + break; + } + default: + UNREACHABLE(); + } + auto total_count = static_cast(links.size()); + promise_.set_value(td_api::make_object(total_count, std::move(links))); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "RevokeChatInviteLinkQuery"); + promise_.set_error(std::move(status)); + } +}; + class DeleteExportedChatInviteQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -6933,7 +7007,7 @@ void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 e } void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, int32 expire_date, - int32 usage_limit, bool is_revoked, + int32 usage_limit, Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); @@ -6942,7 +7016,7 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & // } td_->create_handler(std::move(promise)) - ->send(dialog_id, invite_link, expire_date, usage_limit, is_revoked); + ->send(dialog_id, invite_link, expire_date, usage_limit); } void ContactsManager::get_dialog_invite_link_counts( @@ -6989,6 +7063,13 @@ void ContactsManager::get_dialog_invite_link_users( ->send(dialog_id, invite_link, offset_date, offset_user_id, limit); } +void ContactsManager::revoke_dialog_invite_link(DialogId dialog_id, const string &invite_link, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); +} + void ContactsManager::delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 24837bf68..d703f2fee 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -387,7 +387,7 @@ class ContactsManager : public Actor { Promise> &&promise); void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, - bool is_revoked, Promise> &&promise); + Promise> &&promise); void get_dialog_invite_link_counts(DialogId dialog_id, Promise> &&promise); @@ -400,6 +400,9 @@ class ContactsManager : public Actor { td_api::object_ptr offset_member, int32 limit, Promise> &&promise); + void revoke_dialog_invite_link(DialogId dialog_id, const string &link, + Promise> &&promise); + void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index f5aa4a2d9..e770c5223 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6328,7 +6328,7 @@ void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, request.expire_date_, - request.member_limit_, false, std::move(promise)); + request.member_limit_, std::move(promise)); } void Td::on_request(uint64 id, const td_api::getChatInviteLinkCounts &request) { @@ -6358,8 +6358,7 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, 0, 0, true, - std::move(promise)); + contacts_manager_->revoke_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, std::move(promise)); } void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { From f46ff6e1cdc48914d7c1ce6c30b340415454ca7e Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Feb 2021 19:23:29 +0300 Subject: [PATCH 160/232] Update permanent invite link after it is revoked. --- td/telegram/ContactsManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index a8d7d18d0..12eb411f8 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1910,6 +1910,10 @@ class RevokeChatInviteLinkQuery : public Td::ResultHandler { if (!invite_link.is_valid() || !new_invite_link.is_valid()) { return on_error(id, Status::Error(500, "Receive invalid invite link")); } + if (new_invite_link.get_administrator_user_id() == td->contacts_manager_->get_my_id() && + new_invite_link.is_permanent()) { + td->contacts_manager_->on_get_permanent_dialog_invite_link(dialog_id_, new_invite_link); + } links.push_back(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); links.push_back(new_invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); break; From c226a204face3bf87d10810a61a6dd70b9d86c43 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 8 Feb 2021 15:34:05 +0300 Subject: [PATCH 161/232] Diallow invite link deletion for bots. --- td/telegram/Td.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index e770c5223..6208ebc78 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6362,6 +6362,7 @@ void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { } void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { + CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); CREATE_OK_REQUEST_PROMISE(); contacts_manager_->delete_revoked_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, @@ -6369,6 +6370,7 @@ void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { } void Td::on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request) { + CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), UserId(request.administrator_user_id_), std::move(promise)); From bf42d62f1e3bc788b629480f1770f9a356e01154 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 8 Feb 2021 15:56:08 +0300 Subject: [PATCH 162/232] Unify ResultHandler subclass names and logged name. --- td/telegram/ContactsManager.cpp | 37 +++++++++--------- td/telegram/GroupCallManager.cpp | 7 ++-- td/telegram/MessagesManager.cpp | 50 ++++++++++++------------ td/telegram/PollManager.cpp | 2 +- td/telegram/Td.cpp | 2 +- td/telegram/net/PublicRsaKeyWatchdog.cpp | 2 +- 6 files changed, 49 insertions(+), 51 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 12eb411f8..4e991ceb5 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1154,13 +1154,13 @@ class ToggleChannelSignaturesQuery : public Td::ResultHandler { } }; -class ToggleChannelIsAllHistoryAvailableQuery : public Td::ResultHandler { +class TogglePrehistoryHiddenQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; bool is_all_history_available_; public: - explicit ToggleChannelIsAllHistoryAvailableQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit TogglePrehistoryHiddenQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(ChannelId channel_id, bool is_all_history_available) { @@ -1180,7 +1180,7 @@ class ToggleChannelIsAllHistoryAvailableQuery : public Td::ResultHandler { } auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for TogglePreHistoryHiddenQuery: " << to_string(ptr); + LOG(INFO) << "Receive result for TogglePrehistoryHiddenQuery: " << to_string(ptr); td->updates_manager_->on_get_updates( std::move(ptr), @@ -1201,7 +1201,7 @@ class ToggleChannelIsAllHistoryAvailableQuery : public Td::ResultHandler { return; } } else { - td->contacts_manager_->on_get_channel_error(channel_id_, status, "ToggleChannelIsAllHistoryAvailableQuery"); + td->contacts_manager_->on_get_channel_error(channel_id_, status, "TogglePrehistoryHiddenQuery"); } promise_.set_error(std::move(status)); } @@ -1567,12 +1567,12 @@ class EditChatAdminQuery : public Td::ResultHandler { } }; -class ExportChatInviteLinkQuery : public Td::ResultHandler { +class ExportChatInviteQuery : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; public: - explicit ExportChatInviteLinkQuery(Promise> &&promise) + explicit ExportChatInviteQuery(Promise> &&promise) : promise_(std::move(promise)) { } @@ -1621,7 +1621,7 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ExportChatInviteLinkQuery"); + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ExportChatInviteQuery"); promise_.set_error(std::move(status)); } }; @@ -1884,7 +1884,7 @@ class RevokeChatInviteLinkQuery : public Td::ResultHandler { } auto result = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(result); + LOG(INFO) << "Receive result for RevokeChatInviteLinkQuery: " << to_string(result); vector> links; switch (result->get_id()) { @@ -2002,12 +2002,12 @@ class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { } }; -class CheckDialogInviteLinkQuery : public Td::ResultHandler { +class CheckChatInviteQuery : public Td::ResultHandler { Promise promise_; string invite_link_; public: - explicit CheckDialogInviteLinkQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit CheckChatInviteQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(const string &invite_link) { @@ -2033,13 +2033,13 @@ class CheckDialogInviteLinkQuery : public Td::ResultHandler { } }; -class ImportDialogInviteLinkQuery : public Td::ResultHandler { +class ImportChatInviteQuery : public Td::ResultHandler { Promise promise_; string invite_link_; public: - explicit ImportDialogInviteLinkQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit ImportChatInviteQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(const string &invite_link) { @@ -2059,7 +2059,7 @@ class ImportDialogInviteLinkQuery : public Td::ResultHandler { auto dialog_ids = UpdatesManager::get_chat_dialog_ids(ptr.get()); if (dialog_ids.size() != 1u) { - LOG(ERROR) << "Receive wrong result for ImportDialogInviteLinkQuery: " << to_string(ptr); + LOG(ERROR) << "Receive wrong result for ImportChatInviteQuery: " << to_string(ptr); return on_error(id, Status::Error(500, "Internal Server Error")); } auto dialog_id = dialog_ids[0]; @@ -6152,8 +6152,7 @@ void ContactsManager::toggle_channel_is_all_history_available(ChannelId channel_ } // it can be toggled in public chats, but will not affect them - td_->create_handler(std::move(promise)) - ->send(channel_id, is_all_history_available); + td_->create_handler(std::move(promise))->send(channel_id, is_all_history_available); } void ContactsManager::set_channel_description(ChannelId channel_id, const string &description, @@ -7006,7 +7005,7 @@ void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 e TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - td_->create_handler(std::move(promise)) + td_->create_handler(std::move(promise)) ->send(dialog_id, expire_date, usage_limit, is_permanent); } @@ -7102,7 +7101,7 @@ void ContactsManager::check_dialog_invite_link(const string &invite_link, Promis return promise.set_error(Status::Error(400, "Wrong invite link")); } - td_->create_handler(std::move(promise))->send(invite_link); + td_->create_handler(std::move(promise))->send(invite_link); } void ContactsManager::import_dialog_invite_link(const string &invite_link, Promise &&promise) { @@ -7110,7 +7109,7 @@ void ContactsManager::import_dialog_invite_link(const string &invite_link, Promi return promise.set_error(Status::Error(400, "Wrong invite link")); } - td_->create_handler(std::move(promise))->send(invite_link); + td_->create_handler(std::move(promise))->send(invite_link); } void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, @@ -11931,7 +11930,7 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, // the access is already expired, reget the info if (accessible_before != 0 && accessible_before <= G()->unix_time() + 1) { - td_->create_handler(std::move(promise))->send(invite_link); + td_->create_handler(std::move(promise))->send(invite_link); return; } diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 53de8a21d..69d703661 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -209,8 +209,10 @@ class JoinGroupCallQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - td->group_call_manager_->process_join_group_call_response(input_group_call_id_, generation_, - result_ptr.move_as_ok(), std::move(promise_)); + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for JoinGroupCallQuery with generation " << generation_ << ": " << to_string(ptr); + td->group_call_manager_->process_join_group_call_response(input_group_call_id_, generation_, std::move(ptr), + std::move(promise_)); } void on_error(uint64 id, Status status) override { @@ -1642,7 +1644,6 @@ void GroupCallManager::process_join_group_call_response(InputGroupCallId input_g return; } - LOG(INFO) << "Receive result for JoinGroupCallQuery: " << to_string(updates); td_->updates_manager_->on_get_updates(std::move(updates), PromiseCreator::lambda([promise = std::move(promise)](Unit) mutable { promise.set_error(Status::Error(500, "Wrong join response received")); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 7b1a5c0b6..7364f72b6 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -141,11 +141,11 @@ class UpdateDialogFilterQuery : public Td::ResultHandler { } }; -class ReorderDialogFiltersQuery : public Td::ResultHandler { +class UpdateDialogFiltersOrderQuery : public Td::ResultHandler { Promise promise_; public: - explicit ReorderDialogFiltersQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit UpdateDialogFiltersOrderQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(vector dialog_filter_ids) { @@ -1329,12 +1329,12 @@ class EditDialogTitleQuery : public Td::ResultHandler { } }; -class EditDialogDefaultBannedRightsQuery : public Td::ResultHandler { +class EditChatDefaultBannedRightsQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; public: - explicit EditDialogDefaultBannedRightsQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit EditChatDefaultBannedRightsQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, RestrictedRights permissions) { @@ -1352,7 +1352,7 @@ class EditDialogDefaultBannedRightsQuery : public Td::ResultHandler { } auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for EditDialogPermissionsQuery: " << to_string(ptr); + LOG(INFO) << "Receive result for EditChatDefaultBannedRightsQuery: " << to_string(ptr); td->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); } @@ -1363,7 +1363,7 @@ class EditDialogDefaultBannedRightsQuery : public Td::ResultHandler { return; } } else { - td->messages_manager_->on_get_dialog_error(dialog_id_, status, "EditDialogDefaultBannedRightsQuery"); + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "EditChatDefaultBannedRightsQuery"); } promise_.set_error(std::move(status)); } @@ -2637,7 +2637,7 @@ class DeleteUserHistoryQuery : public Td::ResultHandler { } }; -class ReadAllMentionsQuery : public Td::ResultHandler { +class ReadMentionsQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -2653,7 +2653,7 @@ class ReadAllMentionsQuery : public Td::ResultHandler { } public: - explicit ReadAllMentionsQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit ReadMentionsQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id) { @@ -2673,7 +2673,7 @@ class ReadAllMentionsQuery : public Td::ResultHandler { if (affected_history->pts_count_ > 0) { if (dialog_id_.get_type() == DialogType::Channel) { - LOG(ERROR) << "Receive pts_count " << affected_history->pts_count_ << " in result of ReadAllMentionsQuery in " + LOG(ERROR) << "Receive pts_count " << affected_history->pts_count_ << " in result of ReadMentionsQuery in " << dialog_id_; td->updates_manager_->get_difference("Wrong messages_readMentions result"); } else { @@ -2692,7 +2692,7 @@ class ReadAllMentionsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ReadAllMentionsQuery"); + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ReadMentionsQuery"); promise_.set_error(std::move(status)); } }; @@ -2794,7 +2794,7 @@ class SendMessageActor : public NetActorOnce { } auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for SendMessageQuery for " << random_id_ << ": " << to_string(ptr); + LOG(INFO) << "Receive result for SendMessage for " << random_id_ << ": " << to_string(ptr); auto constructor_id = ptr->get_id(); if (constructor_id != telegram_api::updateShortSentMessage::ID) { @@ -2822,7 +2822,7 @@ class SendMessageActor : public NetActorOnce { } void on_error(uint64 id, Status status) override { - LOG(INFO) << "Receive error for SendMessageQuery: " << status; + LOG(INFO) << "Receive error for SendMessage: " << status; if (G()->close_flag() && G()->parameters().use_message_db) { // do not send error, message will be re-sent return; @@ -2968,8 +2968,7 @@ class SendMultiMediaActor : public NetActorOnce { } auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for SendMultiMediaQuery for " << format::as_array(random_ids_) << ": " - << to_string(ptr); + LOG(INFO) << "Receive result for SendMultiMedia for " << format::as_array(random_ids_) << ": " << to_string(ptr); auto sent_random_ids = UpdatesManager::get_sent_messages_random_ids(ptr.get()); bool is_result_wrong = false; @@ -3000,7 +2999,7 @@ class SendMultiMediaActor : public NetActorOnce { } } if (is_result_wrong) { - LOG(ERROR) << "Receive wrong result for SendMultiMediaQuery with random_ids " << format::as_array(random_ids_) + LOG(ERROR) << "Receive wrong result for SendMultiMedia with random_ids " << format::as_array(random_ids_) << " to " << dialog_id_ << ": " << oneline(to_string(ptr)); td->updates_manager_->schedule_get_difference("Wrong sendMultiMedia result"); } @@ -3009,7 +3008,7 @@ class SendMultiMediaActor : public NetActorOnce { } void on_error(uint64 id, Status status) override { - LOG(INFO) << "Receive error for SendMultiMediaQuery: " << status; + LOG(INFO) << "Receive error for SendMultiMedia: " << status; if (G()->close_flag() && G()->parameters().use_message_db) { // do not send error, message will be re-sent return; @@ -3097,13 +3096,13 @@ class SendMediaActor : public NetActorOnce { } auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for SendMediaQuery for " << random_id_ << ": " << to_string(ptr); + LOG(INFO) << "Receive result for SendMedia for " << random_id_ << ": " << to_string(ptr); td->messages_manager_->check_send_message_result(random_id_, dialog_id_, ptr.get(), "SendMedia"); td->updates_manager_->on_get_updates(std::move(ptr), Promise()); } void on_error(uint64 id, Status status) override { - LOG(INFO) << "Receive error for SendMediaQuery: " << status; + LOG(INFO) << "Receive error for SendMedia: " << status; if (G()->close_flag() && G()->parameters().use_message_db) { // do not send error, message will be re-sent return; @@ -3348,7 +3347,7 @@ class EditMessageActor : public NetActorOnce { } void on_error(uint64 id, Status status) override { - LOG(INFO) << "Receive error for EditMessageQuery: " << status; + LOG(INFO) << "Receive error for EditMessage: " << status; if (!td->auth_manager_->is_bot() && status.message() == "MESSAGE_NOT_MODIFIED") { return promise_.set_value(0); } @@ -3459,12 +3458,12 @@ class SetGameScoreActor : public NetActorOnce { } auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for SetGameScoreActor: " << to_string(ptr); + LOG(INFO) << "Receive result for SetGameScore: " << to_string(ptr); td->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); } void on_error(uint64 id, Status status) override { - LOG(INFO) << "Receive error for SetGameScoreQuery: " << status; + LOG(INFO) << "Receive error for SetGameScore: " << status; td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetGameScoreActor"); promise_.set_error(std::move(status)); } @@ -3647,8 +3646,7 @@ class ForwardMessagesActor : public NetActorOnce { } auto ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for ForwardMessagesQuery for " << format::as_array(random_ids_) << ": " - << to_string(ptr); + LOG(INFO) << "Receive result for ForwardMessages for " << format::as_array(random_ids_) << ": " << to_string(ptr); auto sent_random_ids = UpdatesManager::get_sent_messages_random_ids(ptr.get()); bool is_result_wrong = false; auto sent_random_ids_size = sent_random_ids.size(); @@ -11006,7 +11004,7 @@ void MessagesManager::read_all_dialog_mentions_on_server(DialogId dialog_id, uin } LOG(INFO) << "Read all mentions on server in " << dialog_id; - td_->create_handler(get_erase_log_event_promise(log_event_id, std::move(promise))) + td_->create_handler(get_erase_log_event_promise(log_event_id, std::move(promise))) ->send(dialog_id); } @@ -17795,7 +17793,7 @@ void MessagesManager::reorder_dialog_filters_on_server(vector di send_closure(actor_id, &MessagesManager::on_reorder_dialog_filters, std::move(dialog_filter_ids), result.is_error() ? result.move_as_error() : Status::OK()); }); - td_->create_handler(std::move(promise))->send(std::move(dialog_filter_ids)); + td_->create_handler(std::move(promise))->send(std::move(dialog_filter_ids)); } void MessagesManager::on_reorder_dialog_filters(vector dialog_filter_ids, Status result) { @@ -30707,7 +30705,7 @@ void MessagesManager::set_dialog_permissions(DialogId dialog_id, } // TODO invoke after - td_->create_handler(std::move(promise))->send(dialog_id, new_permissions); + td_->create_handler(std::move(promise))->send(dialog_id, new_permissions); } void MessagesManager::set_dialog_description(DialogId dialog_id, const string &description, Promise &&promise) { diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index c34a2ad19..fd15dfb11 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -224,7 +224,7 @@ class StopPollActor : public NetActorOnce { } auto result = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for StopPollQuery: " << to_string(result); + LOG(INFO) << "Receive result for StopPoll: " << to_string(result); td->updates_manager_->on_get_updates(std::move(result), std::move(promise_)); } diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 6208ebc78..2f99ccfcd 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3137,7 +3137,7 @@ void Td::on_get_promo_data(Resultis_error()) { - LOG(ERROR) << "Receive error for GetCdnConfigQuery: " << net_query->move_as_error(); + LOG(ERROR) << "Receive error for GetCdnConfig: " << net_query->move_as_error(); return; } From 876932843a87aee6090b951f197d38297a78f394 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 8 Feb 2021 23:41:17 +0300 Subject: [PATCH 163/232] Support automatic deletion of messages by ttl_period. --- td/telegram/MessagesManager.cpp | 147 ++++++++++++++++++++++++++------ td/telegram/MessagesManager.h | 21 +++-- 2 files changed, 133 insertions(+), 35 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 7364f72b6..823af1424 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -4837,6 +4837,7 @@ void MessagesManager::Message::store(StorerT &storer) const { bool has_interaction_info_update_date = interaction_info_update_date != 0; bool has_send_emoji = !send_emoji.empty(); bool is_imported = is_forwarded && forward_info->is_imported; + bool has_ttl_period = ttl_period != 0; BEGIN_STORE_FLAGS(); STORE_FLAG(is_channel_post); STORE_FLAG(is_outgoing); @@ -4897,6 +4898,7 @@ void MessagesManager::Message::store(StorerT &storer) const { STORE_FLAG(has_interaction_info_update_date); STORE_FLAG(has_send_emoji); STORE_FLAG(is_imported); + STORE_FLAG(has_ttl_period); // 25 END_STORE_FLAGS(); } @@ -5009,6 +5011,9 @@ void MessagesManager::Message::store(StorerT &storer) const { if (has_reply_markup) { store(reply_markup, storer); } + if (has_ttl_period) { + store(ttl_period, storer); + } } // do not forget to resolve message dependencies @@ -5049,6 +5054,7 @@ void MessagesManager::Message::parse(ParserT &parser) { bool has_interaction_info_update_date = false; bool has_send_emoji = false; bool is_imported = false; + bool has_ttl_period = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_channel_post); PARSE_FLAG(is_outgoing); @@ -5109,6 +5115,7 @@ void MessagesManager::Message::parse(ParserT &parser) { PARSE_FLAG(has_interaction_info_update_date); PARSE_FLAG(has_send_emoji); PARSE_FLAG(is_imported); + PARSE_FLAG(has_ttl_period); END_PARSE_FLAGS(); } @@ -5228,6 +5235,9 @@ void MessagesManager::Message::parse(ParserT &parser) { if (has_reply_markup) { parse(reply_markup, parser); } + if (has_ttl_period) { + parse(ttl_period, parser); + } is_content_secret |= is_secret_message_content(ttl, content->get_type()); // repair is_content_secret for old messages @@ -7423,7 +7433,7 @@ void MessagesManager::process_channel_update(tl_object_ptr } auto dialog_id = DialogId(channel_id); - delete_dialog_messages_from_updates(dialog_id, message_ids, false); + delete_dialog_messages(dialog_id, message_ids, true, false); break; } case telegram_api::updateEditChannelMessage::ID: { @@ -9085,7 +9095,7 @@ void MessagesManager::after_get_difference() { void MessagesManager::on_get_empty_messages(DialogId dialog_id, vector empty_message_ids) { if (!empty_message_ids.empty()) { - delete_dialog_messages_from_updates(dialog_id, std::move(empty_message_ids), true); + delete_dialog_messages(dialog_id, std::move(empty_message_ids), true, true); } } @@ -9903,12 +9913,12 @@ void MessagesManager::delete_messages_from_updates(const vector &mess } } -void MessagesManager::delete_dialog_messages_from_updates(DialogId dialog_id, const vector &message_ids, - bool skip_update_for_not_found_messages) { - CHECK(dialog_id.get_type() == DialogType::Channel || dialog_id.get_type() == DialogType::SecretChat); +void MessagesManager::delete_dialog_messages(DialogId dialog_id, const vector &message_ids, + bool from_updates, bool skip_update_for_not_found_messages) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { LOG(INFO) << "Ignore deleteChannelMessages for unknown " << dialog_id; + CHECK(from_updates); CHECK(dialog_id.get_type() == DialogType::Channel); return; } @@ -9916,12 +9926,16 @@ void MessagesManager::delete_dialog_messages_from_updates(DialogId dialog_id, co vector deleted_message_ids; bool need_update_dialog_pos = false; for (auto message_id : message_ids) { - if (!message_id.is_valid() || (!message_id.is_server() && dialog_id.get_type() != DialogType::SecretChat)) { - LOG(ERROR) << "Incoming update tries to delete " << message_id; - continue; + if (from_updates) { + if (!message_id.is_valid() || (!message_id.is_server() && dialog_id.get_type() != DialogType::SecretChat)) { + LOG(ERROR) << "Incoming update tries to delete " << message_id; + continue; + } + } else { + CHECK(message_id.is_valid()); } - auto message = delete_message(d, message_id, true, &need_update_dialog_pos, "updates"); + auto message = delete_message(d, message_id, true, &need_update_dialog_pos, "delete_dialog_messages"); if (message == nullptr) { if (!skip_update_for_not_found_messages) { deleted_message_ids.push_back(message_id.get()); @@ -9931,7 +9945,7 @@ void MessagesManager::delete_dialog_messages_from_updates(DialogId dialog_id, co } } if (need_update_dialog_pos) { - send_update_chat_last_message(d, "delete_dialog_messages_from_updates"); + send_update_chat_last_message(d, "delete_dialog_messages"); } send_update_delete_messages(dialog_id, std::move(deleted_message_ids), true, false); } @@ -11904,12 +11918,11 @@ bool MessagesManager::ttl_on_open(Dialog *d, Message *m, double now, bool is_loc } void MessagesManager::ttl_register_message(DialogId dialog_id, const Message *m, double now) { - if (m->ttl_expires_at == 0) { - return; - } + CHECK(m != nullptr); + CHECK(m->ttl_expires_at != 0); CHECK(!m->message_id.is_scheduled()); - auto it_flag = ttl_nodes_.insert(TtlNode(dialog_id, m->message_id)); + auto it_flag = ttl_nodes_.emplace(dialog_id, m->message_id, false); CHECK(it_flag.second); auto it = it_flag.first; @@ -11917,14 +11930,27 @@ void MessagesManager::ttl_register_message(DialogId dialog_id, const Message *m, ttl_update_timeout(now); } -void MessagesManager::ttl_unregister_message(DialogId dialog_id, const Message *m, double now, const char *source) { +void MessagesManager::ttl_period_register_message(DialogId dialog_id, const Message *m, int32 unix_time) { + CHECK(m != nullptr); + CHECK(m->ttl_period != 0); + CHECK(!m->message_id.is_scheduled()); + + auto it_flag = ttl_nodes_.emplace(dialog_id, m->message_id, true); + CHECK(it_flag.second); + auto it = it_flag.first; + + auto now = Time::now(); + ttl_heap_.insert(now + (m->date + m->ttl_period - unix_time), it->as_heap_node()); + ttl_update_timeout(now); +} + +void MessagesManager::ttl_unregister_message(DialogId dialog_id, const Message *m, const char *source) { if (m->ttl_expires_at == 0) { return; } CHECK(!m->message_id.is_scheduled()); - TtlNode ttl_node(dialog_id, m->message_id); - auto it = ttl_nodes_.find(ttl_node); + auto it = ttl_nodes_.find(TtlNode(dialog_id, m->message_id, false)); // expect m->ttl == 0, but m->ttl_expires_at > 0 from binlog LOG_CHECK(it != ttl_nodes_.end()) << dialog_id << " " << m->message_id << " " << source << " " << G()->close_flag() @@ -11936,15 +11962,34 @@ void MessagesManager::ttl_unregister_message(DialogId dialog_id, const Message * ttl_heap_.erase(heap_node); } ttl_nodes_.erase(it); - ttl_update_timeout(now); + ttl_update_timeout(Time::now()); +} + +void MessagesManager::ttl_period_unregister_message(DialogId dialog_id, const Message *m) { + if (m->ttl_period == 0) { + return; + } + CHECK(!m->message_id.is_scheduled()); + + auto it = ttl_nodes_.find(TtlNode(dialog_id, m->message_id, true)); + + CHECK(it != ttl_nodes_.end()); + + auto *heap_node = it->as_heap_node(); + if (heap_node->in_heap()) { + ttl_heap_.erase(heap_node); + } + ttl_nodes_.erase(it); + ttl_update_timeout(Time::now()); } void MessagesManager::ttl_loop(double now) { std::unordered_map, DialogIdHash> to_delete; while (!ttl_heap_.empty() && ttl_heap_.top_key() < now) { - auto full_message_id = TtlNode::from_heap_node(ttl_heap_.pop())->full_message_id; + TtlNode *ttl_node = TtlNode::from_heap_node(ttl_heap_.pop()); + auto full_message_id = ttl_node->full_message_id_; auto dialog_id = full_message_id.get_dialog_id(); - if (dialog_id.get_type() == DialogType::SecretChat) { + if (dialog_id.get_type() == DialogType::SecretChat || ttl_node->by_ttl_period_) { to_delete[dialog_id].push_back(full_message_id.get_message_id()); } else { auto d = get_dialog(dialog_id); @@ -11956,7 +12001,7 @@ void MessagesManager::ttl_loop(double now) { } } for (auto &it : to_delete) { - delete_dialog_messages_from_updates(it.first, it.second, false); + delete_dialog_messages(it.first, it.second, false, true); } ttl_update_timeout(now); } @@ -11977,7 +12022,7 @@ void MessagesManager::on_message_ttl_expired(Dialog *d, Message *m) { CHECK(m != nullptr); CHECK(m->ttl > 0); CHECK(d->dialog_id.get_type() != DialogType::SecretChat); - ttl_unregister_message(d->dialog_id, m, Time::now(), "on_message_ttl_expired"); + ttl_unregister_message(d->dialog_id, m, "on_message_ttl_expired"); unregister_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}, "on_message_ttl_expired"); remove_message_file_sources(d->dialog_id, m); on_message_ttl_expired_impl(d, m); @@ -12660,7 +12705,7 @@ void MessagesManager::finish_delete_secret_messages(DialogId dialog_id, std::vec LOG(INFO) << "Skip deletion of service " << message_id; } } - delete_dialog_messages_from_updates(dialog_id, to_delete_message_ids, false); + delete_dialog_messages(dialog_id, to_delete_message_ids, true, false); } void MessagesManager::delete_secret_chat_history(SecretChatId secret_chat_id, bool remove_from_dialog_list, @@ -13033,6 +13078,9 @@ MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( if (message->flags_ & MESSAGE_FLAG_HAS_MEDIA_ALBUM_ID) { message_info.media_album_id = message->grouped_id_; } + if (message->flags_ & MESSAGE_FLAG_HAS_TTL_PERIOD) { + message_info.ttl_period = message->ttl_period_; + } message_info.flags = message->flags_; bool is_content_read = (message->flags_ & MESSAGE_FLAG_HAS_UNREAD_CONTENT) == 0; if (is_message_auto_read(message_info.dialog_id, (message->flags_ & MESSAGE_FLAG_IS_OUT) != 0)) { @@ -13223,9 +13271,15 @@ std::pair> MessagesManager::creat hide_edit_date = false; } + int32 ttl_period = message_info.ttl_period; + if (ttl_period < 0 || message_id.is_scheduled()) { + LOG(ERROR) << "Wrong TTL period = " << ttl_period << " received in " << message_id << " in " << dialog_id; + ttl_period = 0; + } + int32 ttl = message_info.ttl; bool is_content_secret = is_secret_message_content(ttl, content_type); // should be calculated before TTL is adjusted - if (ttl < 0) { + if (ttl < 0 || message_id.is_scheduled()) { LOG(ERROR) << "Wrong TTL = " << ttl << " received in " << message_id << " in " << dialog_id; ttl = 0; } else if (ttl > 0) { @@ -13260,6 +13314,7 @@ std::pair> MessagesManager::creat message->sender_user_id = sender_user_id; message->sender_dialog_id = sender_dialog_id; message->date = date; + message->ttl_period = ttl_period; message->ttl = ttl; message->edit_date = edit_date; message->random_id = message_info.random_id; @@ -15001,7 +15056,8 @@ void MessagesManager::on_message_deleted(Dialog *d, Message *m, bool is_permanen default: UNREACHABLE(); } - ttl_unregister_message(d->dialog_id, m, Time::now(), source); + ttl_unregister_message(d->dialog_id, m, source); + ttl_period_unregister_message(d->dialog_id, m); unregister_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}, "on_message_deleted"); if (m->notification_id.is_valid()) { delete_notification_id_to_message_id_correspondence(d, m->notification_id, m->message_id); @@ -23339,10 +23395,10 @@ Result MessagesManager::process_input_message_content( TRY_RESULT(content, get_input_message_content(dialog_id, std::move(input_message_content), td_)); if (content.ttl < 0 || content.ttl > MAX_PRIVATE_MESSAGE_TTL) { - return Status::Error(10, "Invalid message TTL specified"); + return Status::Error(10, "Invalid message content TTL specified"); } if (content.ttl > 0 && dialog_id.get_type() != DialogType::User) { - return Status::Error(10, "Message TTL can be specified only in private chats"); + return Status::Error(10, "Message content TTL can be specified only in private chats"); } if (dialog_id != DialogId()) { @@ -28495,6 +28551,8 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI CHECK(d->last_message_id != old_message_id); } + sent_message->ttl_period = ttl_period; + // reply_to message may be already deleted // but can't use get_message_force for check, because the message can be already unloaded from the memory // if (get_message_force(d, sent_message->reply_to_message_id, "on_send_message_success 2") == nullptr) { @@ -31907,6 +31965,19 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq ttl_register_message(dialog_id, message.get(), now); } } + if (message->ttl_period > 0) { + CHECK(dialog_id.get_type() != DialogType::SecretChat); + auto unix_time = G()->unix_time(); + if (message->date + message->ttl_period <= unix_time) { + LOG(INFO) << "Can't add " << message_id << " with expired TTL period to " << dialog_id << " from " << source; + delete_message_from_database(d, message_id, message.get(), true); + debug_add_message_to_dialog_fail_reason_ = "delete expired by TTL period message"; + d->being_added_message_id = MessageId(); + return nullptr; + } else { + ttl_period_register_message(dialog_id, message.get(), unix_time); + } + } LOG(INFO) << "Adding not found " << message_id << " to " << dialog_id << " from " << source; if (d->is_empty) { @@ -32297,6 +32368,12 @@ MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialo debug_add_message_to_dialog_fail_reason_ = "skip adding secret scheduled message"; return nullptr; } + if (message->ttl_period != 0) { + LOG(ERROR) << "Tried to add " << message_id << " with TTL period " << message->ttl_period << " to " << dialog_id + << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "skip adding auto-deleting scheduled message"; + return nullptr; + } if (td_->auth_manager_->is_bot()) { LOG(ERROR) << "Bot tried to add " << message_id << " to " << dialog_id << " from " << source; debug_add_message_to_dialog_fail_reason_ = "skip adding scheduled message by bot"; @@ -32468,7 +32545,10 @@ void MessagesManager::add_message_to_database(const Dialog *d, const Message *m, int32 ttl_expires_at = 0; if (m->ttl_expires_at != 0) { - ttl_expires_at = static_cast(m->ttl_expires_at - Time::now() + G()->server_time()); + ttl_expires_at = static_cast(m->ttl_expires_at - Time::now() + G()->server_time()) + 1; + } + if (m->ttl_period != 0 && (ttl_expires_at == 0 || m->date + m->ttl_period < ttl_expires_at)) { + ttl_expires_at = m->date + m->ttl_period; } G()->td_db()->get_messages_db_async()->add_message({d->dialog_id, message_id}, unique_message_id, m->sender_user_id, random_id, ttl_expires_at, get_message_index_mask(d->dialog_id, m), @@ -32921,6 +33001,17 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr old_message->is_mention_notification_disabled = true; } + if (old_message->ttl_period != new_message->ttl_period) { + if (old_message->ttl_period != 0 || !message_id.is_yet_unsent()) { + LOG(ERROR) << message_id << " in " << dialog_id << " has changed TTL period from " << old_message->ttl_period + << " to " << new_message->ttl_period; + } else { + LOG(DEBUG) << "Change message TTL period"; + old_message->ttl_period = new_message->ttl_period; + need_send_update = true; + } + } + if (old_message->reply_to_message_id != new_message->reply_to_message_id) { // Can't check "&& get_message_force(d, old_message->reply_to_message_id, "update_message") == nullptr", because it // can change message tree and invalidate reference to old_message diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 01c1ae85e..c7c2beba8 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -117,6 +117,7 @@ class MessagesManager : public Actor { static constexpr int32 MESSAGE_FLAG_IS_RESTRICTED = 1 << 22; static constexpr int32 MESSAGE_FLAG_HAS_REPLY_INFO = 1 << 23; static constexpr int32 MESSAGE_FLAG_IS_PINNED = 1 << 24; + static constexpr int32 MESSAGE_FLAG_HAS_TTL_PERIOD = 1 << 25; static constexpr int32 SEND_MESSAGE_FLAG_IS_REPLY = 1 << 0; static constexpr int32 SEND_MESSAGE_FLAG_DISABLE_WEB_PAGE_PREVIEW = 1 << 1; @@ -924,6 +925,7 @@ class MessagesManager : public Actor { UserId sender_user_id; DialogId sender_dialog_id; int32 date = 0; + int32 ttl_period = 0; int32 ttl = 0; int64 random_id = 0; tl_object_ptr forward_header; @@ -1068,6 +1070,7 @@ class MessagesManager : public Actor { string send_error_message; double try_resend_at = 0; + int32 ttl_period = 0; int32 ttl = 0; double ttl_expires_at = 0; @@ -1785,8 +1788,8 @@ class MessagesManager : public Actor { void delete_messages_from_updates(const vector &message_ids); - void delete_dialog_messages_from_updates(DialogId dialog_id, const vector &message_ids, - bool skip_update_for_not_found_messages); + void delete_dialog_messages(DialogId dialog_id, const vector &message_ids, bool from_updates, + bool skip_update_for_not_found_messages); void update_dialog_pinned_messages_from_updates(DialogId dialog_id, const vector &message_ids, bool is_pin); @@ -2589,7 +2592,9 @@ class MessagesManager : public Actor { void ttl_on_view(const Dialog *d, Message *m, double view_date, double now); bool ttl_on_open(Dialog *d, Message *m, double now, bool is_local_read); void ttl_register_message(DialogId dialog_id, const Message *m, double now); - void ttl_unregister_message(DialogId dialog_id, const Message *m, double now, const char *source); + void ttl_unregister_message(DialogId dialog_id, const Message *m, const char *source); + void ttl_period_register_message(DialogId dialog_id, const Message *m, int32 unix_time); + void ttl_period_unregister_message(DialogId dialog_id, const Message *m); void ttl_loop(double now); void ttl_update_timeout(double now); @@ -2989,10 +2994,12 @@ class MessagesManager : public Actor { // TTL class TtlNode : private HeapNode { public: - TtlNode(DialogId dialog_id, MessageId message_id) : full_message_id(dialog_id, message_id) { + TtlNode(DialogId dialog_id, MessageId message_id, bool by_ttl_period) + : full_message_id_(dialog_id, message_id), by_ttl_period_(by_ttl_period) { } - FullMessageId full_message_id; + FullMessageId full_message_id_; + bool by_ttl_period_; HeapNode *as_heap_node() const { return const_cast(static_cast(this)); @@ -3002,12 +3009,12 @@ class MessagesManager : public Actor { } bool operator==(const TtlNode &other) const { - return full_message_id == other.full_message_id; + return full_message_id_ == other.full_message_id_; } }; struct TtlNodeHash { std::size_t operator()(const TtlNode &ttl_node) const { - return FullMessageIdHash()(ttl_node.full_message_id); + return FullMessageIdHash()(ttl_node.full_message_id_) * 2 + static_cast(ttl_node.by_ttl_period_); } }; std::unordered_set ttl_nodes_; From 2913b7288bc2575761666615517512922b2e9818 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 9 Feb 2021 16:05:00 +0300 Subject: [PATCH 164/232] Add MessageTtlSetting class. --- CMakeLists.txt | 2 ++ td/telegram/MessageTtlSetting.cpp | 27 +++++++++++++++ td/telegram/MessageTtlSetting.h | 57 +++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 td/telegram/MessageTtlSetting.cpp create mode 100644 td/telegram/MessageTtlSetting.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 97e1b5883..8ff8afb9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,6 +340,7 @@ set(TDLIB_SOURCE td/telegram/MessagesDb.cpp td/telegram/MessageSearchFilter.cpp td/telegram/MessagesManager.cpp + td/telegram/MessageTtlSetting.cpp td/telegram/misc.cpp td/telegram/net/AuthDataShared.cpp td/telegram/net/ConnectionCreator.cpp @@ -520,6 +521,7 @@ set(TDLIB_SOURCE td/telegram/MessagesDb.h td/telegram/MessageSearchFilter.h td/telegram/MessagesManager.h + td/telegram/MessageTtlSetting.h td/telegram/misc.h td/telegram/net/AuthDataShared.h td/telegram/net/ConnectionCreator.h diff --git a/td/telegram/MessageTtlSetting.cpp b/td/telegram/MessageTtlSetting.cpp new file mode 100644 index 000000000..00adec19b --- /dev/null +++ b/td/telegram/MessageTtlSetting.cpp @@ -0,0 +1,27 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/MessageTtlSetting.h" + +namespace td { + +bool MessageTtlSetting::is_empty() const { + return ttl_period_ == 0; +} + +int32 MessageTtlSetting::get_message_ttl_setting_object() const { + return ttl_period_; +} + +bool operator==(const MessageTtlSetting &lhs, const MessageTtlSetting &rhs) { + return lhs.ttl_period_ == rhs.ttl_period_; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const MessageTtlSetting &message_ttl_setting) { + return string_builder << "MessageTtlSetting[" << message_ttl_setting.ttl_period_ << "]"; +} + +} // namespace td diff --git a/td/telegram/MessageTtlSetting.h b/td/telegram/MessageTtlSetting.h new file mode 100644 index 000000000..5532eaa3c --- /dev/null +++ b/td/telegram/MessageTtlSetting.h @@ -0,0 +1,57 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +class MessageTtlSetting { + int32 ttl_period_ = 0; + + friend StringBuilder &operator<<(StringBuilder &string_builder, const MessageTtlSetting &message_ttl_setting); + + friend bool operator==(const MessageTtlSetting &lhs, const MessageTtlSetting &rhs); + + public: + MessageTtlSetting() = default; + + template ::value>> + MessageTtlSetting(T ttl_period) = delete; + + explicit MessageTtlSetting(int32 ttl_period) : ttl_period_(ttl_period) { + } + + bool is_empty() const; + + int32 get_message_ttl_setting_object() const; + + template + void store(StorerT &storer) const { + td::store(ttl_period_, storer); + } + + template + void parse(ParserT &parser) { + td::parse(ttl_period_, parser); + } +}; + +bool operator==(const MessageTtlSetting &lhs, const MessageTtlSetting &rhs); + +inline bool operator!=(const MessageTtlSetting &lhs, const MessageTtlSetting &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const MessageTtlSetting &message_ttl_setting); + +} // namespace td From cc50f3b14302863c2266d4bca2b1da10a9bcaa86 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 9 Feb 2021 17:35:48 +0300 Subject: [PATCH 165/232] Support receiving message TTL setting for all chat types. --- td/generate/scheme/td_api.tl | 9 +++-- td/generate/scheme/td_api.tlo | Bin 196552 -> 196712 bytes td/telegram/ContactsManager.cpp | 40 +++++++++++++++++---- td/telegram/ContactsManager.h | 4 +++ td/telegram/MessagesManager.cpp | 62 +++++++++++++++++++++++++------- td/telegram/MessagesManager.h | 13 +++++-- td/telegram/UpdatesManager.cpp | 14 +++++--- td/telegram/UpdatesManager.h | 4 +-- 8 files changed, 115 insertions(+), 31 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 57afc9add..d02034e30 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -658,11 +658,10 @@ secretChatStateClosed = SecretChatState; //@user_id Identifier of the chat partner //@state State of the secret chat //@is_outbound True, if the chat was created by the current user; otherwise false -//@ttl Current message Time To Live setting (self-destruct timer) for the chat, in seconds //@key_hash Hash of the currently used key for comparison with the hash of the chat partner's key. This is a string of 36 little-endian bytes, which must be split into groups of 2 bits, each denoting a pixel of one of 4 colors FFFFFF, D5E6F3, 2D5775, and 2F99C9. //-The pixels must be used to make a 12x12 square image filled from left to right, top to bottom. Alternatively, the first 32 bytes of the hash can be converted to the hexadecimal format and printed as 32 2-digit hex numbers //@layer Secret chat layer; determines features supported by the chat partner's application. Video notes are supported if the layer >= 66; nested text entities and underline and strikethrough entities are supported if the layer >= 101 -secretChat id:int32 user_id:int32 state:SecretChatState is_outbound:Bool ttl:int32 key_hash:bytes layer:int32 = SecretChat; +secretChat id:int32 user_id:int32 state:SecretChatState is_outbound:Bool key_hash:bytes layer:int32 = SecretChat; //@class MessageSender @description Contains information about the sender of a message @@ -907,13 +906,14 @@ chatPosition list:ChatList order:int64 is_pinned:Bool source:ChatSource = ChatPo //@last_read_outbox_message_id Identifier of the last read outgoing message //@unread_mention_count Number of unread messages with a mention/reply in the chat //@notification_settings Notification settings for this chat +//@message_ttl_setting Current message Time To Live setting (self-destruct timer) for the chat; 0 if not defined. TTL is counted from the time message or its content is viewed in secret chats and from the send date in other chats //@action_bar Describes actions which should be possible to do through a chat action bar; may be null //@voice_chat_group_call_id Group call identifier of an active voice chat; 0 if none or unknown. The voice chat can be received through the method getGroupCall //@is_voice_chat_empty True, if an active voice chat is empty //@reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat //@draft_message A draft of a message in the chat; may be null //@client_data Contains application-specific data associated with the chat. (For example, the chat scroll position or local chat notification settings can be stored here.) Persistent if the message database is used -chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector is_marked_as_unread:Bool is_blocked:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings action_bar:ChatActionBar voice_chat_group_call_id:int32 is_voice_chat_empty:Bool reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; +chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector is_marked_as_unread:Bool is_blocked:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings message_ttl_setting:int32 action_bar:ChatActionBar voice_chat_group_call_id:int32 is_voice_chat_empty:Bool reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; //@description Represents a list of chats @total_count Approximate total count of chats found @chat_ids List of chat identifiers chats total_count:int32 chat_ids:vector = Chats; @@ -3390,6 +3390,9 @@ updateChatNotificationSettings chat_id:int53 notification_settings:chatNotificat //@description Notification settings for some type of chats were updated @scope Types of chats for which notification settings were updated @notification_settings The new notification settings updateScopeNotificationSettings scope:NotificationSettingsScope notification_settings:scopeNotificationSettings = Update; +//@description The message Time To Live setting for a chat was changed @chat_id Chat identifier @message_ttl_setting New value of message_ttl_setting +updateChatMessageTtlSetting chat_id:int53 message_ttl_setting:int32 = Update; + //@description The chat action bar was changed @chat_id Chat identifier @action_bar The new value of the action bar; may be null updateChatActionBar chat_id:int53 action_bar:ChatActionBar = Update; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index fd3c01829d4de6429ef6d12130698613e4ca6b97..89dd8c953ca1472f4d2e1047e3da3f6fbb207179 100644 GIT binary patch delta 2566 zcmZuzZ){Ul6z@Ijx^63fH^Sm7R2?$in@nxAnwnI%$b>G|I3s8oowxwW(1w+S5BtzW zVu(>PeH#a3Ka7M#O!i?|O!+WlG1KhBVrl}(FjXgGbFz&U?4XP?eD9rmUWcKd?)#l{ ze&?Luz329h+$*1sI{Iy;k8XSKy0vj%dqh2Bua4K)JT=zuHXQ1Xho1D6tT^VrP+@y3 z{C<0+!_umL4(sE?PtSrl7(e6wYSTI+bdMHlu~_u=rJvXOABwlXv$beV#et5luJ*kh zyCad#-CZ3KjeW5?etMTxSar{j2HO&YZNl4Hspl%|>ezFzBcc~!(ZAKqe3hEs-hj<< z(S)btqS+|v>ug*+Wz|c<++V^=E6-(7W31Hh-UfntFAKR!eFCk7xh8}{tH6ZVL92lY zQA?|_32};6(JPE4Ul9S!o1e9lG7knOML#)bC)wFGB@Po7n=+G6i6~*Me{=FeL1_~+ z5(TG)o1A;6O@c!8{|J{yZ(LVcXlhqa{rQAdDzAN7>@R(MUe{H(>A!=GHbut_u?}m| z#2ua!3I@_Ziect{gYPQ0xbLdj*fFTbi@(8UBA7J|TGfhbe>aHzGr~^Bff^2S^W3H3+??UqBbjAc6)fzH^Ew*>*SVatCgwZ@nz~oP z?bc)KryEbC-2J1h1V|hQS(x#|^4`pERU$z;d1>0(|bB4unW|{S9zwlspmN%_8d*7yI zvfO^n0!R24#6FVq+=8erxF<1XB(L~tvEhcp4L^olv99J6M zshx+GWAH5cW<)X8-xjZvOY}CojNgSFs9b_aaMPlAjI91eS(`e(-;0Bb+|0htWqWkdqc`~h`pK1?T~&jqAL zpHEI=Uj*oy=sN*)7xkrpF3sZtfiW1YZ$F#tJlN)Z$ehz(By z)Zx%gQG)|TFicbttE>UNlT0;h<@>6#SQdug)qftOw45ic?$$>P)pW^Z=nIt z*}zMmIn7{dtVv^oMlys;I3iX8e!^x;IGfYXLUh|18?Zx+{QRZ&k-i)k>#?^KT;Tcdml%v3jWU?sgOq$qg(itJ1beRrb=!#RVk%Q zxYj}F8RLP0+MKzuzeV%b{}&VETVa%*9ys`#rkf6;bDK2d_%B)l!}0Joa7oA5c1V(A z$S)nWD@7@=Vw!PGozRY)LF#MKx-nM9SQkndX@T%YvSgY$UHAY5>x|b8QRp{I!to_| z$_-D|x8H@yo#6Q5IlBQQqNm`Ti&#=~+Bg_RM}C69mMXq`|Nj6;q_Z$=qnJYGu{)3{ F`VXMQr^^5U delta 2477 zcmZuzUu;ul6yG`Px^C;BK+zhOaFwCRiWHsLhZ<`jx{QKVXIyZQ68^Y{X)ZA1V)oEP zgCtHey=-Sj9%eG*%S^yd%R`MdnaD#;B+*2YB`mC~bL;*f498ZO-@V^=?uzj~efM|H z?|1&(?|k2#h`TQ@SM@t9dKzagJ9f1mYBw&pHY94D!CJ@1r=E)>Vq3%I)!)5avDNvS ze&kU5(UMnIS@Qysf@x+Dt&o{5PsD}l$q@=`LU%j+8@=)T?{ufIVza4OZ!^GYn zIhY(d2%#pgsakpR=;0Gb+s$w#=DQId^O5Hj@EHBaG-^-4ZYpes8oHQ(1}lxP97FJg z!%Tx**C8ZV4cDR8S{doqfP@CFgIehyVUxrHW8jyo*cdd+RcZ{jRG4yRt`6giH#dd4ckOY9YP)zXaXowLN3+$Y&3sPma{5io>e^|+mfG=Y z@YCD~%GUKx-iJpgxx$V~t}A^8w;6AJ_?V;@Y+U5*H(YPD(D^BFNn>h?>DpC6disZT zG%y8TX$jc{5RMTyTPdp+gR!y6n&jU^$GKT z+Yv=k#_tC&EDKBBV%_X5h)Aq{{=Uw3 z&vTx`^X#0ThgRv_e7k7tyXeL|Jd;e{h6qTrXMwfC3$R;arx#c-wZKCXy2B-C`^86_ zK7&tDbPCGompky1q-l%n62FJdl(~uz(@%@ANm}!ZoZDzl2z>njZlIPWZbIu4)TrP~ zcJL);WS4kSY6^_)E-=<@yeC7f3zcVqyen$A5p8|Ke@R+^ozRO)J*qFA6^%T3u z!TRngnvp*1r-?_!spVPbDr%OwiqJAwp)Yfs{$)6C)!5U7e)}{6difgG8T!w10D96W z8__e$^Wp_mvm6B8ZaSbkvQ928MU^Wv1m1pO00fW z(KaZfQ`OIu6>mj+>KV%t*f{Zf!3%Nr6_c+Nm_Soi}^R z%T3FAxpaNKYF{$F9%D~hzXv2Ht)9aBgtvKxCF2d4kv|R;Z4`YpK}t0$Go>3u0L#(0 z2Q}pwieTCTd-p0oYp!ik>SUbs&%|D{G)MP}0*r6s&mlx{`yyp1h& z)(?r4Fprkxpb)FF?1ty0MzQ_q{&c5H60|Szq9#@J88HWziuV#mxBK`i|L;|u?3~A8 Nr{t_e?kM0~>Az^%S!DnK diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 4e991ceb5..c754780d2 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -26,6 +26,7 @@ #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/MessagesManager.h" +#include "td/telegram/MessageTtlSetting.h" #include "td/telegram/misc.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/NotificationManager.h" @@ -9599,6 +9600,11 @@ void ContactsManager::update_secret_chat(SecretChat *c, SecretChatId secret_chat c->state); c->is_state_changed = false; } + if (c->is_ttl_changed) { + send_closure_later(G()->messages_manager(), &MessagesManager::on_update_dialog_message_ttl_setting, + DialogId(secret_chat_id), MessageTtlSetting(c->ttl)); + c->is_ttl_changed = false; + } } if (c->is_changed) { send_closure(G()->td(), &Td::send_update, @@ -9768,6 +9774,13 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u } td_->messages_manager_->on_update_dialog_has_scheduled_server_messages( DialogId(user_id), (user_full->flags_ & USER_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); + { + MessageTtlSetting message_ttl_setting; + if ((user_full->flags_ & USER_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { + message_ttl_setting = MessageTtlSetting(user_full->ttl_period_); + } + td_->messages_manager_->on_update_dialog_message_ttl_setting(DialogId(user_id), message_ttl_setting); + } UserFull *user = add_user_full(user_id); user->expires_at = Time::now() + USER_FULL_EXPIRE_TIME; @@ -10025,6 +10038,13 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c } td_->messages_manager_->on_update_dialog_group_call_id(DialogId(chat_id), input_group_call_id); } + { + MessageTtlSetting message_ttl_setting; + if ((chat_full->flags_ & CHAT_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { + message_ttl_setting = MessageTtlSetting(chat_full->ttl_period_); + } + td_->messages_manager_->on_update_dialog_message_ttl_setting(DialogId(chat_id), message_ttl_setting); + } ChatFull *chat = add_chat_full(chat_id); on_update_chat_full_invite_link(chat, std::move(chat_full->exported_invite_)); @@ -10080,6 +10100,13 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c td_->messages_manager_->on_update_dialog_notify_settings( DialogId(channel_id), std::move(channel_full->notify_settings_), "on_get_channel_full"); + { + MessageTtlSetting message_ttl_setting; + if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { + message_ttl_setting = MessageTtlSetting(channel_full->ttl_period_); + } + td_->messages_manager_->on_update_dialog_message_ttl_setting(DialogId(channel_id), message_ttl_setting); + } auto c = get_channel(channel_id); if (c == nullptr) { @@ -13851,7 +13878,8 @@ void ContactsManager::on_update_secret_chat(SecretChatId secret_chat_id, int64 a if (ttl != -1 && ttl != secret_chat->ttl) { secret_chat->ttl = ttl; - secret_chat->is_changed = true; + secret_chat->need_save_to_database = true; + secret_chat->is_ttl_changed = true; } if (date != 0 && date != secret_chat->date) { secret_chat->date = date; @@ -15242,7 +15270,7 @@ tl_object_ptr ContactsManager::get_secret_chat_state_ob td_api::object_ptr ContactsManager::get_update_unknown_secret_chat_object( SecretChatId secret_chat_id) { return td_api::make_object(td_api::make_object( - secret_chat_id.get(), 0, get_secret_chat_state_object(SecretChatState::Unknown), false, 0, string(), 0)); + secret_chat_id.get(), 0, get_secret_chat_state_object(SecretChatState::Unknown), false, string(), 0)); } int32 ContactsManager::get_secret_chat_id_object(SecretChatId secret_chat_id, const char *source) const { @@ -15270,10 +15298,10 @@ tl_object_ptr ContactsManager::get_secret_chat_object(Secret tl_object_ptr ContactsManager::get_secret_chat_object_const(SecretChatId secret_chat_id, const SecretChat *secret_chat) const { - return td_api::make_object( - secret_chat_id.get(), get_user_id_object(secret_chat->user_id, "secretChat"), - get_secret_chat_state_object(secret_chat->state), secret_chat->is_outbound, secret_chat->ttl, - secret_chat->key_hash, secret_chat->layer); + return td_api::make_object(secret_chat_id.get(), + get_user_id_object(secret_chat->user_id, "secretChat"), + get_secret_chat_state_object(secret_chat->state), + secret_chat->is_outbound, secret_chat->key_hash, secret_chat->layer); } td_api::object_ptr ContactsManager::get_bot_info_object(UserId user_id) const { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index d703f2fee..8998baed2 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -881,6 +881,7 @@ class ContactsManager : public Actor { bool is_outbound = false; + bool is_ttl_changed = true; bool is_state_changed = true; bool is_changed = true; // have new changes that need to be sent to the client and database bool need_save_to_database = true; // have new changes that need only to be saved to the database @@ -997,6 +998,7 @@ class ContactsManager : public Actor { static constexpr int32 USER_FULL_FLAG_CAN_PIN_MESSAGE = 1 << 7; static constexpr int32 USER_FULL_FLAG_HAS_FOLDER_ID = 1 << 11; static constexpr int32 USER_FULL_FLAG_HAS_SCHEDULED_MESSAGES = 1 << 12; + static constexpr int32 USER_FULL_FLAG_HAS_MESSAGE_TTL = 1 << 14; static constexpr int32 CHAT_FLAG_USER_IS_CREATOR = 1 << 0; static constexpr int32 CHAT_FLAG_USER_WAS_KICKED = 1 << 1; @@ -1012,6 +1014,7 @@ class ContactsManager : public Actor { static constexpr int32 CHAT_FULL_FLAG_HAS_SCHEDULED_MESSAGES = 1 << 8; static constexpr int32 CHAT_FULL_FLAG_HAS_FOLDER_ID = 1 << 11; static constexpr int32 CHAT_FULL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 12; + static constexpr int32 CHAT_FULL_FLAG_HAS_MESSAGE_TTL = 1 << 14; static constexpr int32 CHANNEL_FLAG_USER_IS_CREATOR = 1 << 0; static constexpr int32 CHANNEL_FLAG_USER_HAS_LEFT = 1 << 2; @@ -1060,6 +1063,7 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FULL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 21; static constexpr int32 CHANNEL_FULL_FLAG_IS_BLOCKED = 1 << 22; static constexpr int32 CHANNEL_FULL_FLAG_HAS_EXPORTED_INVITE = 1 << 23; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_MESSAGE_TTL = 1 << 24; static constexpr int32 CHAT_INVITE_FLAG_IS_CHANNEL = 1 << 0; static constexpr int32 CHAT_INVITE_FLAG_IS_BROADCAST = 1 << 1; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 823af1424..ddd671017 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -5303,6 +5303,7 @@ void MessagesManager::Dialog::store(StorerT &storer) const { bool has_distance = distance >= 0; bool has_last_yet_unsent_message = last_message_id.is_valid() && last_message_id.is_yet_unsent(); bool has_active_group_call_id = active_group_call_id.is_valid(); + bool has_message_ttl_setting = !message_ttl_setting.is_empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_draft_message); STORE_FLAG(has_last_database_message); @@ -5362,6 +5363,8 @@ void MessagesManager::Dialog::store(StorerT &storer) const { STORE_FLAG(is_group_call_empty); STORE_FLAG(has_active_group_call_id); STORE_FLAG(can_invite_members); + STORE_FLAG(has_message_ttl_setting); + STORE_FLAG(is_message_ttl_setting_inited); END_STORE_FLAGS(); } @@ -5449,6 +5452,9 @@ void MessagesManager::Dialog::store(StorerT &storer) const { if (has_active_group_call_id) { store(active_group_call_id, storer); } + if (has_message_ttl_setting) { + store(message_ttl_setting, storer); + } } // do not forget to resolve dialog dependencies including dependencies of last_message @@ -5479,6 +5485,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) { bool has_pending_read_channel_inbox = false; bool has_distance = false; bool has_active_group_call_id = false; + bool has_message_ttl_setting = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_draft_message); PARSE_FLAG(has_last_database_message); @@ -5538,6 +5545,8 @@ void MessagesManager::Dialog::parse(ParserT &parser) { PARSE_FLAG(is_group_call_empty); PARSE_FLAG(has_active_group_call_id); PARSE_FLAG(can_invite_members); + PARSE_FLAG(has_message_ttl_setting); + PARSE_FLAG(is_message_ttl_setting_inited); END_PARSE_FLAGS(); } else { is_folder_id_inited = false; @@ -5557,6 +5566,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) { has_active_group_call = false; is_group_call_empty = false; can_invite_members = false; + is_message_ttl_setting_inited = false; } parse(last_new_message_id, parser); @@ -5676,6 +5686,9 @@ void MessagesManager::Dialog::parse(ParserT &parser) { if (has_active_group_call_id) { parse(active_group_call_id, parser); } + if (has_message_ttl_setting) { + parse(message_ttl_setting, parser); + } } template @@ -19768,7 +19781,8 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * can_delete_for_all_users, can_report_dialog(d->dialog_id), d->notification_settings.silent_send_message, d->server_unread_count + d->local_unread_count, d->last_read_inbox_message_id.get(), d->last_read_outbox_message_id.get(), d->unread_mention_count, - get_chat_notification_settings_object(&d->notification_settings), get_chat_action_bar_object(d), + get_chat_notification_settings_object(&d->notification_settings), + d->message_ttl_setting.get_message_ttl_setting_object(), get_chat_action_bar_object(d), active_group_call_id.get(), active_group_call_id.is_valid() ? d->is_group_call_empty : true, d->reply_markup_message_id.get(), std::move(draft_message), d->client_data); } @@ -28379,10 +28393,6 @@ void MessagesManager::send_update_chat_action_bar(const Dialog *d) { } void MessagesManager::send_update_chat_voice_chat(const Dialog *d) { - if (td_->auth_manager_->is_bot()) { - return; - } - CHECK(d != nullptr); LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_voice_chat"; on_dialog_updated(d->dialog_id, "send_update_chat_voice_chat"); @@ -28392,6 +28402,15 @@ void MessagesManager::send_update_chat_voice_chat(const Dialog *d) { d->is_group_call_empty)); } +void MessagesManager::send_update_chat_message_ttl_setting(const Dialog *d) { + CHECK(d != nullptr); + LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_message_ttl_setting"; + on_dialog_updated(d->dialog_id, "send_update_chat_message_ttl_setting"); + send_closure(G()->td(), &Td::send_update, + td_api::make_object( + d->dialog_id.get(), d->message_ttl_setting.get_message_ttl_setting_object())); +} + void MessagesManager::send_update_chat_has_scheduled_messages(Dialog *d, bool from_deletion) { if (td_->auth_manager_->is_bot()) { return; @@ -29540,10 +29559,6 @@ void MessagesManager::do_set_dialog_folder_id(Dialog *d, FolderId folder_id) { void MessagesManager::on_update_dialog_group_call(DialogId dialog_id, bool has_active_group_call, bool is_group_call_empty, const char *source) { - if (td_->auth_manager_->is_bot()) { - return; - } - LOG(INFO) << "Update voice chat in " << dialog_id << " with has_active_voice_chat = " << has_active_group_call << " and is_voice_chat_empty = " << is_group_call_empty << " from " << source; @@ -29582,10 +29597,6 @@ void MessagesManager::on_update_dialog_group_call(DialogId dialog_id, bool has_a } void MessagesManager::on_update_dialog_group_call_id(DialogId dialog_id, InputGroupCallId input_group_call_id) { - if (td_->auth_manager_->is_bot()) { - return; - } - auto d = get_dialog_force(dialog_id); if (d == nullptr) { // nothing to do @@ -29607,6 +29618,24 @@ void MessagesManager::on_update_dialog_group_call_id(DialogId dialog_id, InputGr } } +void MessagesManager::on_update_dialog_message_ttl_setting(DialogId dialog_id, MessageTtlSetting message_ttl_setting) { + auto d = get_dialog_force(dialog_id); + if (d == nullptr) { + // nothing to do + return; + } + + if (d->message_ttl_setting != message_ttl_setting) { + d->message_ttl_setting = message_ttl_setting; + d->is_message_ttl_setting_inited = true; + send_update_chat_message_ttl_setting(d); + } + if (!d->is_message_ttl_setting_inited) { + d->is_message_ttl_setting_inited = true; + on_dialog_updated(dialog_id, "on_update_dialog_message_ttl_setting"); + } +} + void MessagesManager::on_update_dialog_filters() { if (td_->auth_manager_->is_bot()) { // just in case @@ -33568,6 +33597,9 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr &&d, do_set_dialog_folder_id( d.get(), td_->contacts_manager_->get_secret_chat_initial_folder_id(dialog_id.get_secret_chat_id())); } + d->message_ttl_setting = + MessageTtlSetting(td_->contacts_manager_->get_secret_chat_ttl(dialog_id.get_secret_chat_id())); + d->is_message_ttl_setting_inited = true; break; case DialogType::None: @@ -33671,6 +33703,10 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab // asynchronously get dialog folder id from the server get_dialog_info_full(dialog_id, Auto()); } + if (!d->is_message_ttl_setting_inited && !td_->auth_manager_->is_bot() && order != DEFAULT_ORDER) { + // asynchronously get dialog message ttl setting from the server + get_dialog_info_full(dialog_id, Auto()); + } if (!d->know_action_bar && !td_->auth_manager_->is_bot() && dialog_type != DialogType::SecretChat && dialog_id != get_my_dialog_id() && have_input_peer(dialog_id, AccessRights::Read)) { // asynchronously get action bar from the server diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index c7c2beba8..472a80f9d 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -32,6 +32,7 @@ #include "td/telegram/MessageReplyInfo.h" #include "td/telegram/MessagesDb.h" #include "td/telegram/MessageSearchFilter.h" +#include "td/telegram/MessageTtlSetting.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/Notification.h" #include "td/telegram/NotificationGroupId.h" @@ -288,6 +289,8 @@ class MessagesManager : public Actor { void on_update_dialog_group_call_id(DialogId dialog_id, InputGroupCallId input_group_call_id); + void on_update_dialog_message_ttl_setting(DialogId dialog_id, MessageTtlSetting message_ttl_setting); + void on_update_dialog_filters(); void on_update_service_notification(tl_object_ptr &&update, @@ -1070,9 +1073,9 @@ class MessagesManager : public Actor { string send_error_message; double try_resend_at = 0; - int32 ttl_period = 0; - int32 ttl = 0; - double ttl_expires_at = 0; + int32 ttl_period = 0; // counted from message send date + int32 ttl = 0; // counted from message content view date + double ttl_expires_at = 0; // only for ttl int64 media_album_id = 0; @@ -1145,6 +1148,7 @@ class MessagesManager : public Actor { MessageId last_pinned_message_id; MessageId reply_markup_message_id; DialogNotificationSettings notification_settings; + MessageTtlSetting message_ttl_setting; unique_ptr draft_message; LogEventIdWithGeneration save_draft_message_log_event_id; LogEventIdWithGeneration save_notification_settings_log_event_id; @@ -1226,6 +1230,7 @@ class MessagesManager : public Actor { bool had_last_yet_unsent_message = false; // whether the dialog was stored to database without last message bool has_active_group_call = false; bool is_group_call_empty = false; + bool is_message_ttl_setting_inited = false; bool increment_view_counter = false; @@ -2216,6 +2221,8 @@ class MessagesManager : public Actor { void send_update_chat_voice_chat(const Dialog *d); + void send_update_chat_message_ttl_setting(const Dialog *d); + void send_update_chat_has_scheduled_messages(Dialog *d, bool from_deletion); void send_update_user_chat_action(DialogId dialog_id, MessageId top_thread_message_id, UserId user_id, diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index a68797ee3..f8fc56630 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -28,6 +28,7 @@ #include "td/telegram/Location.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessagesManager.h" +#include "td/telegram/MessageTtlSetting.h" #include "td/telegram/net/DcOptions.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/NotificationManager.h" @@ -2297,6 +2298,15 @@ void UpdatesManager::on_update(tl_object_ptr u promise.set_value(Unit()); } +void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + MessageTtlSetting message_ttl_setting; + if ((update->flags_ & telegram_api::updatePeerHistoryTTL::TTL_PERIOD_MASK) != 0) { + message_ttl_setting = MessageTtlSetting(update->ttl_period_); + } + td_->messages_manager_->on_update_dialog_message_ttl_setting(DialogId(update->peer_), message_ttl_setting); + promise.set_value(Unit()); +} + void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { td_->contacts_manager_->on_update_peer_located(std::move(update->peers_), true); promise.set_value(Unit()); @@ -2858,8 +2868,4 @@ void UpdatesManager::on_update(tl_object_ptr update, promise.set_value(Unit()); } -void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - promise.set_value(Unit()); -} - } // namespace td diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 0ee5d820f..079b2bb62 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -337,6 +337,8 @@ class UpdatesManager : public Actor { void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); @@ -459,8 +461,6 @@ class UpdatesManager : public Actor { void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); - - void on_update(tl_object_ptr update, Promise &&promise); }; } // namespace td From 54b7b0f6e30b8a87b1d2aa5a31f669e81f533ec1 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 9 Feb 2021 17:51:20 +0300 Subject: [PATCH 166/232] Return message ttl for ordinary self-destructing messages. --- td/telegram/MessagesManager.cpp | 14 +++++++++----- td/telegram/MessagesManager.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index ddd671017..16d24f309 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -11943,7 +11943,7 @@ void MessagesManager::ttl_register_message(DialogId dialog_id, const Message *m, ttl_update_timeout(now); } -void MessagesManager::ttl_period_register_message(DialogId dialog_id, const Message *m, int32 unix_time) { +void MessagesManager::ttl_period_register_message(DialogId dialog_id, const Message *m, double server_time) { CHECK(m != nullptr); CHECK(m->ttl_period != 0); CHECK(!m->message_id.is_scheduled()); @@ -11953,7 +11953,7 @@ void MessagesManager::ttl_period_register_message(DialogId dialog_id, const Mess auto it = it_flag.first; auto now = Time::now(); - ttl_heap_.insert(now + (m->date + m->ttl_period - unix_time), it->as_heap_node()); + ttl_heap_.insert(now + (m->date + m->ttl_period - server_time), it->as_heap_node()); ttl_update_timeout(now); } @@ -22613,6 +22613,10 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial } else { ttl_expires_in = m->ttl; } + if (ttl == 0 && m->ttl_period != 0) { + ttl = m->ttl_period; + ttl_expires_in = max(m->date + m->ttl_period - G()->server_time(), 1e-3); + } } else { ttl = 0; } @@ -31996,15 +32000,15 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } if (message->ttl_period > 0) { CHECK(dialog_id.get_type() != DialogType::SecretChat); - auto unix_time = G()->unix_time(); - if (message->date + message->ttl_period <= unix_time) { + auto server_time = G()->server_time(); + if (message->date + message->ttl_period <= server_time) { LOG(INFO) << "Can't add " << message_id << " with expired TTL period to " << dialog_id << " from " << source; delete_message_from_database(d, message_id, message.get(), true); debug_add_message_to_dialog_fail_reason_ = "delete expired by TTL period message"; d->being_added_message_id = MessageId(); return nullptr; } else { - ttl_period_register_message(dialog_id, message.get(), unix_time); + ttl_period_register_message(dialog_id, message.get(), server_time); } } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 472a80f9d..647dd346b 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -2600,7 +2600,7 @@ class MessagesManager : public Actor { bool ttl_on_open(Dialog *d, Message *m, double now, bool is_local_read); void ttl_register_message(DialogId dialog_id, const Message *m, double now); void ttl_unregister_message(DialogId dialog_id, const Message *m, const char *source); - void ttl_period_register_message(DialogId dialog_id, const Message *m, int32 unix_time); + void ttl_period_register_message(DialogId dialog_id, const Message *m, double server_time); void ttl_period_unregister_message(DialogId dialog_id, const Message *m); void ttl_loop(double now); void ttl_update_timeout(double now); From 4423ccbde27f212ba75ea839c60c1bde511545a2 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 9 Feb 2021 17:59:57 +0300 Subject: [PATCH 167/232] Add invite_link_changes filter for chat event log. --- td/generate/scheme/td_api.tl | 3 ++- td/generate/scheme/td_api.tlo | Bin 196712 -> 196756 bytes td/telegram/MessagesManager.cpp | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index d02034e30..870f81bfd 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2468,8 +2468,9 @@ chatEvents events:vector = ChatEvents; //@member_restrictions True, if member restricted/unrestricted/banned/unbanned events should be returned //@info_changes True, if changes in chat information should be returned //@setting_changes True, if changes in chat settings should be returned +//@invite_link_changes True, if changes to invite links should be returned //@voice_chat_changes True, if voice chat actions should be returned -chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool member_joins:Bool member_leaves:Bool member_invites:Bool member_promotions:Bool member_restrictions:Bool info_changes:Bool setting_changes:Bool voice_chat_changes:Bool = ChatEventLogFilters; +chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool member_joins:Bool member_leaves:Bool member_invites:Bool member_promotions:Bool member_restrictions:Bool info_changes:Bool setting_changes:Bool invite_link_changes:Bool voice_chat_changes:Bool = ChatEventLogFilters; //@class LanguagePackStringValue @description Represents the value of a string in a language pack diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 89dd8c953ca1472f4d2e1047e3da3f6fbb207179..520f65093ef4faa8683cab2a45f08a0a6d819718 100644 GIT binary patch delta 110 zcmaFSz%!+hXM+wq%j|4n)6E9#EqRI{mT+=LVu@>6YF>#?e!5#`PDyG}F-ZL8F(DtG z%~wkoGYSf4=9OiZq{ioD=4HnNmFG?WsKg{axuV=Vx|oRZX{VvzXFV?sXM so3EBGW}LjF+yJbp`9}Hn8|92W-#I}F7#V MessagesManager::get_ch if (filters->setting_changes_) { flags |= telegram_api::channelAdminLogEventsFilter::SETTINGS_MASK; } - if (filters->voice_chat_changes_) { + if (filters->invite_link_changes_) { flags |= telegram_api::channelAdminLogEventsFilter::GROUP_CALL_MASK; } + if (filters->voice_chat_changes_) { + flags |= telegram_api::channelAdminLogEventsFilter::INVITES_MASK; + } return make_tl_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, From 5ac0a9bd8ae5be480ee990a9b5f90c5af766814a Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 9 Feb 2021 18:10:20 +0300 Subject: [PATCH 168/232] Add chatEventMessageTtlSettingChanged. --- td/generate/scheme/td_api.tl | 3 +++ td/generate/scheme/td_api.tlo | Bin 196756 -> 196928 bytes td/telegram/MessagesManager.cpp | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 870f81bfd..14e5be2b5 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2418,6 +2418,9 @@ chatEventLinkedChatChanged old_linked_chat_id:int53 new_linked_chat_id:int53 = C //@description The slow_mode_delay setting of a supergroup was changed @old_slow_mode_delay Previous value of slow_mode_delay @new_slow_mode_delay New value of slow_mode_delay chatEventSlowModeDelayChanged old_slow_mode_delay:int32 new_slow_mode_delay:int32 = ChatEventAction; +//@description The message TTL setting was changed @old_message_ttl_setting Previous value of message_ttl_setting @new_message_ttl_setting New value of message_ttl_setting +chatEventMessageTtlSettingChanged old_message_ttl_setting:int32 new_message_ttl_setting:int32 = ChatEventAction; + //@description The sign_messages setting of a channel was toggled @sign_messages New value of sign_messages chatEventSignMessagesToggled sign_messages:Bool = ChatEventAction; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 520f65093ef4faa8683cab2a45f08a0a6d819718..ffac0e17e5bb0038f0d1db437f74fd38debe6e5b 100644 GIT binary patch delta 747 zcmbQz$aA2HX92tD{dXq2_?oKtSNC99PV$8o3RM$*|b+O)V}?Oiv9d$q7y^Dap)B z2gYMwdTI&-11vC^fcm7L-PaV)&q;}&yfRjsRXi`X9K=$ee87qWVxvX@)W(! z0;sD_6hK{NQV11EDTMm>L?Ki$OOXpimroH?O-&J0%>$SSJnB4(p=#hF8(<<2ilGL| zlt9(MMLJ5L!8M^?XY!X4Xdo(-I&i`~$~gJ#LF35|ZV)duuPAL_QOdY|MJdypE&!B8 BTD$-N delta 672 zcmX@m#51LlX92rt=_D;Ke&>wD64$cSyb{OclFa-(xycim#5e1(x9Bn!ZQf`iZ^Qyp zHTg=K4TK?*9syw#q-#JJ2QmyGj0+ed1{vsTCS>TK>H3nPApx@D<}o231|TSMX4B!Y zZ953I)+EyetbF=`rA!Kwdoo2JnwMm%;MVgX6JqH!KPH{&4~{X)On-2Ti36fBAq(mt zhg6-(JF+O%7CDPEG!orDhdf{Hu+qKpBM598%UEQpUJFq>SlJ7XWrT;i3Ql diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 6e13eb64b..51d05f815 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -31335,8 +31335,13 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob auto action = move_tl_object_as(action_ptr); return make_tl_object(action->join_muted_); } - case telegram_api::channelAdminLogEventActionChangeHistoryTTL::ID: - return nullptr; + case telegram_api::channelAdminLogEventActionChangeHistoryTTL::ID: { + auto action = move_tl_object_as(action_ptr); + auto old_value = MessageTtlSetting(clamp(action->prev_value_, 0, 86400 * 366)); + auto new_value = MessageTtlSetting(clamp(action->new_value_, 0, 86400 * 366)); + return make_tl_object(old_value.get_message_ttl_setting_object(), + new_value.get_message_ttl_setting_object()); + } default: UNREACHABLE(); return nullptr; From d6da217b9a4d166b159741b9cdfaf1612eff4458 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 9 Feb 2021 18:51:08 +0300 Subject: [PATCH 169/232] Allow changing message TTL in non-secret chats. --- td/generate/scheme/td_api.tl | 5 ++-- td/generate/scheme/td_api.tlo | Bin 196928 -> 196936 bytes td/telegram/MessagesManager.cpp | 51 +++++++++++++++++++++++++++++--- td/telegram/MessagesManager.h | 2 +- td/telegram/Td.cpp | 5 ++-- td/telegram/Td.h | 2 +- td/telegram/cli.cpp | 4 +-- 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 14e5be2b5..b28106040 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4270,8 +4270,9 @@ setChatTitle chat_id:int53 title:string = Ok; //@chat_id Chat identifier @photo New chat photo. Pass null to delete the chat photo setChatPhoto chat_id:int53 photo:InputChatPhoto = Ok; -//@description Changes the message TTL setting (sets a new self-destruct timer) in a chat. Requires can_delete_messages administrator right in basic groups, supergroups and channels @chat_id Chat identifier @ttl New TTL value, in seconds -setChatMessageTtl chat_id:int53 ttl:int32 = Ok; +//@description Changes the message TTL setting (sets a new self-destruct timer) in a chat. Requires can_delete_messages administrator right in basic groups, supergroups and channels +//@chat_id Chat identifier @ttl New TTL value, in seconds; must be one of 0, 86400, 604800 unless chat is secret +setChatMessageTtlSetting chat_id:int53 ttl:int32 = Ok; //@description Changes the chat members permissions. Supported only for basic groups and supergroups. Requires can_restrict_members administrator right //@chat_id Chat identifier @permissions New non-administrator members permissions in the chat diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index ffac0e17e5bb0038f0d1db437f74fd38debe6e5b..61114bfe77d4c3d9fc9c5e16fad89a5592458ea5 100644 GIT binary patch delta 53 zcmX@m#B-vFr=f*$3)7EVvfn>l6Ot%SEpg6BEb&b(E>28O4JpY9PAw_P%uAo%_=m}O Jxx;NHH2{WQ7Y+ab delta 53 zcmX@n#B-pDr=f*$3)7EV;+1)?7YY`qmN;i5miVR?7bm8thLq$mO#dImWH>!wC6mPV JgxgFnQ~_<67FYlP diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 51d05f815..9aacd2d14 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1329,6 +1329,47 @@ class EditDialogTitleQuery : public Td::ResultHandler { } }; +class SetHistoryTtlQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit SetHistoryTtlQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, int32 period) { + dialog_id_ = dialog_id; + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + CHECK(input_peer != nullptr); + + send_query(G()->net_query_creator().create(telegram_api::messages_setHistoryTTL(std::move(input_peer), period))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for SetHistoryTtlQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); + } + + void on_error(uint64 id, Status status) override { + if (status.message() == "CHAT_NOT_MODIFIED") { + if (!td->auth_manager_->is_bot()) { + promise_.set_value(Unit()); + return; + } + } else { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetHistoryTtlQuery"); + } + promise_.set_error(std::move(status)); + } +}; + class EditChatDefaultBannedRightsQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -30685,7 +30726,7 @@ void MessagesManager::set_dialog_title(DialogId dialog_id, const string &title, td_->create_handler(std::move(promise))->send(dialog_id, new_title); } -void MessagesManager::set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Promise &&promise) { +void MessagesManager::set_dialog_message_ttl_setting(DialogId dialog_id, int32 ttl, Promise &&promise) { if (ttl < 0) { return promise.set_error(Status::Error(400, "Message TTL can't be negative")); } @@ -30694,11 +30735,12 @@ void MessagesManager::set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Prom if (d == nullptr) { return promise.set_error(Status::Error(400, "Chat not found")); } + if (!have_input_peer(dialog_id, AccessRights::Write)) { + return promise.set_error(Status::Error(400, "Have no write access to the chat")); + } LOG(INFO) << "Begin to set message TTL in " << dialog_id << " to " << ttl; - TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); - switch (dialog_id.get_type()) { case DialogType::User: break; @@ -30725,7 +30767,8 @@ void MessagesManager::set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Prom } if (dialog_id.get_type() != DialogType::SecretChat) { - promise.set_error(Status::Error(5, "Can't set message TTL in non-secret chat")); + // TODO invoke after + td_->create_handler(std::move(promise))->send(dialog_id, ttl); } else { bool need_update_dialog_pos = false; Message *m = get_message_to_send(d, MessageId(), MessageId(), MessageSendOptions(), diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 647dd346b..7cbb41587 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -388,7 +388,7 @@ class MessagesManager : public Actor { Result> resend_messages(DialogId dialog_id, vector message_ids) TD_WARN_UNUSED_RESULT; - void set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Promise &&promise); + void set_dialog_message_ttl_setting(DialogId dialog_id, int32 ttl, Promise &&promise); Status send_screenshot_taken_notification_message(DialogId dialog_id); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 2f99ccfcd..d308f9036 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6113,10 +6113,9 @@ void Td::on_request(uint64 id, const td_api::setChatPhoto &request) { messages_manager_->set_dialog_photo(DialogId(request.chat_id_), request.photo_, std::move(promise)); } -void Td::on_request(uint64 id, const td_api::setChatMessageTtl &request) { - DialogId dialog_id(request.chat_id_); +void Td::on_request(uint64 id, const td_api::setChatMessageTtlSetting &request) { CREATE_OK_REQUEST_PROMISE(); - messages_manager_->set_dialog_message_ttl(dialog_id, request.ttl_, std::move(promise)); + messages_manager_->set_dialog_message_ttl_setting(DialogId(request.chat_id_), request.ttl_, std::move(promise)); } void Td::on_request(uint64 id, const td_api::setChatPermissions &request) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 7a66b29dd..625286588 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -740,7 +740,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::setChatPhoto &request); - void on_request(uint64 id, const td_api::setChatMessageTtl &request); + void on_request(uint64 id, const td_api::setChatMessageTtlSetting &request); void on_request(uint64 id, const td_api::setChatPermissions &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 6a496b1f2..8c2cd8a97 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3535,11 +3535,11 @@ class CliClient final : public Actor { send_request(td_api::make_object( as_chat_id(chat_id), td_api::make_object(as_input_file(animation), to_double(main_frame_timestamp)))); - } else if (op == "scmt") { + } else if (op == "scmts") { string chat_id; int32 ttl; get_args(args, chat_id, ttl); - send_request(td_api::make_object(as_chat_id(chat_id), ttl)); + send_request(td_api::make_object(as_chat_id(chat_id), ttl)); } else if (op == "scperm") { string chat_id; string permissions; From c1a14d887baaa41beca7fd2606a5a086630064b9 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 10 Feb 2021 14:36:33 +0300 Subject: [PATCH 170/232] Don't send updateDeleteMessage twice for the same message. --- td/telegram/MessagesManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 9aacd2d14..f668f068b 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -9980,6 +9980,7 @@ void MessagesManager::delete_dialog_messages(DialogId dialog_id, const vector deleted_message_ids; bool need_update_dialog_pos = false; for (auto message_id : message_ids) { + CHECK(!message_id.is_scheduled()); if (from_updates) { if (!message_id.is_valid() || (!message_id.is_server() && dialog_id.get_type() != DialogType::SecretChat)) { LOG(ERROR) << "Incoming update tries to delete " << message_id; @@ -9989,9 +9990,10 @@ void MessagesManager::delete_dialog_messages(DialogId dialog_id, const vectordeleted_message_ids.count(message_id) != 0; auto message = delete_message(d, message_id, true, &need_update_dialog_pos, "delete_dialog_messages"); if (message == nullptr) { - if (!skip_update_for_not_found_messages) { + if (!skip_update_for_not_found_messages && !was_already_deleted) { deleted_message_ids.push_back(message_id.get()); } } else { From 65d422935243db69aad42eb8ce3e1bb873a140ce Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 12 Feb 2021 20:14:59 +0300 Subject: [PATCH 171/232] Allow to specify report text for all report chat reasons. --- td/generate/scheme/td_api.tl | 9 +++++---- td/generate/scheme/td_api.tlo | Bin 196936 -> 196936 bytes td/telegram/MessagesManager.cpp | 20 +++++++------------- td/telegram/MessagesManager.h | 2 +- td/telegram/Td.cpp | 4 +++- td/telegram/cli.cpp | 7 ++++--- 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b28106040..cf814babf 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2932,8 +2932,8 @@ chatReportReasonUnrelatedLocation = ChatReportReason; //@description The chat represents a fake account chatReportReasonFake = ChatReportReason; -//@description A custom reason provided by the user @text Report text -chatReportReasonCustom text:string = ChatReportReason; +//@description A custom reason provided by the user +chatReportReasonCustom = ChatReportReason; //@description Contains an HTTPS link to a message in a supergroup or channel @link Message link @is_public True, if the link will work for non-members of the chat @@ -4914,8 +4914,9 @@ deleteAccount reason:string = Ok; //@description Removes a chat action bar without any other action @chat_id Chat identifier removeChatActionBar chat_id:int53 = Ok; -//@description Reports a chat to the Telegram moderators. A chat can be reported only from the chat action bar, or if this is a private chat with a bot, a private chat with a user sharing their location, a supergroup, or a channel, since other chats can't be checked by moderators @chat_id Chat identifier @reason The reason for reporting the chat @message_ids Identifiers of reported messages, if any -reportChat chat_id:int53 reason:ChatReportReason message_ids:vector = Ok; +//@description Reports a chat to the Telegram moderators. A chat can be reported only from the chat action bar, or if this is a private chat with a bot, a private chat with a user sharing their location, a supergroup, or a channel, since other chats can't be checked by moderators +//@chat_id Chat identifier @reason The reason for reporting the chat @message_ids Identifiers of reported messages, if any @text Additional report details; 0-1024 characters +reportChat chat_id:int53 reason:ChatReportReason message_ids:vector text:string = Ok; //@description Returns an HTTP URL with the chat statistics. Currently this method of getting the statistics are disabled and can be deleted in the future @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link @is_dark Pass true if a URL with the dark theme must be returned diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 61114bfe77d4c3d9fc9c5e16fad89a5592458ea5..31da88b2ee96cdda51a579dd32723e49e7a4bab3 100644 GIT binary patch delta 328 zcmX@n#B-vFXG0G+OS<-%(9ILLYuY71teeM#d>DYB$eB%t!?x`pNNlpoWF6d+8TcgE z;FJ7-Pts(H32r+Zrf6v3axKX9AaGTVD@LsNvd`oTQzUSj)T}VIU12KYc7>@-0!sY0 z%N4hC6{QyB7nL|?B$iCyznaN#dck%k0alP@4AT>TF*;B7*)K5N!IVj5` &&report_reason, - const vector &message_ids) { + const vector &message_ids, const string &message) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); @@ -4397,11 +4397,11 @@ class ReportPeerQuery : public Td::ResultHandler { if (message_ids.empty()) { send_query(G()->net_query_creator().create( - telegram_api::account_reportPeer(std::move(input_peer), std::move(report_reason), string()))); + telegram_api::account_reportPeer(std::move(input_peer), std::move(report_reason), message))); } else { send_query(G()->net_query_creator().create( telegram_api::messages_report(std::move(input_peer), MessagesManager::get_server_message_ids(message_ids), - std::move(report_reason), string()))); + std::move(report_reason), message))); } } @@ -8169,7 +8169,8 @@ bool MessagesManager::can_report_dialog(DialogId dialog_id) const { } void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr &reason, - const vector &message_ids, Promise &&promise) { + const vector &message_ids, const string &message, + Promise &&promise) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { return promise.set_error(Status::Error(3, "Chat not found")); @@ -8251,23 +8252,16 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr(reason.get()); - auto text = other_reason->text_; - if (!clean_input_string(text)) { - return promise.set_error(Status::Error(400, "Text must be encoded in UTF-8")); - } - + case td_api::chatReportReasonCustom::ID: report_reason = make_tl_object(); break; - } default: UNREACHABLE(); } CHECK(report_reason != nullptr); td_->create_handler(std::move(promise)) - ->send(dialog_id, std::move(report_reason), server_message_ids); + ->send(dialog_id, std::move(report_reason), server_message_ids, message); } void MessagesManager::on_get_peer_settings(DialogId dialog_id, diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 7cbb41587..03ff3cc36 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -776,7 +776,7 @@ class MessagesManager : public Actor { void reget_dialog_action_bar(DialogId dialog_id, const char *source); void report_dialog(DialogId dialog_id, const tl_object_ptr &reason, - const vector &message_ids, Promise &&promise); + const vector &message_ids, const string &message, Promise &&promise); void on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings, bool ignore_privacy_exception = false); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index d308f9036..2920a1869 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6965,9 +6965,11 @@ void Td::on_request(uint64 id, const td_api::removeChatActionBar &request) { void Td::on_request(uint64 id, td_api::reportChat &request) { CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.text_); CREATE_OK_REQUEST_PROMISE(); messages_manager_->report_dialog(DialogId(request.chat_id_), request.reason_, - MessagesManager::get_message_ids(request.message_ids_), std::move(promise)); + MessagesManager::get_message_ids(request.message_ids_), request.text_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatStatisticsUrl &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 8c2cd8a97..02f09b55b 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1338,7 +1338,7 @@ class CliClient final : public Actor { if (reason == "fake") { return td_api::make_object(); } - return td_api::make_object(reason.str()); + return td_api::make_object(); } static td_api::object_ptr get_network_type(MutableSlice type) { @@ -3905,9 +3905,10 @@ class CliClient final : public Actor { string chat_id; string reason; string message_ids; - get_args(args, chat_id, reason, message_ids); + string text; + get_args(args, chat_id, reason, message_ids, text); send_request(td_api::make_object(as_chat_id(chat_id), get_chat_report_reason(reason), - as_message_ids(message_ids))); + as_message_ids(message_ids), text)); } else if (op == "gcsu") { string chat_id; string parameters; From f92339de854dc0e7d58e3c59c14b35de5b34ff5e Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 16 Feb 2021 15:15:45 +0300 Subject: [PATCH 172/232] Support telegram_api::messageActionSetMessagesTTL. --- td/generate/scheme/td_api.tl | 2 +- td/telegram/MessageContent.cpp | 12 ++++++++++++ td/telegram/UpdatesManager.cpp | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index cf814babf..ea05f3a86 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1708,7 +1708,7 @@ messagePinMessage message_id:int53 = MessageContent; //@description A screenshot of a message in the chat has been taken messageScreenshotTaken = MessageContent; -//@description The TTL (Time To Live) setting messages in a secret chat has been changed @ttl New TTL +//@description The TTL (Time To Live) setting for messages in the chat has been changed @ttl New message TTL setting messageChatSetTtl ttl:int32 = MessageContent; //@description A non-standard action has happened in the chat @text Message text to be shown in the chat diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index f01027987..49fbf5655 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -4525,6 +4525,10 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptrflags_ & telegram_api::messageActionPhoneCall::DURATION_MASK) != 0 ? phone_call->duration_ : 0; auto is_video = (phone_call->flags_ & telegram_api::messageActionPhoneCall::VIDEO_MASK) != 0; + if (duration < 0) { + LOG(ERROR) << "Receive invalid " << oneline(to_string(phone_call)); + break; + } return make_unique(phone_call->call_id_, duration, get_call_discard_reason(phone_call->reason_), is_video); } @@ -4622,6 +4626,14 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(InputGroupCallId(invite_to_group_call->call_), std::move(user_ids)); } + case telegram_api::messageActionSetMessagesTTL::ID: { + auto set_messages_ttl = move_tl_object_as(action); + if (set_messages_ttl->period_ < 0) { + LOG(ERROR) << "Receive wrong ttl = " << set_messages_ttl->period_; + break; + } + return td::make_unique(set_messages_ttl->period_); + } default: UNREACHABLE(); } diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index f8fc56630..c3b0382a7 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -608,6 +608,7 @@ bool UpdatesManager::is_acceptable_message(const telegram_api::Message *message_ case telegram_api::messageActionSecureValuesSentMe::ID: case telegram_api::messageActionContactSignUp::ID: case telegram_api::messageActionGroupCall::ID: + case telegram_api::messageActionSetMessagesTTL::ID: break; case telegram_api::messageActionChatCreate::ID: { auto chat_create = static_cast(action); From 79d29295845f791116a6c4d41ca83e4aad919c4f Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 16 Feb 2021 15:26:52 +0300 Subject: [PATCH 173/232] support ttl_period for service messages. --- td/telegram/MessagesManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 83a306af5..8727b4ce1 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -13164,6 +13164,9 @@ MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( message_info.sender_dialog_id = message_info.dialog_id; } message_info.date = message->date_; + if (message->flags_ & MESSAGE_FLAG_HAS_TTL_PERIOD) { + message_info.ttl_period = message->ttl_period_; + } message_info.flags = message->flags_; auto reply_to_message_id = MessageId(ServerMessageId(message->reply_to_ == nullptr ? 0 : message->reply_to_->reply_to_msg_id_)); From 12ead50446e9df6234b4b93ecb8ff6fd4c2374f7 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 16 Feb 2021 15:31:58 +0300 Subject: [PATCH 174/232] Add number of revoked invite link to chatInviteLinkCount. --- td/generate/scheme/td_api.tl | 9 ++++++--- td/generate/scheme/td_api.tlo | Bin 196936 -> 196988 bytes td/telegram/ContactsManager.cpp | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index ea05f3a86..b9d02e77f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -559,8 +559,11 @@ chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_da //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; -//@description Describes a chat administrator with a number of active chat invite links @user_id Administrator's user identifier @invite_link_count Number of active invite links -chatInviteLinkCount user_id:int32 invite_link_count:int32 = ChatInviteLinkCount; +//@description Describes a chat administrator with a number of active and revoked chat invite links +//@user_id Administrator's user identifier +//@invite_link_count Number of active invite links +//@revoked_invite_link_count Number of revoked invite links +chatInviteLinkCount user_id:int32 invite_link_count:int32 revoked_invite_link_count:int32 = ChatInviteLinkCount; //@description Contains a list of chat invite link counts @invite_link_counts List of invite linkcounts chatInviteLinkCounts invite_link_counts:vector = ChatInviteLinkCounts; @@ -4461,7 +4464,7 @@ createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatIn //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; -//@description Returns list of chat administrators with number of active invite links. Requires owner privileges in the chat @chat_id Chat identifier +//@description Returns list of chat administrators with number of their invite links. Requires owner privileges in the chat @chat_id Chat identifier getChatInviteLinkCounts chat_id:int53 = ChatInviteLinkCounts; //@description Returns invite links for a chat created by specified administrator. Requires administrator privileges and can_invite_users right in the chat to get own links and owner privileges to get other links diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 31da88b2ee96cdda51a579dd32723e49e7a4bab3..4aaf30278761cdd4443886341e08a9b99b9fc81b 100644 GIT binary patch delta 101 zcmX@n#Pg?#X9Eid%gzP12R3tXEGU%$v4oQ|5=%Vu$}&q*eKPa1o%2icNMYzb?p=C7`IQTW7^RK0DAZ( - td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkCount"), admin->invites_count_)); + td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkCount"), admin->invites_count_, + admin->revoked_invites_count_)); } promise_.set_value(td_api::make_object(std::move(invite_link_counts))); } From 5955b43d88ef8d5b85d8d01d569818dbb49f2ee8 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 16 Feb 2021 18:27:17 +0300 Subject: [PATCH 175/232] Add supergroup.is_broadcast_group flag. --- td/generate/scheme/td_api.tl | 3 ++- td/generate/scheme/td_api.tlo | Bin 196988 -> 197032 bytes td/telegram/ContactsManager.cpp | 34 ++++++++++++++++++++++++++------ td/telegram/ContactsManager.h | 2 ++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b9d02e77f..2e9eae12d 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -614,11 +614,12 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 memb //@sign_messages True, if messages sent to the channel should contain information about the sender. This field is only applicable to channels //@is_slow_mode_enabled True, if the slow mode is enabled in the supergroup //@is_channel True, if the supergroup is a channel +//@is_broadcast_group True, if the supergroup is a broadcast group, i.e. only administrators can send messages and there is no limit on number of members //@is_verified True, if the supergroup or channel is verified //@restriction_reason If non-empty, contains a human-readable description of the reason why access to this supergroup or channel must be restricted //@is_scam True, if many users reported this supergroup or channel as a scam //@is_fake True, if many users reported this supergroup or channel as a fake account -supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup; +supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_broadcast_group:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup; //@description Contains full information about a supergroup or channel //@photo Chat photo; may be null diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 4aaf30278761cdd4443886341e08a9b99b9fc81b..981fc6f522ff0f24409757050b880eb4e2c5acf6 100644 GIT binary patch delta 132 zcmey<#IvHAXG4K5OMAPMP;-gy@)BLfnUWw5S8-`UYEgPoerW*%h=22#kPrW4M-S!g z8;&tXFbWH07RM(QLU&asf T!E)0Nm@`^HR4tjybR!u67_Kun delta 116 zcmZ3{%=4#-XG4K5i&@y6SR Dp>Z#2 diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 84c248245..969a47935 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3942,6 +3942,7 @@ void ContactsManager::Channel::store(StorerT &storer) const { STORE_FLAG(has_restriction_reasons); STORE_FLAG(legacy_has_active_group_call); STORE_FLAG(is_fake); + STORE_FLAG(is_gigagroup); END_STORE_FLAGS(); store(status, storer); @@ -4010,6 +4011,7 @@ void ContactsManager::Channel::parse(ParserT &parser) { PARSE_FLAG(has_restriction_reasons); PARSE_FLAG(legacy_has_active_group_call); PARSE_FLAG(is_fake); + PARSE_FLAG(is_gigagroup); END_PARSE_FLAGS(); if (use_new_rights) { @@ -14745,6 +14747,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char auto restriction_reasons = get_restriction_reasons(std::move(channel.restriction_reason_)); bool is_scam = (channel.flags_ & CHANNEL_FLAG_IS_SCAM) != 0; bool is_fake = (channel.flags_ & CHANNEL_FLAG_IS_FAKE) != 0; + bool is_gigagroup = (channel.flags_ & CHANNEL_FLAG_IS_GIGAGROUP) != 0; bool have_participant_count = (channel.flags_ & CHANNEL_FLAG_HAS_PARTICIPANT_COUNT) != 0; int32 participant_count = have_participant_count ? channel.participants_count_ : 0; @@ -14767,7 +14770,9 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char sign_messages = true; } else { LOG_IF(ERROR, is_slow_mode_enabled) << "Slow mode enabled in the " << channel_id << " from " << source; + LOG_IF(ERROR, is_gigagroup) << "Receive broadcast group as channel " << channel_id << " from " << source; is_slow_mode_enabled = false; + is_gigagroup = false; } DialogParticipantStatus status = [&] { @@ -14800,13 +14805,28 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char on_update_channel_default_permissions(c, channel_id, get_restricted_rights(std::move(channel.default_banned_rights_))); - if (c->is_megagroup != is_megagroup || c->is_verified != is_verified) { + if (c->has_linked_channel != has_linked_channel || c->has_location != has_location || + c->is_slow_mode_enabled != is_slow_mode_enabled || c->is_megagroup != is_megagroup || + c->restriction_reasons != restriction_reasons || c->is_scam != is_scam || c->is_fake != is_fake || + c->is_gigagroup != is_gigagroup) { + c->has_linked_channel = has_linked_channel; + c->has_location = has_location; + c->is_slow_mode_enabled = is_slow_mode_enabled; c->is_megagroup = is_megagroup; - c->is_verified = is_verified; + c->restriction_reasons = std::move(restriction_reasons); + c->is_scam = is_scam; + c->is_fake = is_fake; + c->is_gigagroup = is_gigagroup; c->is_changed = true; invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled); } + if (c->is_verified != is_verified || c->sign_messages != sign_messages) { + c->is_verified = is_verified; + c->sign_messages = sign_messages; + + c->is_changed = true; + } update_channel(c, channel_id); } else { @@ -14852,7 +14872,8 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char bool need_invalidate_channel_full = false; if (c->has_linked_channel != has_linked_channel || c->has_location != has_location || c->is_slow_mode_enabled != is_slow_mode_enabled || c->is_megagroup != is_megagroup || - c->restriction_reasons != restriction_reasons || c->is_scam != is_scam || c->is_fake != is_fake) { + c->restriction_reasons != restriction_reasons || c->is_scam != is_scam || c->is_fake != is_fake || + c->is_gigagroup != is_gigagroup) { c->has_linked_channel = has_linked_channel; c->has_location = has_location; c->is_slow_mode_enabled = is_slow_mode_enabled; @@ -14860,6 +14881,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char c->restriction_reasons = std::move(restriction_reasons); c->is_scam = is_scam; c->is_fake = is_fake; + c->is_gigagroup = is_gigagroup; c->is_changed = true; need_invalidate_channel_full = true; @@ -15203,7 +15225,7 @@ td_api::object_ptr ContactsManager::get_update_unknown ChannelId channel_id) { return td_api::make_object(td_api::make_object( channel_id.get(), string(), 0, DialogParticipantStatus::Banned(0).get_chat_member_status_object(), 0, false, - false, false, false, true, false, "", false, false)); + false, false, false, true, false, false, string(), false, false)); } int32 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char *source) const { @@ -15226,8 +15248,8 @@ tl_object_ptr ContactsManager::get_supergroup_object(Channel return td_api::make_object( channel_id.get(), c->username, c->date, get_channel_status(c).get_chat_member_status_object(), c->participant_count, c->has_linked_channel, c->has_location, c->sign_messages, c->is_slow_mode_enabled, - !c->is_megagroup, c->is_verified, get_restriction_reason_description(c->restriction_reasons), c->is_scam, - c->is_fake); + !c->is_megagroup, c->is_gigagroup, c->is_verified, get_restriction_reason_description(c->restriction_reasons), + c->is_scam, c->is_fake); } tl_object_ptr ContactsManager::get_supergroup_full_info_object(ChannelId channel_id) const { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 8998baed2..05b87c1c9 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -779,6 +779,7 @@ class ContactsManager : public Actor { bool is_slow_mode_enabled = false; bool is_megagroup = false; + bool is_gigagroup = false; bool is_verified = false; bool is_scam = false; bool is_fake = false; @@ -1038,6 +1039,7 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 23; static constexpr int32 CHANNEL_FLAG_IS_GROUP_CALL_NON_EMPTY = 1 << 24; static constexpr int32 CHANNEL_FLAG_IS_FAKE = 1 << 25; + static constexpr int32 CHANNEL_FLAG_IS_GIGAGROUP = 1 << 26; static constexpr int32 CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 0; static constexpr int32 CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT = 1 << 1; From 498476a95d3737e3c20df8f14196ee07dc0d4a30 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 16 Feb 2021 18:59:13 +0300 Subject: [PATCH 176/232] Update documentation. --- td/generate/scheme/td_api.tl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 2e9eae12d..aa77b88ec 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4455,14 +4455,14 @@ replacePermanentChatInviteLink chat_id:int53 = ChatInviteLink; //@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never -//@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited +//@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-99999; pass 0 if not limited createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatInviteLink; //@description Edits a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links //@chat_id Chat identifier //@invite_link Invite link to be edited //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never -//@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-100000; pass 0 if not limited +//@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-99999; pass 0 if not limited editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; //@description Returns list of chat administrators with number of their invite links. Requires owner privileges in the chat @chat_id Chat identifier From 9b62afbea277212db3e169a6f462e93d3ffa8429 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 17 Feb 2021 01:06:35 +0300 Subject: [PATCH 177/232] Add td_api::toggleSupergroupIsBroadcastGroup. --- td/generate/scheme/td_api.tl | 3 ++ td/generate/scheme/td_api.tlo | Bin 197032 -> 197148 bytes td/telegram/ContactsManager.cpp | 54 ++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 2 ++ td/telegram/Td.cpp | 6 ++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 4 +++ 7 files changed, 71 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index aa77b88ec..9dfec93d9 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4779,6 +4779,9 @@ toggleSupergroupSignMessages supergroup_id:int32 sign_messages:Bool = Ok; //@description Toggles whether the message history of a supergroup is available to new members; requires can_change_info administrator right @supergroup_id The identifier of the supergroup @is_all_history_available The new value of is_all_history_available toggleSupergroupIsAllHistoryAvailable supergroup_id:int32 is_all_history_available:Bool = Ok; +//@description Upgrades supergroup to a broadcast group; requires owner privileges in the supergroup @supergroup_id Identifier of the supergroup +toggleSupergroupIsBroadcastGroup supergroup_id:int32 = Ok; + //@description Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup @supergroup_id Supergroup identifier @user_id User identifier @message_ids Identifiers of messages sent in the supergroup by the user. This list must be non-empty reportSupergroupSpam supergroup_id:int32 user_id:int32 message_ids:vector = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 981fc6f522ff0f24409757050b880eb4e2c5acf6..2fbe17949b8d1fe9bab3d88e42c488b13607928e 100644 GIT binary patch delta 79 zcmZ3{%rmEjr=f*$3zLlj{OJW hn39}WT;dMmPY*O_5}tm>oKa_*3zOh>%im1-Yyi{H9p?Z5 delta 27 jcmbQ!!n2~8r=f*$3zLlj promise_; + ChannelId channel_id_; + + public: + explicit ConvertToGigagroupQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id) { + channel_id_ = channel_id; + + auto input_channel = td->contacts_manager_->get_input_channel(channel_id); + CHECK(input_channel != nullptr); + send_query(G()->net_query_creator().create(telegram_api::channels_convertToGigagroup(std::move(input_channel)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for ConvertToGigagroupQuery: " << to_string(ptr); + + td->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); + } + + void on_error(uint64 id, Status status) override { + if (status.message() == "CHAT_NOT_MODIFIED") { + promise_.set_value(Unit()); + return; + } else { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "ConvertToGigagroupQuery"); + } + promise_.set_error(std::move(status)); + } +}; + class EditChatAboutQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -6159,6 +6198,21 @@ void ContactsManager::toggle_channel_is_all_history_available(ChannelId channel_ td_->create_handler(std::move(promise))->send(channel_id, is_all_history_available); } +void ContactsManager::convert_channel_to_gigagroup(ChannelId channel_id, Promise &&promise) { + auto c = get_channel(channel_id); + if (c == nullptr) { + return promise.set_error(Status::Error(6, "Supergroup not found")); + } + if (!get_channel_permissions(c).is_creator()) { + return promise.set_error(Status::Error(6, "Not enough rights to convert group to broadcast group")); + } + if (get_channel_type(c) != ChannelType::Megagroup) { + return promise.set_error(Status::Error(6, "Chat must be a supergroup")); + } + + td_->create_handler(std::move(promise))->send(channel_id); +} + void ContactsManager::set_channel_description(ChannelId channel_id, const string &description, Promise &&promise) { auto new_description = strip_empty_characters(description, MAX_DESCRIPTION_LENGTH); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 05b87c1c9..f903248b4 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -352,6 +352,8 @@ class ContactsManager : public Actor { void toggle_channel_is_all_history_available(ChannelId channel_id, bool is_all_history_available, Promise &&promise); + void convert_channel_to_gigagroup(ChannelId channel_id, Promise &&promise); + void set_channel_description(ChannelId channel_id, const string &description, Promise &&promise); void set_channel_discussion_group(DialogId dialog_id, DialogId discussion_dialog_id, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 2920a1869..1f2131314 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6733,6 +6733,12 @@ void Td::on_request(uint64 id, const td_api::toggleSupergroupIsAllHistoryAvailab request.is_all_history_available_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::toggleSupergroupIsBroadcastGroup &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->convert_channel_to_gigagroup(ChannelId(request.supergroup_id_), std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::reportSupergroupSpam &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 625286588..d3672bbac 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -892,6 +892,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::toggleSupergroupIsAllHistoryAvailable &request); + void on_request(uint64 id, const td_api::toggleSupergroupIsBroadcastGroup &request); + void on_request(uint64 id, const td_api::reportSupergroupSpam &request); void on_request(uint64 id, td_api::getSupergroupMembers &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 02f09b55b..43891ff61 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3728,6 +3728,10 @@ class CliClient final : public Actor { get_args(args, supergroup_id, is_all_history_available); send_request(td_api::make_object(as_supergroup_id(supergroup_id), is_all_history_available)); + } else if (op == "ToggleSupergroupIsBroadcastGroup") { + string supergroup_id; + get_args(args, supergroup_id); + send_request(td_api::make_object(as_supergroup_id(supergroup_id))); } else if (op == "tsgsm") { string supergroup_id; bool sign_messages; From 998d022b2d6e405cd2c152edca79d73f34460479 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 18 Feb 2021 13:47:33 +0300 Subject: [PATCH 178/232] Add td_api::chatEventVoiceChatParticipantVolumeLevelChanged. --- td/generate/scheme/td_api.tl | 3 +++ td/generate/scheme/td_api.tlo | Bin 197148 -> 197308 bytes td/telegram/MessagesManager.cpp | 12 ++++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 9dfec93d9..f1085ef39 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2455,6 +2455,9 @@ chatEventVoiceChatDiscarded group_call_id:int32 = ChatEventAction; //@description A voice chat participant was muted or unmuted @user_id Identifier of the affected user @is_muted New value of is_muted chatEventVoiceChatParticipantIsMutedToggled user_id:int32 is_muted:Bool = ChatEventAction; +//@description A voice chat participant volume level was changed @user_id Identifier of the affected user @volume_level New value of volume_level; 1-20000 in hundreds of percents +chatEventVoiceChatParticipantVolumeLevelChanged user_id:int32 volume_level:int32 = ChatEventAction; + //@description The mute_new_participants setting of a voice chat was toggled @mute_new_participants New value of the mute_new_participants setting chatEventVoiceChatMuteNewParticipantsToggled mute_new_participants:Bool = ChatEventAction; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 2fbe17949b8d1fe9bab3d88e42c488b13607928e..4bd9704fd4c28738fb04c58bee8d28deefad4742 100644 GIT binary patch delta 769 zcmbQ!!n3E9X92tD@!d+{{LUGPC9Y+uc_og?C7Jno3X>-?iEq|nZ_#Be*}Tz2-iQUH zYVwsd8wf)rJp#fgNY{Wc4rCZW7#A=^3^LHwOvund)Ac1oLjq*Q&0|773_wui%%;O( z+jbCatx2W{SoySNObU~GGDRSImt?Bo*76_|V&`;)7fia7dp)G5JKSdCfM`s}g1W~c zRcG>!EFXv{M>f<=D56`kp=uvwLw#(M167xklK|0q0V*;n7b=jF3so`&Ch{W}YK%%A z)R=}msK}Z;XqdjqgX)sW&wv=*kq-^iJunfC0!TdJJd5l zO+JAsWKs(Ca6yC48#AR8aB^NGvMJOwKGw z%qt1Y&neAK^+_#D%>l~frKhHV(+S8xi4!W558UNs1|`=sr4A5p36wz?%^_v&A!Ur) KL&}&=bOQiw12Vh- delta 688 zcmdnf$}^{hX92tD{dXq2_?Q~o!sjoJ^jILCJutK{nLOHaSprIXMXsofn`YlX9T~DY;N3Q(z)La-qhk z?s4nfwfh!5#U~K-~iq(I|if#pHy70*IO`1qPh3kYk*_@DGzQ zMBJqiYFbGl)U*dM5rHD8J4=czAc{8>xj+~{ilDkQilJ(HiXkFsf&T@&a7+nQ^@DmQ rsM#klg-lAJUKS|RnY^GB8W1^U4p7%X8O>kH+P{=BZvRrobfOyoYQ##` diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 8727b4ce1..f31a1ad76 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -31371,8 +31371,16 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob return make_tl_object( td_->contacts_manager_->get_user_id_object(participant.user_id, "LogEventActionParticipantUnmute"), false); } - case telegram_api::channelAdminLogEventActionParticipantVolume::ID: - return nullptr; + case telegram_api::channelAdminLogEventActionParticipantVolume::ID: { + auto action = move_tl_object_as(action_ptr); + GroupCallParticipant participant(std::move(action->participant_)); + if (!participant.is_valid()) { + return nullptr; + } + return make_tl_object( + td_->contacts_manager_->get_user_id_object(participant.user_id, "LogEventActionParticipantVolume"), + participant.volume_level); + } case telegram_api::channelAdminLogEventActionToggleGroupCallSetting::ID: { auto action = move_tl_object_as(action_ptr); return make_tl_object(action->join_muted_); From 7eb751083c8ea4dc1f6152740df7ed2fd7b6150a Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 18 Feb 2021 16:24:22 +0300 Subject: [PATCH 179/232] Add can_inite_users right check just in case. --- td/telegram/ContactsManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 8e5e4f89b..2144c43f2 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -9213,7 +9213,8 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val CHECK(c != nullptr); // ignore ChatFull without invite link - if (c->is_active && c->status.is_administrator() && !chat_full->invite_link.is_valid()) { + if (c->is_active && c->status.is_administrator() && c->status.can_invite_users() && + !chat_full->invite_link.is_valid()) { chats_full_.erase(chat_id); return; } @@ -13466,7 +13467,8 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha } } - if (c->is_active && c->status.is_administrator() && !chat_full->invite_link.is_valid()) { + if (c->is_active && c->status.is_administrator() && c->status.can_invite_users() && + !chat_full->invite_link.is_valid()) { LOG(INFO) << "Have outdated invite link in " << chat_id; return true; } From b9b6b05bae6cab4952d78d90a05beda330be990a Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 18 Feb 2021 22:14:10 +0300 Subject: [PATCH 180/232] Don't allow changing auto-delete in Saved Messages and 777000. --- td/generate/scheme/td_api.tl | 1 + td/telegram/MessagesManager.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index f1085ef39..62c297b84 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4278,6 +4278,7 @@ setChatTitle chat_id:int53 title:string = Ok; setChatPhoto chat_id:int53 photo:InputChatPhoto = Ok; //@description Changes the message TTL setting (sets a new self-destruct timer) in a chat. Requires can_delete_messages administrator right in basic groups, supergroups and channels +//-Message TTL setting of a chat with the current user (Saved Messages) and the chat 777000 (Telegram) can't be changed //@chat_id Chat identifier @ttl New TTL value, in seconds; must be one of 0, 86400, 604800 unless chat is secret setChatMessageTtlSetting chat_id:int53 ttl:int32 = Ok; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index f31a1ad76..182d52a46 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -30727,7 +30727,7 @@ void MessagesManager::set_dialog_title(DialogId dialog_id, const string &title, void MessagesManager::set_dialog_message_ttl_setting(DialogId dialog_id, int32 ttl, Promise &&promise) { if (ttl < 0) { - return promise.set_error(Status::Error(400, "Message TTL can't be negative")); + return promise.set_error(Status::Error(400, "Message auto-delete time can't be negative")); } Dialog *d = get_dialog_force(dialog_id); @@ -30742,19 +30742,25 @@ void MessagesManager::set_dialog_message_ttl_setting(DialogId dialog_id, int32 t switch (dialog_id.get_type()) { case DialogType::User: + if (dialog_id == get_my_dialog_id() || + dialog_id == DialogId(ContactsManager::get_service_notifications_user_id())) { + return promise.set_error(Status::Error(400, "Message auto-delete time in the chat can't be changed")); + } break; case DialogType::Chat: { auto chat_id = dialog_id.get_chat_id(); auto status = td_->contacts_manager_->get_chat_permissions(chat_id); if (!status.can_delete_messages()) { - return promise.set_error(Status::Error(400, "Not enough rights to set message TTL in the chat")); + return promise.set_error( + Status::Error(400, "Not enough rights to change message auto-delete time in the chat")); } break; } case DialogType::Channel: { auto status = td_->contacts_manager_->get_channel_permissions(dialog_id.get_channel_id()); if (!status.can_change_info_and_settings()) { - return promise.set_error(Status::Error(400, "Not enough rights to set message TTL in the chat")); + return promise.set_error( + Status::Error(400, "Not enough rights to change message auto-delete time in the chat")); } break; } @@ -30775,7 +30781,7 @@ void MessagesManager::set_dialog_message_ttl_setting(DialogId dialog_id, int32 t send_update_new_message(d, m); if (need_update_dialog_pos) { - send_update_chat_last_message(d, "send_dialog_set_ttl_message"); + send_update_chat_last_message(d, "set_dialog_message_ttl_setting"); } int64 random_id = begin_send_message(dialog_id, m); From ad43c83bcec619ac67ee497bca3b5fba4dad86a2 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 01:38:16 +0300 Subject: [PATCH 181/232] Add DialogParticipant constructor from telegram_api::ChatParticipant. --- td/telegram/ContactsManager.cpp | 38 ++++++++----------------------- td/telegram/DialogParticipant.cpp | 26 +++++++++++++++++++++ td/telegram/DialogParticipant.h | 3 +++ 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 2144c43f2..8fcb8efcd 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -11108,29 +11108,10 @@ void ContactsManager::on_get_chat_participants(tl_object_ptrparticipants_.size()); for (auto &participant_ptr : participants->participants_) { - DialogParticipant dialog_participant; - switch (participant_ptr->get_id()) { - case telegram_api::chatParticipant::ID: { - auto participant = move_tl_object_as(participant_ptr); - dialog_participant = {UserId(participant->user_id_), UserId(participant->inviter_id_), participant->date_, - DialogParticipantStatus::Member()}; - break; - } - case telegram_api::chatParticipantCreator::ID: { - auto participant = move_tl_object_as(participant_ptr); - new_creator_user_id = UserId(participant->user_id_); - dialog_participant = {new_creator_user_id, new_creator_user_id, c->date, - DialogParticipantStatus::Creator(true, false, string())}; - break; - } - case telegram_api::chatParticipantAdmin::ID: { - auto participant = move_tl_object_as(participant_ptr); - dialog_participant = {UserId(participant->user_id_), UserId(participant->inviter_id_), participant->date_, - DialogParticipantStatus::GroupAdministrator(c->status.is_creator())}; - break; - } - default: - UNREACHABLE(); + DialogParticipant dialog_participant(std::move(participant_ptr), c->date, c->status.is_creator()); + if (!dialog_participant.is_valid()) { + LOG(ERROR) << "Receive invalid " << dialog_participant; + continue; } LOG_IF(ERROR, !have_user(dialog_participant.user_id)) @@ -11143,18 +11124,17 @@ void ContactsManager::on_get_chat_participants(tl_object_ptrdate; dialog_participant.joined_date = c->date; } + if (dialog_participant.status.is_creator()) { + new_creator_user_id = dialog_participant.user_id; + } new_participants.push_back(std::move(dialog_participant)); } - if (new_creator_user_id.is_valid()) { - LOG_IF(ERROR, !have_user(new_creator_user_id)) - << "Have no information about group creator " << new_creator_user_id << " in " << chat_id; - if (chat_full->creator_user_id.is_valid() && chat_full->creator_user_id != new_creator_user_id) { + if (chat_full->creator_user_id != new_creator_user_id) { + if (new_creator_user_id.is_valid() && chat_full->creator_user_id.is_valid()) { LOG(ERROR) << "Group creator has changed from " << chat_full->creator_user_id << " to " << new_creator_user_id << " in " << chat_id; } - } - if (chat_full->creator_user_id != new_creator_user_id) { chat_full->creator_user_id = new_creator_user_id; chat_full->is_changed = true; } diff --git a/td/telegram/DialogParticipant.cpp b/td/telegram/DialogParticipant.cpp index 7bd29a4e8..a8ef0814c 100644 --- a/td/telegram/DialogParticipant.cpp +++ b/td/telegram/DialogParticipant.cpp @@ -643,6 +643,32 @@ DialogParticipant::DialogParticipant(UserId user_id, UserId inviter_user_id, int } } +DialogParticipant::DialogParticipant(tl_object_ptr &&participant_ptr, + int32 chat_creation_date, bool is_creator) { + switch (participant_ptr->get_id()) { + case telegram_api::chatParticipant::ID: { + auto participant = move_tl_object_as(participant_ptr); + *this = {UserId(participant->user_id_), UserId(participant->inviter_id_), participant->date_, + DialogParticipantStatus::Member()}; + break; + } + case telegram_api::chatParticipantCreator::ID: { + auto participant = move_tl_object_as(participant_ptr); + *this = {UserId(participant->user_id_), UserId(participant->user_id_), chat_creation_date, + DialogParticipantStatus::Creator(true, false, string())}; + break; + } + case telegram_api::chatParticipantAdmin::ID: { + auto participant = move_tl_object_as(participant_ptr); + *this = {UserId(participant->user_id_), UserId(participant->inviter_id_), participant->date_, + DialogParticipantStatus::GroupAdministrator(is_creator)}; + break; + } + default: + UNREACHABLE(); + } +} + DialogParticipant::DialogParticipant(tl_object_ptr &&participant_ptr, DialogParticipantStatus my_status) { CHECK(participant_ptr != nullptr); diff --git a/td/telegram/DialogParticipant.h b/td/telegram/DialogParticipant.h index 5f376c241..b382ba790 100644 --- a/td/telegram/DialogParticipant.h +++ b/td/telegram/DialogParticipant.h @@ -383,6 +383,9 @@ struct DialogParticipant { DialogParticipant(UserId user_id, UserId inviter_user_id, int32 joined_date, DialogParticipantStatus status); + DialogParticipant(tl_object_ptr &&participant_ptr, int32 chat_creation_date, + bool is_creator); + DialogParticipant(tl_object_ptr &&participant_ptr, DialogParticipantStatus my_status); From fd190bc9bd372f2794e89245186be196ab28cade Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 02:16:50 +0300 Subject: [PATCH 182/232] Process updateChatParticipant and updateBotStopped. --- td/telegram/ContactsManager.cpp | 66 +++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 5 +++ td/telegram/UpdatesManager.cpp | 37 ++++++++++++++---- td/telegram/UpdatesManager.h | 6 +-- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 8fcb8efcd..8eff35adf 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -12832,6 +12832,72 @@ void ContactsManager::on_update_channel_default_permissions(ChannelId channel_id } } +void ContactsManager::on_update_bot_stopped(UserId user_id, int32 date, bool is_stopped) { + if (!td_->auth_manager_->is_bot()) { + LOG(ERROR) << "Receive updateBotStopped by non-bot"; + return; + } + if (!user_id.is_valid() || date <= 0) { + LOG(ERROR) << "Receive invalid updateBotStopped by " << user_id << " at " << date; + return; + } + if (!have_user_force(user_id)) { + LOG(ERROR) << "Receive updateBotStopped by unknown " << user_id; + return; + } + + DialogParticipant old_dialog_participant(get_my_id(), user_id, date, DialogParticipantStatus::Banned(0)); + DialogParticipant new_dialog_participant(get_my_id(), user_id, date, DialogParticipantStatus::Member()); + if (is_stopped) { + std::swap(old_dialog_participant.status, new_dialog_participant.status); + } + + // TODO send update +} + +void ContactsManager::on_update_chat_participant(ChatId chat_id, UserId user_id, int32 date, + tl_object_ptr old_participant, + tl_object_ptr new_participant) { + if (!td_->auth_manager_->is_bot()) { + LOG(ERROR) << "Receive updateChatParticipant by non-bot"; + return; + } + if (!chat_id.is_valid() || !user_id.is_valid() || date <= 0 || + (old_participant == nullptr && new_participant == nullptr)) { + LOG(ERROR) << "Receive invalid updateChatParticipant in " << chat_id << " for " << user_id << " at " << date << ": " + << to_string(old_participant) << " -> " << to_string(new_participant); + return; + } + + const Chat *c = get_chat(chat_id); + if (c == nullptr) { + LOG(ERROR) << "Receive updateChatParticipant in unknown " << chat_id; + return; + } + + DialogParticipant old_dialog_participant; + DialogParticipant new_dialog_participant; + if (old_participant != nullptr) { + old_dialog_participant = DialogParticipant(std::move(old_participant), c->date, c->status.is_creator()); + if (new_participant == nullptr) { + new_dialog_participant = DialogParticipant::left(old_dialog_participant.user_id); + } else { + new_dialog_participant = DialogParticipant(std::move(new_participant), c->date, c->status.is_creator()); + } + } else { + new_dialog_participant = DialogParticipant(std::move(new_participant), c->date, c->status.is_creator()); + old_dialog_participant = DialogParticipant::left(new_dialog_participant.user_id); + } + if (old_dialog_participant.user_id != new_dialog_participant.user_id || !old_dialog_participant.is_valid() || + !new_dialog_participant.is_valid()) { + LOG(ERROR) << "Receive wrong updateChannelParticipant: " << old_dialog_participant << " -> " + << new_dialog_participant; + return; + } + + // TODO send update +} + void ContactsManager::on_update_channel_participant(ChannelId channel_id, UserId user_id, int32 date, tl_object_ptr old_participant, tl_object_ptr new_participant) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index f903248b4..7a84a1721 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -210,6 +210,11 @@ class ContactsManager : public Actor { Promise &&promise); void on_update_channel_default_permissions(ChannelId channel_id, RestrictedRights default_permissions); void on_update_channel_administrator_count(ChannelId channel_id, int32 administrator_count); + + void on_update_bot_stopped(UserId user_id, int32 date, bool is_stopped); + void on_update_chat_participant(ChatId chat_id, UserId user_id, int32 date, + tl_object_ptr old_participant, + tl_object_ptr new_participant); void on_update_channel_participant(ChannelId channel_id, UserId user_id, int32 date, tl_object_ptr old_participant, tl_object_ptr new_participant); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index c3b0382a7..ca28063ba 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1954,6 +1954,19 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up add_qts(qts)); break; } + case telegram_api::updateBotStopped::ID: { + auto update = move_tl_object_as(update_ptr); + td_->contacts_manager_->on_update_bot_stopped(UserId(update->user_id_), update->date_, + std::move(update->stopped_)); + break; + } + case telegram_api::updateChatParticipant::ID: { + auto update = move_tl_object_as(update_ptr); + td_->contacts_manager_->on_update_chat_participant(ChatId(update->chat_id_), UserId(update->user_id_), + update->date_, std::move(update->prev_participant_), + std::move(update->new_participant_)); + break; + } case telegram_api::updateChannelParticipant::ID: { auto update = move_tl_object_as(update_ptr); td_->contacts_manager_->on_update_channel_participant(ChannelId(update->channel_id_), UserId(update->user_id_), @@ -2453,6 +2466,8 @@ int32 UpdatesManager::get_update_pts(const telegram_api::Update *update) { bool UpdatesManager::is_qts_update(const telegram_api::Update *update) { switch (update->get_id()) { case telegram_api::updateNewEncryptedMessage::ID: + case telegram_api::updateBotStopped::ID: + case telegram_api::updateChatParticipant::ID: case telegram_api::updateChannelParticipant::ID: return true; default: @@ -2464,6 +2479,10 @@ int32 UpdatesManager::get_update_qts(const telegram_api::Update *update) { switch (update->get_id()) { case telegram_api::updateNewEncryptedMessage::ID: return static_cast(update)->qts_; + case telegram_api::updateBotStopped::ID: + return static_cast(update)->qts_; + case telegram_api::updateChatParticipant::ID: + return static_cast(update)->qts_; case telegram_api::updateChannelParticipant::ID: return static_cast(update)->qts_; default: @@ -2850,6 +2869,16 @@ void UpdatesManager::on_update(tl_object_ptr upd promise.set_value(Unit()); } +void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + auto qts = update->qts_; + add_pending_qts_update(std::move(update), qts, std::move(promise)); +} + +void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + auto qts = update->qts_; + add_pending_qts_update(std::move(update), qts, std::move(promise)); +} + void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { auto qts = update->qts_; add_pending_qts_update(std::move(update), qts, std::move(promise)); @@ -2857,14 +2886,6 @@ void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - promise.set_value(Unit()); -} - -void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - promise.set_value(Unit()); -} - void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { promise.set_value(Unit()); } diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 079b2bb62..d13329988 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -452,14 +452,12 @@ class UpdatesManager : public Actor { void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); // unsupported updates - void on_update(tl_object_ptr update, Promise &&promise); - - void on_update(tl_object_ptr update, Promise &&promise); - void on_update(tl_object_ptr update, Promise &&promise); }; From d765c58d86fc63e611ba5dbd9b9b51f8896780ba Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 13:13:39 +0300 Subject: [PATCH 183/232] tg_cli: improve variable name. --- td/telegram/cli.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 43891ff61..e02ba1c69 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2860,10 +2860,10 @@ class CliClient final : public Actor { get_args(args, chat_id, is_marked_as_read); send_request(td_api::make_object(as_chat_id(chat_id), is_marked_as_read)); } else if (op == "tmsib") { - string chat_id; + string sender_id; bool is_blocked; - get_args(args, chat_id, is_blocked); - send_request(td_api::make_object(as_message_sender(chat_id), is_blocked)); + get_args(args, sender_id, is_blocked); + send_request(td_api::make_object(as_message_sender(sender_id), is_blocked)); } else if (op == "bmsfr") { string message_id; bool delete_message; From 62a4c0c58d2933fe063186bbb91e2aa7589f0103 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 13:57:46 +0300 Subject: [PATCH 184/232] Add updateChatMember. --- td/generate/scheme/td_api.tl | 5 +++++ td/generate/scheme/td_api.tlo | Bin 197308 -> 197588 bytes td/telegram/ContactsManager.cpp | 24 +++++++++++++++++++++--- td/telegram/ContactsManager.h | 8 ++++++-- td/telegram/UpdatesManager.cpp | 6 ++++-- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 62c297b84..1ba4b0ced 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3599,6 +3599,11 @@ updatePoll poll:poll = Update; //@description A user changed the answer to a poll; for bots only @poll_id Unique poll identifier @user_id The user, who changed the answer to the poll @option_ids 0-based identifiers of answer options, chosen by the user updatePollAnswer poll_id:int64 user_id:int32 option_ids:vector = Update; +//@description User rights changed in a chat; for bots only @chat_id Chat identifier @actor_user_id Identifier of the user, changing the rights +//@date Point in time (Unix timestamp) when the user rights was changed @invite_link If user has joined the chat using an invite link, the invite link; may be null +//@old_chat_member Previous chat member @new_chat_member New chat member +updateChatMember chat_id:int53 actor_user_id:int32 date:int32 invite_link:chatInviteLink old_chat_member:chatMember new_chat_member:chatMember = Update; + //@description Contains a list of updates @updates List of updates updates updates:vector = Updates; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 4bd9704fd4c28738fb04c58bee8d28deefad4742..dd0952f592a3f1e5816fa8630b4f24c8bd6e49df 100644 GIT binary patch delta 2209 zcmZ8iZD?Cn81DNfY18(0&DO7oMJ^a!=5ARE0Si`$tjvfRMCRCFfwr!}3fWd>rWGSh zR$X;1bY9!zkNzkU5J50y#YE_j3Pq|`tkB^Gl~ruJ*w3_0zdz>9J?Fj6mR~2&d7k%u z-gDmf+`&(z@tabtue#`k4|Z(1XIq*Y!@-=h>TRsJGh987aP3CFJKpbx!tc!)1{?Yp zYV}pm6gc#SI@Ijqdx0qstF9?{lViy#Q1Ua%`#&G?A%j=pC>xyyhZ?(O<&5w&IQgn? z8lrsVogvJB8U1Y7_?wy$S(z=UZFD>j&#}&jaGc8q9|~E|EqsG5sBo0yO-VAQhf5lN zEB^z9Qw|(Q4NND0w*qcX3CvQI19EZ}WWFoS(XMAsU}|57!n)?japEz2>~FNO(AywM zS5M|D)|Ws1Nk3oLJXJ2A!z%Tu&qN6W20l3tT|8xYfv%vhql*2qK;@e(f|3!ai3^JAIVEnVcZoDwUWKKWpou3xpMn~Fr6(CmQCloO#+SU{ zX6?&#%86xBoi4Jih!D)TLLRTLKm$)OMVQW=SOu9SC$N-nMpj7>TO~nhI=k$aG%=!S zvZR;2m8P;CYjoEBH3~9dV3TXcIj>Uy&(~1H#@9i(m|xG%=-r@V2Ik%;USOYiUlOL;-cwj|pX#wcqSeG} z=w^{eBpA>(vQ7zp#1q|if|%a|^ENZ`G-HLctt* zUw~Cl(p%sJJcw>))&HQ2bpn3KOO{Z?XSImDk6A0CIFPRp#X~lVDCRqb!Wwg$NZaSu zy1T=K;{8e(8_I8}sIHI9tP4#-Uu&kAEoSs{7sKW(k0#bQi^tVOz{z|T%GG8O!Q(Lt z+C{I7<{GAQDO6n^^5>X^@<<%bBcskZik9$N32U`d;R9BxVA6^qZq1)hSR^0492+sP zbUykxR#%W!0HiaL!(hDA#7JWq44M5I<* z7VY22r6}&$QD)dLqvF~OELKMMB$rduddi6xE~iuLEys4{$?1unK4G%)={ecl7(N}m z^j7eyHeLGnXQ&YsOHt#+@D&C(;90GSW#&7FrK<1U!!KP4HPv6#B=wg_lau*yNoQ97 v05P6V&pbF*d^!03lRUNIb33otSs`vR-dcg959~C&nE=_J!gyc<8w&piHl-F& delta 2042 zcmZ8iUr1YL6!#pH7|l&h>z`#HBM)I_^p-^$m=6wanRJ2qaLYOt1?v+-^tE>o2OH9ZtzCx0-$MDdH&?SBH zmQA+5`Ni+XZ2cvYuRK68zqEa$AOzU>1`Rg5 zA-XbDxi%?%V3YKXZo(ihSs=`a&Hsa9^08+Wn|emE?Unj&BNd8BR!EJ}?QVs}9^!(`^)hX{8h_l0oHbwy^em+^o5o4Fe=TGYq z+`6w~V9G7N^OTAlo*G5 z-N4Ii#6fIH2UT8j&`2y2VSY(m5jG=HNAnV~wL1l-g?|;#WXwrLvQA3%o}hB-2^{C5 z?h~ddf4P~b6&E;OYNZCcT)4!svPauth_manager_->is_bot()); + td_->messages_manager_->force_create_dialog(dialog_id, "send_update_chat_member", true); + send_closure(G()->td(), &Td::send_update, + td_api::make_object( + dialog_id.get(), get_user_id_object(agent_user_id, "send_update_chat_member"), date, + invite_link.get_chat_invite_link_object(this), get_chat_member_object(old_dialog_participant), + get_chat_member_object(new_dialog_participant))); +} + void ContactsManager::on_update_bot_stopped(UserId user_id, int32 date, bool is_stopped) { if (!td_->auth_manager_->is_bot()) { LOG(ERROR) << "Receive updateBotStopped by non-bot"; @@ -12852,10 +12865,12 @@ void ContactsManager::on_update_bot_stopped(UserId user_id, int32 date, bool is_ std::swap(old_dialog_participant.status, new_dialog_participant.status); } - // TODO send update + send_update_chat_member(DialogId(user_id), user_id, date, DialogInviteLink(), old_dialog_participant, + new_dialog_participant); } void ContactsManager::on_update_chat_participant(ChatId chat_id, UserId user_id, int32 date, + DialogInviteLink invite_link, tl_object_ptr old_participant, tl_object_ptr new_participant) { if (!td_->auth_manager_->is_bot()) { @@ -12895,10 +12910,12 @@ void ContactsManager::on_update_chat_participant(ChatId chat_id, UserId user_id, return; } - // TODO send update + send_update_chat_member(DialogId(chat_id), user_id, date, invite_link, old_dialog_participant, + new_dialog_participant); } void ContactsManager::on_update_channel_participant(ChannelId channel_id, UserId user_id, int32 date, + DialogInviteLink invite_link, tl_object_ptr old_participant, tl_object_ptr new_participant) { if (!td_->auth_manager_->is_bot()) { @@ -12932,7 +12949,8 @@ void ContactsManager::on_update_channel_participant(ChannelId channel_id, UserId return; } - // TODO send update + send_update_chat_member(DialogId(channel_id), user_id, date, invite_link, old_dialog_participant, + new_dialog_participant); } void ContactsManager::update_contacts_hints(const User *u, UserId user_id, bool from_database) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 7a84a1721..cf04e26c4 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -212,10 +212,10 @@ class ContactsManager : public Actor { void on_update_channel_administrator_count(ChannelId channel_id, int32 administrator_count); void on_update_bot_stopped(UserId user_id, int32 date, bool is_stopped); - void on_update_chat_participant(ChatId chat_id, UserId user_id, int32 date, + void on_update_chat_participant(ChatId chat_id, UserId user_id, int32 date, DialogInviteLink invite_link, tl_object_ptr old_participant, tl_object_ptr new_participant); - void on_update_channel_participant(ChannelId channel_id, UserId user_id, int32 date, + void on_update_channel_participant(ChannelId channel_id, UserId user_id, int32 date, DialogInviteLink invite_link, tl_object_ptr old_participant, tl_object_ptr new_participant); @@ -1368,6 +1368,10 @@ class ContactsManager : public Actor { void on_clear_imported_contacts(vector &&contacts, vector contacts_unique_id, std::pair, vector> &&to_add, Promise &&promise); + void send_update_chat_member(DialogId dialog_id, UserId agent_user_id, int32 date, DialogInviteLink invite_link, + const DialogParticipant &old_dialog_participant, + const DialogParticipant &new_dialog_participant); + static vector> get_chats_nearby_object( const vector &dialogs_nearby); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index ca28063ba..ac30b12ff 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1963,14 +1963,16 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up case telegram_api::updateChatParticipant::ID: { auto update = move_tl_object_as(update_ptr); td_->contacts_manager_->on_update_chat_participant(ChatId(update->chat_id_), UserId(update->user_id_), - update->date_, std::move(update->prev_participant_), + update->date_, DialogInviteLink(std::move(update->invite_)), + std::move(update->prev_participant_), std::move(update->new_participant_)); break; } case telegram_api::updateChannelParticipant::ID: { auto update = move_tl_object_as(update_ptr); td_->contacts_manager_->on_update_channel_participant(ChannelId(update->channel_id_), UserId(update->user_id_), - update->date_, std::move(update->prev_participant_), + update->date_, DialogInviteLink(std::move(update->invite_)), + std::move(update->prev_participant_), std::move(update->new_participant_)); break; } From ce80a2f12fea4cd72521c3cdf329bb552fffa7ac Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 14:12:50 +0300 Subject: [PATCH 185/232] Improve qts gap debug. --- td/telegram/UpdatesManager.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index ac30b12ff..1d0868f69 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -199,11 +199,22 @@ void UpdatesManager::fill_seq_gap(void *td) { } void UpdatesManager::fill_qts_gap(void *td) { - fill_gap(td, "qts"); + CHECK(td != nullptr); + if (G()->close_flag()) { + return; + } + + auto td_ptr = static_cast(td); + auto qts = std::numeric_limits::max(); + if (!td_ptr->updates_manager_->pending_qts_updates_.empty()) { + qts = td_ptr->updates_manager_->pending_qts_updates_.begin()->first; + } + string source = PSTRING() << "qts from " << td_ptr->updates_manager_->get_qts() << " to " << qts; + fill_gap(td, source.c_str()); } void UpdatesManager::fill_get_difference_gap(void *td) { - fill_gap(td, "getDifference"); + fill_gap(td, "rare getDifference calls"); } void UpdatesManager::fill_gap(void *td, const char *source) { From 60e45f645734b6a53fe13693877d4946b02f440e Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 14:43:16 +0300 Subject: [PATCH 186/232] Fix processing of qts updates. --- td/telegram/UpdatesManager.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 1d0868f69..653824b0f 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1657,7 +1657,7 @@ void UpdatesManager::add_pending_qts_update(tl_object_ptr int32 old_qts = get_qts(); LOG(INFO) << "Process update with qts = " << qts << ", current qts = " << old_qts; - if (qts < old_qts - 10001) { + if (qts < old_qts - 100001) { LOG(WARNING) << "Restore qts after qts overflow from " << old_qts << " to " << qts << " by " << oneline(to_string(update)); add_qts(qts - 1).set_value(Unit()); @@ -1674,7 +1674,7 @@ void UpdatesManager::add_pending_qts_update(tl_object_ptr CHECK(!running_get_difference_); - if (qts > old_qts + 1) { + if (qts > old_qts + 1 && old_qts > 0) { LOG(INFO) << "Postpone update with qts = " << qts; if (pending_qts_updates_.empty()) { set_qts_gap_timeout(MAX_UNFILLED_GAP_TIME); @@ -1955,8 +1955,10 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up Promise &&promise) { LOG(DEBUG) << "Process " << to_string(update_ptr); if (last_get_difference_qts_ + FORCED_GET_DIFFERENCE_PTS_DIFF < qts) { + if (last_get_difference_qts_ != 0) { + schedule_get_difference("process_qts_update"); + } last_get_difference_qts_ = qts; - schedule_get_difference("process_qts_update"); } switch (update_ptr->get_id()) { case telegram_api::updateNewEncryptedMessage::ID: { @@ -1969,6 +1971,7 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up auto update = move_tl_object_as(update_ptr); td_->contacts_manager_->on_update_bot_stopped(UserId(update->user_id_), update->date_, std::move(update->stopped_)); + add_qts(qts).set_value(Unit()); break; } case telegram_api::updateChatParticipant::ID: { @@ -1977,6 +1980,7 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up update->date_, DialogInviteLink(std::move(update->invite_)), std::move(update->prev_participant_), std::move(update->new_participant_)); + add_qts(qts).set_value(Unit()); break; } case telegram_api::updateChannelParticipant::ID: { @@ -1985,6 +1989,7 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up update->date_, DialogInviteLink(std::move(update->invite_)), std::move(update->prev_participant_), std::move(update->new_participant_)); + add_qts(qts).set_value(Unit()); break; } default: From ada09fecee9d14425ac51c9c435ac54bf0671a68 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 15:58:14 +0300 Subject: [PATCH 187/232] Add can_manage_chat administrator privilege. --- td/generate/scheme/td_api.tl | 7 +++-- td/generate/scheme/td_api.tlo | Bin 197588 -> 197628 bytes td/telegram/DialogParticipant.cpp | 44 +++++++++++++++++++----------- td/telegram/DialogParticipant.h | 21 ++++++++++---- td/telegram/cli.cpp | 30 ++++++++++---------- 5 files changed, 62 insertions(+), 40 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 1ba4b0ced..8bd62477a 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -455,17 +455,18 @@ chatMemberStatusCreator custom_title:string is_anonymous:Bool is_member:Bool = C //@description The user is a member of a chat and has some additional privileges. In basic groups, administrators can edit and delete messages sent by others, add new members, ban unprivileged members, and manage voice chats. In supergroups and channels, there are more detailed options for administrator privileges //@custom_title A custom title of the administrator; 0-16 characters without emojis; applicable to supergroups only //@can_be_edited True, if the current user can edit the administrator privileges for the called user +//@can_manage_chat True, if the administrator can get chat event log, get chat statistics, get message statistics in channels, get channel members, see anonymous administrators in supergoups and ignore slow mode. Implied by any other privilege; applicable to supergroups and channels only //@can_change_info True, if the administrator can change the chat title, photo, and other settings //@can_post_messages True, if the administrator can create channel posts; applicable to channels only //@can_edit_messages True, if the administrator can edit messages of other users and pin messages; applicable to channels only //@can_delete_messages True, if the administrator can delete messages of other users //@can_invite_users True, if the administrator can invite new users to the chat //@can_restrict_members True, if the administrator can restrict, ban, or unban chat members -//@can_pin_messages True, if the administrator can pin messages; applicable to groups only +//@can_pin_messages True, if the administrator can pin messages; applicable to basic groups and supergroups only //@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by them -//@can_manage_voice_chats True, if the administrator can manage voice chats; applicable to groups only +//@can_manage_voice_chats True, if the administrator can manage voice chats; applicable to basic groups and supergroups only //@is_anonymous True, if the administrator isn't shown in the chat member list and sends messages anonymously; applicable to supergroups only -chatMemberStatusAdministrator custom_title:string can_be_edited:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_voice_chats:Bool is_anonymous:Bool = ChatMemberStatus; +chatMemberStatusAdministrator custom_title:string can_be_edited:Bool can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_voice_chats:Bool is_anonymous:Bool = ChatMemberStatus; //@description The user is a member of a chat, without any additional privileges or restrictions chatMemberStatusMember = ChatMemberStatus; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index dd0952f592a3f1e5816fa8630b4f24c8bd6e49df..3693d28f4ce3f45e8a9aa223802b02d1471f211d 100644 GIT binary patch delta 306 zcmcc8&hw|8XTt?f7Pq=1;+wB^sWCe*aOn#UlKRG8W0W3cGLY4xOut;_a zTo|e^J3|s8#M27ZQqbBW0yc>O0dBP6DTHvulkrgDyF!^DI{N$Xh1hDwz3t0+C!Xnuz zaAByv>2TP#9R!I@-q4`~QT3ps1H#DZ#G`yorv^mTjZPadqgkSByF?e` g7F#f{{lam^?H7(Sxrl(can_be_edited) * CAN_BE_EDITED) | + (static_cast(can_manage_dialog) * CAN_MANAGE_DIALOG) | (static_cast(can_change_info) * CAN_CHANGE_INFO_AND_SETTINGS_ADMIN) | (static_cast(can_post_messages) * CAN_POST_MESSAGES) | (static_cast(can_edit_messages) * CAN_EDIT_MESSAGES) | @@ -56,6 +57,7 @@ DialogParticipantStatus DialogParticipantStatus::Administrator(bool is_anonymous if (flags == 0 || flags == CAN_BE_EDITED) { return Member(); } + flags |= CAN_MANAGE_DIALOG; return DialogParticipantStatus(Type::Administrator, IS_MEMBER | ALL_RESTRICTED_RIGHTS | flags, 0, std::move(rank)); } @@ -94,14 +96,14 @@ DialogParticipantStatus DialogParticipantStatus::Banned(int32 banned_until_date) } DialogParticipantStatus DialogParticipantStatus::GroupAdministrator(bool is_creator) { - return Administrator(false, string(), is_creator, true, false, false, true, true, true, true, false, true); + return Administrator(false, string(), is_creator, true, true, false, false, true, true, true, true, false, true); } DialogParticipantStatus DialogParticipantStatus::ChannelAdministrator(bool is_creator, bool is_megagroup) { if (is_megagroup) { - return Administrator(false, string(), is_creator, true, false, false, true, true, true, true, false, false); + return Administrator(false, string(), is_creator, true, true, false, false, true, true, true, true, false, false); } else { - return Administrator(false, string(), is_creator, false, true, true, true, false, true, false, false, false); + return Administrator(false, string(), is_creator, true, false, true, true, true, false, true, false, false, false); } } @@ -117,9 +119,9 @@ tl_object_ptr DialogParticipantStatus::get_chat_member return td_api::make_object(rank_, is_anonymous(), is_member()); case Type::Administrator: return td_api::make_object( - rank_, can_be_edited(), can_change_info_and_settings(), can_post_messages(), can_edit_messages(), - can_delete_messages(), can_invite_users(), can_restrict_members(), can_pin_messages(), can_promote_members(), - can_manage_calls(), is_anonymous()); + rank_, can_be_edited(), can_manage_dialog(), can_change_info_and_settings(), can_post_messages(), + can_edit_messages(), can_delete_messages(), can_invite_users(), can_restrict_members(), can_pin_messages(), + can_promote_members(), can_manage_calls(), is_anonymous()); case Type::Member: return td_api::make_object(); case Type::Restricted: @@ -167,6 +169,9 @@ tl_object_ptr DialogParticipantStatus::get_chat_a if (is_anonymous()) { flags |= telegram_api::chatAdminRights::ANONYMOUS_MASK; } + if (can_manage_dialog()) { + flags |= telegram_api::chatAdminRights::OTHER_MASK; + } LOG(INFO) << "Create chat admin rights " << flags; return make_tl_object( @@ -297,6 +302,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant return string_builder; case DialogParticipantStatus::Type::Administrator: string_builder << "Administrator: "; + if (status.can_manage_dialog()) { + string_builder << "(manage)"; + } if (status.can_change_info_and_settings()) { string_builder << "(change)"; } @@ -404,7 +412,7 @@ DialogParticipantStatus get_dialog_participant_status(const tl_object_ptr(status.get()); return DialogParticipantStatus::Administrator( - st->is_anonymous_, st->custom_title_, true /*st->can_be_edited_*/, st->can_change_info_, + st->is_anonymous_, st->custom_title_, true /*st->can_be_edited_*/, st->can_manage_chat_, st->can_change_info_, st->can_post_messages_, st->can_edit_messages_, st->can_delete_messages_, st->can_invite_users_, st->can_restrict_members_, st->can_pin_messages_, st->can_promote_members_, st->can_manage_voice_chats_); } @@ -449,10 +457,14 @@ DialogParticipantStatus get_dialog_participant_status(bool can_be_edited, bool can_promote_members = (admin_rights->flags_ & telegram_api::chatAdminRights::ADD_ADMINS_MASK) != 0; bool can_manage_calls = (admin_rights->flags_ & telegram_api::chatAdminRights::MANAGE_CALL_MASK) != 0; bool is_anonymous = (admin_rights->flags_ & telegram_api::chatAdminRights::ANONYMOUS_MASK) != 0; - return DialogParticipantStatus::Administrator(is_anonymous, std::move(rank), can_be_edited, can_change_info, - can_post_messages, can_edit_messages, can_delete_messages, - can_invite_users, can_restrict_members, can_pin_messages, - can_promote_members, can_manage_calls); + bool can_manage_dialog = (admin_rights->flags_ & telegram_api::chatAdminRights::OTHER_MASK) != 0; + if (!can_manage_dialog) { + LOG(ERROR) << "Receive wrong other flag in " << to_string(admin_rights); + } + return DialogParticipantStatus::Administrator(is_anonymous, std::move(rank), can_be_edited, can_manage_dialog, + can_change_info, can_post_messages, can_edit_messages, + can_delete_messages, can_invite_users, can_restrict_members, + can_pin_messages, can_promote_members, can_manage_calls); } DialogParticipantStatus get_dialog_participant_status( diff --git a/td/telegram/DialogParticipant.h b/td/telegram/DialogParticipant.h index b382ba790..9606ab33b 100644 --- a/td/telegram/DialogParticipant.h +++ b/td/telegram/DialogParticipant.h @@ -121,6 +121,7 @@ class DialogParticipantStatus { static constexpr uint32 CAN_PIN_MESSAGES_ADMIN = 1 << 7; static constexpr uint32 CAN_PROMOTE_MEMBERS = 1 << 8; static constexpr uint32 CAN_MANAGE_CALLS = 1 << 9; + static constexpr uint32 CAN_MANAGE_DIALOG = 1 << 10; static constexpr uint32 CAN_BE_EDITED = 1 << 15; @@ -144,9 +145,10 @@ class DialogParticipantStatus { static constexpr int TYPE_SHIFT = 28; static constexpr uint32 HAS_UNTIL_DATE = 1u << 31; - static constexpr uint32 ALL_ADMINISTRATOR_RIGHTS = - CAN_CHANGE_INFO_AND_SETTINGS_ADMIN | CAN_POST_MESSAGES | CAN_EDIT_MESSAGES | CAN_DELETE_MESSAGES | - CAN_INVITE_USERS_ADMIN | CAN_RESTRICT_MEMBERS | CAN_PIN_MESSAGES_ADMIN | CAN_PROMOTE_MEMBERS | CAN_MANAGE_CALLS; + static constexpr uint32 ALL_ADMINISTRATOR_RIGHTS = CAN_CHANGE_INFO_AND_SETTINGS_ADMIN | CAN_POST_MESSAGES | + CAN_EDIT_MESSAGES | CAN_DELETE_MESSAGES | CAN_INVITE_USERS_ADMIN | + CAN_RESTRICT_MEMBERS | CAN_PIN_MESSAGES_ADMIN | + CAN_PROMOTE_MEMBERS | CAN_MANAGE_CALLS | CAN_MANAGE_DIALOG; static constexpr uint32 ALL_ADMIN_PERMISSION_RIGHTS = CAN_CHANGE_INFO_AND_SETTINGS_BANNED | CAN_INVITE_USERS_BANNED | CAN_PIN_MESSAGES_BANNED; @@ -171,9 +173,10 @@ class DialogParticipantStatus { public: static DialogParticipantStatus Creator(bool is_member, bool is_anonymous, string rank); - static DialogParticipantStatus Administrator(bool is_anonymous, string rank, bool can_be_edited, bool can_change_info, - bool can_post_messages, bool can_edit_messages, bool can_delete_messages, - bool can_invite_users, bool can_restrict_members, bool can_pin_messages, + static DialogParticipantStatus Administrator(bool is_anonymous, string rank, bool can_be_edited, + bool can_manage_dialog, bool can_change_info, bool can_post_messages, + bool can_edit_messages, bool can_delete_messages, bool can_invite_users, + bool can_restrict_members, bool can_pin_messages, bool can_promote_members, bool can_manage_calls); static DialogParticipantStatus Member(); @@ -208,6 +211,10 @@ class DialogParticipantStatus { // unrestricts user if restriction time expired. Should be called before all privileges checks void update_restrictions() const; + bool can_manage_dialog() const { + return (flags_ & CAN_MANAGE_DIALOG) != 0; + } + bool can_change_info_and_settings() const { return (flags_ & CAN_CHANGE_INFO_AND_SETTINGS_ADMIN) != 0 || (flags_ & CAN_CHANGE_INFO_AND_SETTINGS_BANNED) != 0; } @@ -359,6 +366,8 @@ class DialogParticipantStatus { if (is_creator()) { flags_ |= ALL_ADMINISTRATOR_RIGHTS | ALL_PERMISSION_RIGHTS; + } else if (is_administrator()) { + flags_ |= CAN_MANAGE_DIALOG; } } diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index e02ba1c69..dde219695 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3622,30 +3622,30 @@ class CliClient final : public Actor { status = td_api::make_object("", true, true); } else if (status_str == "uncreator") { status = td_api::make_object("", false, false); - } else if (status_str == "anon") { - status = td_api::make_object("anon", true, true, true, true, true, true, - true, true, true, false, true); } else if (status_str == "anonadmin") { - status = td_api::make_object("anon", false, false, false, false, false, - false, false, false, false, false, true); + status = td_api::make_object("anon", true, true, true, true, true, true, + true, true, true, true, true, true); + } else if (status_str == "anon") { + status = td_api::make_object( + "anon", false, false, false, false, false, false, false, false, false, false, false, true); } else if (status_str == "addadmin") { - status = td_api::make_object("anon", false, false, false, false, false, - false, false, false, true, false, false); + status = td_api::make_object( + "anon", false, false, false, false, false, false, false, false, false, true, false, false); } else if (status_str == "calladmin") { - status = td_api::make_object("anon", false, false, false, false, false, - false, false, false, false, true, false); + status = td_api::make_object( + "anon", false, false, false, false, false, false, false, false, false, false, true, false); } else if (status_str == "admin") { - status = td_api::make_object("", true, true, true, true, true, true, - true, true, true, false, false); + status = td_api::make_object("", true, false, true, true, true, true, + true, true, true, true, true, false); } else if (status_str == "adminq") { - status = td_api::make_object("title", true, true, true, true, true, true, - true, true, true, false, false); + status = td_api::make_object("title", true, false, true, true, true, + true, true, true, true, true, true, false); } else if (status_str == "minadmin") { status = td_api::make_object("", true, true, false, false, false, false, - false, false, false, false, false); + false, false, false, false, false, false); } else if (status_str == "unadmin") { status = td_api::make_object("", true, false, false, false, false, false, - false, false, false, false, false); + false, false, false, false, false, false); } else if (status_str == "rest") { status = td_api::make_object( true, static_cast(120 + std::time(nullptr)), From 2e943a6c850587eefb6234df3c765b5ed95dccc4 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 16:37:44 +0300 Subject: [PATCH 188/232] Fix misprint. --- td/telegram/Global.cpp | 2 +- td/telegram/Global.h | 2 +- td/telegram/MessagesManager.cpp | 10 +++++----- td/telegram/UpdatesManager.cpp | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/td/telegram/Global.cpp b/td/telegram/Global.cpp index 24b99b6fe..d52193afc 100644 --- a/td/telegram/Global.cpp +++ b/td/telegram/Global.cpp @@ -215,7 +215,7 @@ DcId Global::get_webfile_dc_id() const { return DcId::internal(dc_id); } -bool Global::ignore_backgrond_updates() const { +bool Global::ignore_background_updates() const { return !parameters_.use_file_db && !parameters_.use_secret_chats && shared_config_->get_option_boolean("ignore_background_updates"); } diff --git a/td/telegram/Global.h b/td/telegram/Global.h index 92b0a5f3a..838888839 100644 --- a/td/telegram/Global.h +++ b/td/telegram/Global.h @@ -100,7 +100,7 @@ class Global : public ActorContext { return parameters_.use_test_dc; } - bool ignore_backgrond_updates() const; + bool ignore_background_updates() const; NetQueryCreator &net_query_creator() { return *net_query_creator_.get(); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 182d52a46..89ebf967c 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -12214,7 +12214,7 @@ void MessagesManager::init() { if (!dialog_filters.empty()) { DialogFiltersLogEvent log_event; if (log_event_parse(log_event, dialog_filters).is_ok()) { - dialog_filters_updated_date_ = G()->ignore_backgrond_updates() ? 0 : log_event.updated_date; + dialog_filters_updated_date_ = G()->ignore_background_updates() ? 0 : log_event.updated_date; std::unordered_set server_dialog_filter_ids; for (auto &dialog_filter : log_event.server_dialog_filters_out) { if (server_dialog_filter_ids.insert(dialog_filter->dialog_filter_id).second) { @@ -35069,7 +35069,7 @@ string MessagesManager::get_channel_pts_key(DialogId dialog_id) { } int32 MessagesManager::load_channel_pts(DialogId dialog_id) const { - if (G()->ignore_backgrond_updates()) { + if (G()->ignore_background_updates()) { G()->td_db()->get_binlog_pmc()->erase(get_channel_pts_key(dialog_id)); // just in case return 0; } @@ -35118,7 +35118,7 @@ void MessagesManager::set_channel_pts(Dialog *d, int32 new_pts, const char *sour repair_channel_server_unread_count(d); } } - if (!G()->ignore_backgrond_updates()) { + if (!G()->ignore_background_updates()) { G()->td_db()->get_binlog_pmc()->set(get_channel_pts_key(d->dialog_id), to_string(new_pts)); } } else if (new_pts < d->pts) { @@ -35224,7 +35224,7 @@ void MessagesManager::get_channel_difference(DialogId dialog_id, int32 pts, bool return; } - if (force && get_channel_difference_to_log_event_id_.count(dialog_id) == 0 && !G()->ignore_backgrond_updates()) { + if (force && get_channel_difference_to_log_event_id_.count(dialog_id) == 0 && !G()->ignore_background_updates()) { auto channel_id = dialog_id.get_channel_id(); CHECK(input_channel->get_id() == telegram_api::inputChannel::ID); auto access_hash = static_cast(*input_channel).access_hash_; @@ -36628,7 +36628,7 @@ void MessagesManager::on_binlog_events(vector &&events) { break; } case LogEvent::HandlerType::GetChannelDifference: { - if (G()->ignore_backgrond_updates()) { + if (G()->ignore_background_updates()) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; } diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 653824b0f..7bf193459 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -327,13 +327,13 @@ void UpdatesManager::on_qts_ack(PtsManager::PtsId ack_token) { void UpdatesManager::save_pts(int32 pts) { if (pts == std::numeric_limits::max()) { G()->td_db()->get_binlog_pmc()->erase("updates.pts"); - } else if (!G()->ignore_backgrond_updates()) { + } else if (!G()->ignore_background_updates()) { G()->td_db()->get_binlog_pmc()->set("updates.pts", to_string(pts)); } } void UpdatesManager::save_qts(int32 qts) { - if (!G()->ignore_backgrond_updates()) { + if (!G()->ignore_background_updates()) { G()->td_db()->get_binlog_pmc()->set("updates.qts", to_string(qts)); } } @@ -387,7 +387,7 @@ void UpdatesManager::set_date(int32 date, bool from_update, string date_source) date_ = date; date_source_ = std::move(date_source); - if (!G()->ignore_backgrond_updates()) { + if (!G()->ignore_background_updates()) { G()->td_db()->get_binlog_pmc()->set("updates.date", to_string(date)); } } else if (date < date_) { @@ -1124,7 +1124,7 @@ void UpdatesManager::init_state() { } auto pmc = G()->td_db()->get_binlog_pmc(); - if (G()->ignore_backgrond_updates()) { + if (G()->ignore_background_updates()) { // just in case pmc->erase("updates.pts"); pmc->erase("updates.qts"); From 3774b13ca10a6a63a80e768dc603d058264f5cb5 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 19 Feb 2021 19:11:27 +0300 Subject: [PATCH 189/232] Add td_api::reportChatPhoto. --- td/generate/scheme/td_api.tl | 11 +-- td/generate/scheme/td_api.tlo | Bin 197628 -> 197812 bytes td/telegram/ContactsManager.cpp | 6 +- td/telegram/MessagesManager.cpp | 118 ++++++++++++++++++++++++++++++-- td/telegram/MessagesManager.h | 8 ++- td/telegram/Td.cpp | 13 +++- td/telegram/Td.h | 2 + td/telegram/cli.cpp | 20 ++++-- 8 files changed, 154 insertions(+), 24 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 8bd62477a..193994626 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -377,7 +377,7 @@ chatPhotos total_count:int32 photos:vector = ChatPhotos; //@class InputChatPhoto @description Describes a photo to be set as a user profile or chat photo -//@description A previously used profile photo of the current user @chat_photo_id Identifier of the profile photo to reuse +//@description A previously used profile photo of the current user @chat_photo_id Identifier of the current user's profile photo to reuse inputChatPhotoPrevious chat_photo_id:int64 = InputChatPhoto; //@description A static photo in JPEG format @photo Photo to be set as profile photo. Only inputFileLocal and inputFileGenerated are allowed @@ -904,7 +904,7 @@ chatPosition list:ChatList order:int64 is_pinned:Bool source:ChatSource = ChatPo //@has_scheduled_messages True, if the chat has scheduled messages //@can_be_deleted_only_for_self True, if the chat messages can be deleted only for the current user while other users will continue to see the messages //@can_be_deleted_for_all_users True, if the chat messages can be deleted for all users -//@can_be_reported True, if the chat can be reported to Telegram moderators through reportChat +//@can_be_reported True, if the chat can be reported to Telegram moderators through reportChat or reportChatPhoto //@default_disable_notification Default value of the disable_notification parameter, used when a message is sent to the chat //@unread_count Number of unread messages in the chat //@last_read_inbox_message_id Identifier of the last read incoming message @@ -4932,9 +4932,12 @@ deleteAccount reason:string = Ok; removeChatActionBar chat_id:int53 = Ok; //@description Reports a chat to the Telegram moderators. A chat can be reported only from the chat action bar, or if this is a private chat with a bot, a private chat with a user sharing their location, a supergroup, or a channel, since other chats can't be checked by moderators -//@chat_id Chat identifier @reason The reason for reporting the chat @message_ids Identifiers of reported messages, if any @text Additional report details; 0-1024 characters -reportChat chat_id:int53 reason:ChatReportReason message_ids:vector text:string = Ok; +//@chat_id Chat identifier @message_ids Identifiers of reported messages, if any @reason The reason for reporting the chat @text Additional report details; 0-1024 characters +reportChat chat_id:int53 message_ids:vector reason:ChatReportReason text:string = Ok; +//@description Reports a chat photo to the Telegram moderators. A chat photo can be reported only if this is a private chat with a bot, a private chat with a user sharing their location, a supergroup, or a channel, since other chats can't be checked by moderators +//@chat_id Chat identifier @file_id Identifier of the photo to report. Only full photos from chatPhoto can be reported @reason The reason for reporting the chat photo @text Additional report details; 0-1024 characters +reportChatPhoto chat_id:int53 file_id:int32 reason:ChatReportReason text:string = Ok; //@description Returns an HTTP URL with the chat statistics. Currently this method of getting the statistics are disabled and can be deleted in the future @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link @is_dark Pass true if a URL with the dark theme must be returned getChatStatisticsUrl chat_id:int53 parameters:string is_dark:Bool = HttpUrl; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 3693d28f4ce3f45e8a9aa223802b02d1471f211d..2a8e444092c03ed7cc868d9da69bf37f3445811f 100644 GIT binary patch delta 99 zcmey<&abV w2|Jk-roY(9WFn&S@}xFDLQOzMeo6lH2Wy!OrbpB>DNdiblSzI%P@#wy0B!FkwEzGB delta 67 zcmV-J0KEUSi46RQ41k0Iv;rJ50iU-WG6FM)4?U+Lw+eD)aBp&SLug@im*1@dF_*}> Z0w9;Xy#h3ofLaEZ>bU|kx74}<;7Mk>8(aVY diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 4f2ce05a8..452b99d5c 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -9883,10 +9883,8 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u register_user_photo(u, user_id, user->photo); } - if (user_full->bot_info_ != nullptr) { - if (on_update_bot_info(std::move(user_full->bot_info_), false)) { - user->need_send_update = true; - } + if (user_full->bot_info_ != nullptr && on_update_bot_info(std::move(user_full->bot_info_), false)) { + user->need_send_update = true; } update_user_full(user, user_id); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 89ebf967c..e7974da4a 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -4388,8 +4388,8 @@ class ReportPeerQuery : public Td::ResultHandler { explicit ReportPeerQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, tl_object_ptr &&report_reason, - const vector &message_ids, const string &message) { + void send(DialogId dialog_id, const vector &message_ids, + tl_object_ptr &&report_reason, const string &message) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); @@ -4430,6 +4430,47 @@ class ReportPeerQuery : public Td::ResultHandler { } }; +class ReportProfilePhotoQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit ReportProfilePhotoQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, tl_object_ptr &&input_photo, + tl_object_ptr &&report_reason, const string &message) { + dialog_id_ = dialog_id; + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + + send_query(G()->net_query_creator().create(telegram_api::account_reportProfilePhoto( + std::move(input_peer), std::move(input_photo), std::move(report_reason), message))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + bool result = result_ptr.ok(); + if (!result) { + return on_error(id, Status::Error(400, "Receive false as result")); + } + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + LOG(INFO) << "Receive error for report chat photo: " << status; + // TODO support FILE_REFERENCE errors + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ReportProfilePhotoQuery"); + promise_.set_error(std::move(status)); + } +}; + class EditPeerFoldersQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -8168,8 +8209,8 @@ bool MessagesManager::can_report_dialog(DialogId dialog_id) const { } } -void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr &reason, - const vector &message_ids, const string &message, +void MessagesManager::report_dialog(DialogId dialog_id, const vector &message_ids, + const tl_object_ptr &reason, const string &message, Promise &&promise) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -8261,7 +8302,74 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptrcreate_handler(std::move(promise)) - ->send(dialog_id, std::move(report_reason), server_message_ids, message); + ->send(dialog_id, server_message_ids, std::move(report_reason), message); +} + +void MessagesManager::report_dialog_photo(DialogId dialog_id, FileId file_id, + const tl_object_ptr &reason, const string &message, + Promise &&promise) { + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(3, "Can't access the chat")); + } + + if (reason == nullptr) { + return promise.set_error(Status::Error(3, "Reason must be non-empty")); + } + + if (!can_report_dialog(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat photo can't be reported")); + } + + auto file_view = td_->file_manager_->get_file_view(file_id); + if (file_view.empty()) { + return promise.set_error(Status::Error(400, "Unknown file ID")); + } + if (file_view.get_type() != FileType::Photo || !file_view.has_remote_location() || + !file_view.remote_location().is_photo()) { + return promise.set_error(Status::Error(400, "Only full chat photos can be reported")); + } + + tl_object_ptr report_reason; + switch (reason->get_id()) { + case td_api::chatReportReasonSpam::ID: + report_reason = make_tl_object(); + break; + case td_api::chatReportReasonViolence::ID: + report_reason = make_tl_object(); + break; + case td_api::chatReportReasonPornography::ID: + report_reason = make_tl_object(); + break; + case td_api::chatReportReasonChildAbuse::ID: + report_reason = make_tl_object(); + break; + case td_api::chatReportReasonCopyright::ID: + report_reason = make_tl_object(); + break; + case td_api::chatReportReasonFake::ID: + report_reason = make_tl_object(); + break; + case td_api::chatReportReasonUnrelatedLocation::ID: + report_reason = make_tl_object(); + if (dialog_id.get_type() == DialogType::Channel) { + hide_dialog_action_bar(d); + } + break; + case td_api::chatReportReasonCustom::ID: + report_reason = make_tl_object(); + break; + default: + UNREACHABLE(); + } + CHECK(report_reason != nullptr); + + td_->create_handler(std::move(promise)) + ->send(dialog_id, file_view.remote_location().as_input_photo(), std::move(report_reason), message); } void MessagesManager::on_get_peer_settings(DialogId dialog_id, diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 03ff3cc36..5078f5795 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -775,8 +775,12 @@ class MessagesManager : public Actor { void reget_dialog_action_bar(DialogId dialog_id, const char *source); - void report_dialog(DialogId dialog_id, const tl_object_ptr &reason, - const vector &message_ids, const string &message, Promise &&promise); + void report_dialog(DialogId dialog_id, const vector &message_ids, + const tl_object_ptr &reason, const string &message, + Promise &&promise); + + void report_dialog_photo(DialogId dialog_id, FileId file_id, const tl_object_ptr &reason, + const string &message, Promise &&promise); void on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings, bool ignore_privacy_exception = false); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 1f2131314..0928d5020 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6973,9 +6973,16 @@ void Td::on_request(uint64 id, td_api::reportChat &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.text_); CREATE_OK_REQUEST_PROMISE(); - messages_manager_->report_dialog(DialogId(request.chat_id_), request.reason_, - MessagesManager::get_message_ids(request.message_ids_), request.text_, - std::move(promise)); + messages_manager_->report_dialog(DialogId(request.chat_id_), MessagesManager::get_message_ids(request.message_ids_), + request.reason_, request.text_, std::move(promise)); +} + +void Td::on_request(uint64 id, td_api::reportChatPhoto &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.text_); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->report_dialog_photo(DialogId(request.chat_id_), FileId(request.file_id_, 0), request.reason_, + request.text_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatStatisticsUrl &request) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index d3672bbac..95c543cac 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -978,6 +978,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::reportChat &request); + void on_request(uint64 id, td_api::reportChatPhoto &request); + void on_request(uint64 id, td_api::getChatStatisticsUrl &request); void on_request(uint64 id, const td_api::getChatStatistics &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index dde219695..40813c946 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2267,7 +2267,7 @@ class CliClient final : public Actor { } else if (op == "gatss") { send_request(td_api::make_object(as_file_id(args))); } else if (op == "storage") { - send_request(td_api::make_object(as_limit(args))); + send_request(td_api::make_object(to_integer(args))); } else if (op == "storage_fast") { send_request(td_api::make_object()); } else if (op == "database") { @@ -2544,7 +2544,7 @@ class CliClient final : public Actor { int32 max_file_id = as_file_id(file_id); int32 min_file_id = (op == "dff" ? 1 : max_file_id); for (int32 i = min_file_id; i <= max_file_id; i++) { - send_request(td_api::make_object(i, priority, offset, as_limit(limit), op == "dfs")); + send_request(td_api::make_object(i, priority, offset, to_integer(limit), op == "dfs")); } } else if (op == "cdf") { send_request(td_api::make_object(as_file_id(args), false)); @@ -3907,12 +3907,20 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "rc") { string chat_id; - string reason; string message_ids; + string reason; string text; - get_args(args, chat_id, reason, message_ids, text); - send_request(td_api::make_object(as_chat_id(chat_id), get_chat_report_reason(reason), - as_message_ids(message_ids), text)); + get_args(args, chat_id, message_ids, reason, text); + send_request(td_api::make_object(as_chat_id(chat_id), as_message_ids(message_ids), + get_chat_report_reason(reason), text)); + } else if (op == "rcp") { + string chat_id; + string file_id; + string reason; + string text; + get_args(args, chat_id, file_id, reason, text); + send_request(td_api::make_object(as_chat_id(chat_id), as_file_id(file_id), + get_chat_report_reason(reason), text)); } else if (op == "gcsu") { string chat_id; string parameters; From c67a0d9d7bdad9d3dd4d7208a26de6a5ba019f09 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 20 Feb 2021 03:55:09 +0300 Subject: [PATCH 190/232] Add class ReportReason. --- CMakeLists.txt | 2 + td/telegram/MessagesManager.cpp | 104 +++++--------------------------- td/telegram/MessagesManager.h | 7 +-- td/telegram/ReportReason.cpp | 97 +++++++++++++++++++++++++++++ td/telegram/ReportReason.h | 48 +++++++++++++++ td/telegram/Td.cpp | 17 ++++-- td/telegram/cli.cpp | 6 +- 7 files changed, 181 insertions(+), 100 deletions(-) create mode 100644 td/telegram/ReportReason.cpp create mode 100644 td/telegram/ReportReason.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ff8afb9c..3faec5b54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -372,6 +372,7 @@ set(TDLIB_SOURCE td/telegram/PollManager.cpp td/telegram/QueryCombiner.cpp td/telegram/ReplyMarkup.cpp + td/telegram/ReportReason.cpp td/telegram/RestrictionReason.cpp td/telegram/SecretChatActor.cpp td/telegram/SecretChatDb.cpp @@ -566,6 +567,7 @@ set(TDLIB_SOURCE td/telegram/PublicDialogType.h td/telegram/QueryCombiner.h td/telegram/ReplyMarkup.h + td/telegram/ReportReason.h td/telegram/RequestActor.h td/telegram/RestrictionReason.h td/telegram/ScheduledServerMessageId.h diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index e7974da4a..45ec84eaa 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -4388,20 +4388,19 @@ class ReportPeerQuery : public Td::ResultHandler { explicit ReportPeerQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, const vector &message_ids, - tl_object_ptr &&report_reason, const string &message) { + void send(DialogId dialog_id, const vector &message_ids, ReportReason &&report_reason) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); if (message_ids.empty()) { - send_query(G()->net_query_creator().create( - telegram_api::account_reportPeer(std::move(input_peer), std::move(report_reason), message))); + send_query(G()->net_query_creator().create(telegram_api::account_reportPeer( + std::move(input_peer), report_reason.get_input_report_reason(), report_reason.get_message()))); } else { send_query(G()->net_query_creator().create( telegram_api::messages_report(std::move(input_peer), MessagesManager::get_server_message_ids(message_ids), - std::move(report_reason), message))); + report_reason.get_input_report_reason(), report_reason.get_message()))); } } @@ -4438,15 +4437,15 @@ class ReportProfilePhotoQuery : public Td::ResultHandler { explicit ReportProfilePhotoQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, tl_object_ptr &&input_photo, - tl_object_ptr &&report_reason, const string &message) { + void send(DialogId dialog_id, tl_object_ptr &&input_photo, ReportReason &&report_reason) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create(telegram_api::account_reportProfilePhoto( - std::move(input_peer), std::move(input_photo), std::move(report_reason), message))); + std::move(input_peer), std::move(input_photo), report_reason.get_input_report_reason(), + report_reason.get_message()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -8209,8 +8208,7 @@ bool MessagesManager::can_report_dialog(DialogId dialog_id) const { } } -void MessagesManager::report_dialog(DialogId dialog_id, const vector &message_ids, - const tl_object_ptr &reason, const string &message, +void MessagesManager::report_dialog(DialogId dialog_id, const vector &message_ids, ReportReason &&reason, Promise &&promise) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -8221,14 +8219,10 @@ void MessagesManager::report_dialog(DialogId dialog_id, const vector return promise.set_error(Status::Error(3, "Can't access the chat")); } - if (reason == nullptr) { - return promise.set_error(Status::Error(3, "Reason must be non-empty")); - } - Dialog *user_d = d; bool is_dialog_spam_report = false; bool can_report_spam = d->can_report_spam; - if (reason->get_id() == td_api::chatReportReasonSpam::ID && message_ids.empty()) { + if (reason.is_spam() && message_ids.empty()) { // report from action bar if (dialog_id.get_type() == DialogType::SecretChat) { auto user_dialog_id = DialogId(td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id())); @@ -8267,46 +8261,14 @@ void MessagesManager::report_dialog(DialogId dialog_id, const vector } } - tl_object_ptr report_reason; - switch (reason->get_id()) { - case td_api::chatReportReasonSpam::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonViolence::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonPornography::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonChildAbuse::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonCopyright::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonFake::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonUnrelatedLocation::ID: - report_reason = make_tl_object(); - if (dialog_id.get_type() == DialogType::Channel) { - hide_dialog_action_bar(d); - } - break; - case td_api::chatReportReasonCustom::ID: - report_reason = make_tl_object(); - break; - default: - UNREACHABLE(); + if (dialog_id.get_type() == DialogType::Channel && reason.is_unrelated_location()) { + hide_dialog_action_bar(d); } - CHECK(report_reason != nullptr); - td_->create_handler(std::move(promise)) - ->send(dialog_id, server_message_ids, std::move(report_reason), message); + td_->create_handler(std::move(promise))->send(dialog_id, server_message_ids, std::move(reason)); } -void MessagesManager::report_dialog_photo(DialogId dialog_id, FileId file_id, - const tl_object_ptr &reason, const string &message, +void MessagesManager::report_dialog_photo(DialogId dialog_id, FileId file_id, ReportReason &&reason, Promise &&promise) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -8317,10 +8279,6 @@ void MessagesManager::report_dialog_photo(DialogId dialog_id, FileId file_id, return promise.set_error(Status::Error(3, "Can't access the chat")); } - if (reason == nullptr) { - return promise.set_error(Status::Error(3, "Reason must be non-empty")); - } - if (!can_report_dialog(dialog_id)) { return promise.set_error(Status::Error(3, "Chat photo can't be reported")); } @@ -8334,42 +8292,8 @@ void MessagesManager::report_dialog_photo(DialogId dialog_id, FileId file_id, return promise.set_error(Status::Error(400, "Only full chat photos can be reported")); } - tl_object_ptr report_reason; - switch (reason->get_id()) { - case td_api::chatReportReasonSpam::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonViolence::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonPornography::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonChildAbuse::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonCopyright::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonFake::ID: - report_reason = make_tl_object(); - break; - case td_api::chatReportReasonUnrelatedLocation::ID: - report_reason = make_tl_object(); - if (dialog_id.get_type() == DialogType::Channel) { - hide_dialog_action_bar(d); - } - break; - case td_api::chatReportReasonCustom::ID: - report_reason = make_tl_object(); - break; - default: - UNREACHABLE(); - } - CHECK(report_reason != nullptr); - td_->create_handler(std::move(promise)) - ->send(dialog_id, file_view.remote_location().as_input_photo(), std::move(report_reason), message); + ->send(dialog_id, file_view.remote_location().as_input_photo(), std::move(reason)); } void MessagesManager::on_get_peer_settings(DialogId dialog_id, diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 5078f5795..50f6f077e 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -41,6 +41,7 @@ #include "td/telegram/NotificationId.h" #include "td/telegram/NotificationSettings.h" #include "td/telegram/ReplyMarkup.h" +#include "td/telegram/ReportReason.h" #include "td/telegram/RestrictionReason.h" #include "td/telegram/ScheduledServerMessageId.h" #include "td/telegram/SecretChatId.h" @@ -775,12 +776,10 @@ class MessagesManager : public Actor { void reget_dialog_action_bar(DialogId dialog_id, const char *source); - void report_dialog(DialogId dialog_id, const vector &message_ids, - const tl_object_ptr &reason, const string &message, + void report_dialog(DialogId dialog_id, const vector &message_ids, ReportReason &&reason, Promise &&promise); - void report_dialog_photo(DialogId dialog_id, FileId file_id, const tl_object_ptr &reason, - const string &message, Promise &&promise); + void report_dialog_photo(DialogId dialog_id, FileId file_id, ReportReason &&reason, Promise &&promise); void on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings, bool ignore_privacy_exception = false); diff --git a/td/telegram/ReportReason.cpp b/td/telegram/ReportReason.cpp new file mode 100644 index 000000000..5d983f67c --- /dev/null +++ b/td/telegram/ReportReason.cpp @@ -0,0 +1,97 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/ReportReason.h" + +#include "td/telegram/misc.h" + +namespace td { + +Result ReportReason::get_report_reason(td_api::object_ptr reason, + string &&message) { + if (!clean_input_string(message)) { + return Status::Error(400, "Report text must be encoded in UTF-8"); + } + if (reason == nullptr) { + return Status::Error(400, "Reason must be non-empty"); + } + + auto type = [&] { + switch (reason->get_id()) { + case td_api::chatReportReasonSpam::ID: + return ReportReason::Type::Spam; + case td_api::chatReportReasonViolence::ID: + return ReportReason::Type::Violence; + case td_api::chatReportReasonPornography::ID: + return ReportReason::Type::Pornography; + case td_api::chatReportReasonChildAbuse::ID: + return ReportReason::Type::ChildAbuse; + case td_api::chatReportReasonCopyright::ID: + return ReportReason::Type::Copyright; + case td_api::chatReportReasonUnrelatedLocation::ID: + return ReportReason::Type::UnrelatedLocation; + case td_api::chatReportReasonFake::ID: + return ReportReason::Type::Fake; + case td_api::chatReportReasonCustom::ID: + return ReportReason::Type::Custom; + default: + UNREACHABLE(); + return ReportReason::Type::Custom; + } + }(); + return ReportReason(type, std::move(message)); +} + +tl_object_ptr ReportReason::get_input_report_reason() const { + switch (type_) { + case ReportReason::Type::Spam: + return make_tl_object(); + case ReportReason::Type::Violence: + return make_tl_object(); + case ReportReason::Type::Pornography: + return make_tl_object(); + case ReportReason::Type::ChildAbuse: + return make_tl_object(); + case ReportReason::Type::Copyright: + return make_tl_object(); + case ReportReason::Type::UnrelatedLocation: + return make_tl_object(); + case ReportReason::Type::Fake: + return make_tl_object(); + case ReportReason::Type::Custom: + return make_tl_object(); + default: + UNREACHABLE(); + return nullptr; + } +} + +StringBuilder &operator<<(StringBuilder &string_builder, const ReportReason &report_reason) { + string_builder << "ReportReason"; + switch (report_reason.type_) { + case ReportReason::Type::Spam: + return string_builder << "Spam"; + case ReportReason::Type::Violence: + return string_builder << "Violence"; + case ReportReason::Type::Pornography: + return string_builder << "Pornography"; + case ReportReason::Type::ChildAbuse: + return string_builder << "ChildAbuse"; + case ReportReason::Type::Copyright: + return string_builder << "Copyright"; + case ReportReason::Type::UnrelatedLocation: + return string_builder << "UnrelatedLocation"; + case ReportReason::Type::Fake: + return string_builder << "Fake"; + case ReportReason::Type::Custom: + return string_builder << "Custom"; + default: + UNREACHABLE(); + } + return string_builder; +} + +} // namespace td diff --git a/td/telegram/ReportReason.h b/td/telegram/ReportReason.h new file mode 100644 index 000000000..edccf287e --- /dev/null +++ b/td/telegram/ReportReason.h @@ -0,0 +1,48 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" +#include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class ReportReason { + enum class Type : int32 { Spam, Violence, Pornography, ChildAbuse, Copyright, UnrelatedLocation, Fake, Custom }; + Type type_ = Type::Spam; + string message_; + + friend StringBuilder &operator<<(StringBuilder &string_builder, const ReportReason &report_reason); + + ReportReason(Type type, string &&message) : type_(type), message_(std::move(message)) { + } + + public: + static Result get_report_reason(td_api::object_ptr reason, string &&message); + + tl_object_ptr get_input_report_reason() const; + + const string &get_message() const { + return message_; + } + + bool is_spam() const { + return type_ == Type::Spam; + } + + bool is_unrelated_location() const { + return type_ == Type::UnrelatedLocation; + } +}; + +StringBuilder &operator<<(StringBuilder &string_builder, const ReportReason &report_reason); + +} // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 0928d5020..c2c5f24a3 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -80,6 +80,7 @@ #include "td/telegram/PollManager.h" #include "td/telegram/PrivacyManager.h" #include "td/telegram/PublicDialogType.h" +#include "td/telegram/ReportReason.h" #include "td/telegram/RequestActor.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/SecretChatsManager.h" @@ -6971,18 +6972,24 @@ void Td::on_request(uint64 id, const td_api::removeChatActionBar &request) { void Td::on_request(uint64 id, td_api::reportChat &request) { CHECK_IS_USER(); - CLEAN_INPUT_STRING(request.text_); + auto r_report_reason = ReportReason::get_report_reason(std::move(request.reason_), std::move(request.text_)); + if (r_report_reason.is_error()) { + return send_error_raw(id, r_report_reason.error().code(), r_report_reason.error().message()); + } CREATE_OK_REQUEST_PROMISE(); messages_manager_->report_dialog(DialogId(request.chat_id_), MessagesManager::get_message_ids(request.message_ids_), - request.reason_, request.text_, std::move(promise)); + r_report_reason.move_as_ok(), std::move(promise)); } void Td::on_request(uint64 id, td_api::reportChatPhoto &request) { CHECK_IS_USER(); - CLEAN_INPUT_STRING(request.text_); + auto r_report_reason = ReportReason::get_report_reason(std::move(request.reason_), std::move(request.text_)); + if (r_report_reason.is_error()) { + return send_error_raw(id, r_report_reason.error().code(), r_report_reason.error().message()); + } CREATE_OK_REQUEST_PROMISE(); - messages_manager_->report_dialog_photo(DialogId(request.chat_id_), FileId(request.file_id_, 0), request.reason_, - request.text_, std::move(promise)); + messages_manager_->report_dialog_photo(DialogId(request.chat_id_), FileId(request.file_id_, 0), + r_report_reason.move_as_ok(), std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatStatisticsUrl &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 40813c946..38d233285 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1317,6 +1317,9 @@ class CliClient final : public Actor { static td_api::object_ptr get_chat_report_reason(MutableSlice reason) { reason = trim(reason); + if (reason == "null") { + return nullptr; + } if (reason == "spam") { return td_api::make_object(); } @@ -2544,7 +2547,8 @@ class CliClient final : public Actor { int32 max_file_id = as_file_id(file_id); int32 min_file_id = (op == "dff" ? 1 : max_file_id); for (int32 i = min_file_id; i <= max_file_id; i++) { - send_request(td_api::make_object(i, priority, offset, to_integer(limit), op == "dfs")); + send_request( + td_api::make_object(i, priority, offset, to_integer(limit), op == "dfs")); } } else if (op == "cdf") { send_request(td_api::make_object(as_file_id(args), false)); From de71870e83595b3916d5fd98298f467521f0b8aa Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 20 Feb 2021 21:06:58 +0300 Subject: [PATCH 191/232] Repair file_reference in reportChatPhoto. --- td/telegram/MessagesManager.cpp | 33 ++++++++++++++++++++++++++++----- td/telegram/ReportReason.h | 2 ++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 45ec84eaa..8e67d590b 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -4432,20 +4432,27 @@ class ReportPeerQuery : public Td::ResultHandler { class ReportProfilePhotoQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; + FileId file_id_; + string file_reference_; + ReportReason report_reason_; public: explicit ReportProfilePhotoQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, tl_object_ptr &&input_photo, ReportReason &&report_reason) { + void send(DialogId dialog_id, FileId file_id, tl_object_ptr &&input_photo, + ReportReason &&report_reason) { dialog_id_ = dialog_id; + file_id_ = file_id; + file_reference_ = FileManager::extract_file_reference(input_photo); + report_reason_ = std::move(report_reason); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create(telegram_api::account_reportProfilePhoto( - std::move(input_peer), std::move(input_photo), report_reason.get_input_report_reason(), - report_reason.get_message()))); + std::move(input_peer), std::move(input_photo), report_reason_.get_input_report_reason(), + report_reason_.get_message()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -4464,7 +4471,23 @@ class ReportProfilePhotoQuery : public Td::ResultHandler { void on_error(uint64 id, Status status) override { LOG(INFO) << "Receive error for report chat photo: " << status; - // TODO support FILE_REFERENCE errors + if (!td->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) { + VLOG(file_references) << "Receive " << status << " for " << file_id_; + td->file_manager_->delete_file_reference(file_id_, file_reference_); + td->file_reference_manager_->repair_file_reference( + file_id_, + PromiseCreator::lambda([dialog_id = dialog_id_, file_id = file_id_, report_reason = std::move(report_reason_), + promise = std::move(promise_)](Result result) mutable { + if (result.is_error()) { + LOG(INFO) << "Reported photo " << file_id << " is likely to be deleted"; + return promise.set_value(Unit()); + } + send_closure(G()->messages_manager(), &MessagesManager::report_dialog_photo, dialog_id, file_id, + std::move(report_reason), std::move(promise)); + })); + return; + } + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ReportProfilePhotoQuery"); promise_.set_error(std::move(status)); } @@ -8293,7 +8316,7 @@ void MessagesManager::report_dialog_photo(DialogId dialog_id, FileId file_id, Re } td_->create_handler(std::move(promise)) - ->send(dialog_id, file_view.remote_location().as_input_photo(), std::move(reason)); + ->send(dialog_id, file_id, file_view.remote_location().as_input_photo(), std::move(reason)); } void MessagesManager::on_get_peer_settings(DialogId dialog_id, diff --git a/td/telegram/ReportReason.h b/td/telegram/ReportReason.h index edccf287e..43410d62b 100644 --- a/td/telegram/ReportReason.h +++ b/td/telegram/ReportReason.h @@ -26,6 +26,8 @@ class ReportReason { } public: + ReportReason() = default; + static Result get_report_reason(td_api::object_ptr reason, string &&message); tl_object_ptr get_input_report_reason() const; From 65e7a271a2c804ffbbbef6aa766086eaa3350d05 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 20 Feb 2021 21:21:06 +0300 Subject: [PATCH 192/232] Add variables for old/new content type. --- td/telegram/MessagesManager.cpp | 71 ++++++++++++++------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 8e67d590b..f59adcc02 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -33010,19 +33010,20 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr DialogId dialog_id = d->dialog_id; MessageId message_id = old_message->message_id; + auto old_content_type = old_message->content->get_type(); + auto new_content_type = new_message->content->get_type(); bool is_scheduled = message_id.is_scheduled(); bool need_send_update = false; - bool is_new_available = new_message->content->get_type() != MessageContentType::ChatDeleteHistory; + bool is_new_available = new_content_type != MessageContentType::ChatDeleteHistory; bool replace_legacy = (old_message->legacy_layer != 0 && (new_message->legacy_layer == 0 || old_message->legacy_layer < new_message->legacy_layer)) || - old_message->content->get_type() == MessageContentType::Unsupported; + old_content_type == MessageContentType::Unsupported; bool was_visible_message_reply_info = is_visible_message_reply_info(dialog_id, old_message); if (old_message->date != new_message->date) { if (new_message->date > 0) { LOG_IF(ERROR, !is_scheduled && !new_message->is_outgoing && dialog_id != get_my_dialog_id()) << "Date has changed for incoming " << message_id << " in " << dialog_id << " from " << old_message->date - << " to " << new_message->date << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << " to " << new_message->date << ", message content type is " << old_content_type << '/' << new_content_type; CHECK(old_message->date > 0); LOG(DEBUG) << "Message date has changed from " << old_message->date << " to " << new_message->date; old_message->date = new_message->date; @@ -33032,8 +33033,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr need_send_update = true; } else { LOG(ERROR) << "Receive " << message_id << " in " << dialog_id << " with wrong date " << new_message->date - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } } if (old_message->date == old_message->edited_schedule_date) { @@ -33051,8 +33051,8 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr need_send_update = true; } } else { - LOG(ERROR) << "Receive " << message_id << " in " << dialog_id << " of type " << old_message->content->get_type() - << "/" << new_message->content->get_type() << " with wrong edit date " << new_message->edit_date + LOG(ERROR) << "Receive " << message_id << " in " << dialog_id << " of type " << old_content_type << "/" + << new_content_type << " with wrong edit date " << new_message->edit_date << ", old edit date = " << old_message->edit_date; } } @@ -33069,8 +33069,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr // there can be race for sent signed posts or changed anonymous flag LOG_IF(ERROR, old_message->sender_user_id != UserId() && new_message->sender_user_id != UserId()) << message_id << " in " << dialog_id << " has changed sender from " << old_message->sender_user_id << " to " - << new_message->sender_user_id << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << new_message->sender_user_id << ", message content type is " << old_content_type << '/' << new_content_type; LOG_IF(WARNING, (new_message->sender_user_id.is_valid() || old_message->author_signature.empty()) && !old_message->sender_dialog_id.is_valid() && !new_message->sender_dialog_id.is_valid()) @@ -33084,8 +33083,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr // there can be race for changed anonymous flag LOG_IF(ERROR, old_message->sender_dialog_id != DialogId() && new_message->sender_dialog_id != DialogId()) << message_id << " in " << dialog_id << " has changed sender from " << old_message->sender_dialog_id << " to " - << new_message->sender_dialog_id << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << new_message->sender_dialog_id << ", message content type is " << old_content_type << '/' << new_content_type; LOG(DEBUG) << "Change message sender"; old_message->sender_dialog_id = new_message->sender_dialog_id; @@ -33096,8 +33094,8 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr if (!replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has received forward info " << *new_message->forward_info << ", really forwarded from " << old_message->real_forward_from_message_id << " in " - << old_message->real_forward_from_dialog_id << ", message content type is " - << old_message->content->get_type() << '/' << new_message->content->get_type(); + << old_message->real_forward_from_dialog_id << ", message content type is " << old_content_type + << '/' << new_content_type; } old_message->forward_info = std::move(new_message->forward_info); need_send_update = true; @@ -33114,8 +33112,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr LOG(ERROR) << message_id << " in " << dialog_id << " has changed forward info from " << *old_message->forward_info << " to " << *new_message->forward_info << ", really forwarded from " << old_message->real_forward_from_message_id << " in " << old_message->real_forward_from_dialog_id - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } old_message->forward_info = std::move(new_message->forward_info); need_send_update = true; @@ -33124,8 +33121,8 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr LOG(ERROR) << message_id << " in " << dialog_id << " sent by " << old_message->sender_user_id << "/" << old_message->sender_dialog_id << " has lost forward info " << *old_message->forward_info << ", really forwarded from " << old_message->real_forward_from_message_id << " in " - << old_message->real_forward_from_dialog_id << ", message content type is " - << old_message->content->get_type() << '/' << new_message->content->get_type(); + << old_message->real_forward_from_dialog_id << ", message content type is " << old_content_type << '/' + << new_content_type; old_message->forward_info = nullptr; need_send_update = true; } @@ -33140,8 +33137,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr if (new_message->notification_id.is_valid()) { LOG(ERROR) << "Notification id for " << message_id << " in " << dialog_id << " has tried to change from " << old_message->notification_id << " to " << new_message->notification_id - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } } else { CHECK(new_message->notification_id.is_valid()); @@ -33174,8 +33170,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr } else if (is_new_available) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed message it is reply to from " << old_message->reply_to_message_id << " to " << new_message->reply_to_message_id - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } } if (old_message->reply_in_dialog_id != new_message->reply_in_dialog_id) { @@ -33186,8 +33181,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr } else if (is_new_available && old_message->reply_in_dialog_id.is_valid()) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed dialog it is reply in from " << old_message->reply_in_dialog_id << " to " << new_message->reply_in_dialog_id - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } } if (old_message->top_thread_message_id != new_message->top_thread_message_id) { @@ -33199,8 +33193,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr } else if (is_new_available) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed message thread from " << old_message->top_thread_message_id << " to " << new_message->top_thread_message_id - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } } if (old_message->via_bot_user_id != new_message->via_bot_user_id) { @@ -33208,8 +33201,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr !replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed bot via it is sent from " << old_message->via_bot_user_id << " to " << new_message->via_bot_user_id - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } LOG(DEBUG) << "Change message via_bot from " << old_message->via_bot_user_id << " to " << new_message->via_bot_user_id; @@ -33224,25 +33216,24 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr if (old_message->is_outgoing != new_message->is_outgoing && is_new_available) { if (!replace_legacy && !(message_id.is_scheduled() && dialog_id == get_my_dialog_id())) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed is_outgoing from " << old_message->is_outgoing - << " to " << new_message->is_outgoing << ", message content type is " - << old_message->content->get_type() << '/' << new_message->content->get_type(); + << " to " << new_message->is_outgoing << ", message content type is " << old_content_type << '/' + << new_content_type; } old_message->is_outgoing = new_message->is_outgoing; need_send_update = true; } LOG_IF(ERROR, old_message->is_channel_post != new_message->is_channel_post) << message_id << " in " << dialog_id << " has changed is_channel_post from " << old_message->is_channel_post - << " to " << new_message->is_channel_post << ", message content type is " << old_message->content->get_type() - << '/' << new_message->content->get_type(); + << " to " << new_message->is_channel_post << ", message content type is " << old_content_type << '/' + << new_content_type; if (old_message->contains_mention != new_message->contains_mention) { - auto old_type = old_message->content->get_type(); - if (old_message->edit_date == 0 && is_new_available && old_type != MessageContentType::PinMessage && - old_type != MessageContentType::ExpiredPhoto && old_type != MessageContentType::ExpiredVideo && + if (old_message->edit_date == 0 && is_new_available && old_content_type != MessageContentType::PinMessage && + old_content_type != MessageContentType::ExpiredPhoto && old_content_type != MessageContentType::ExpiredVideo && !replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed contains_mention from " << old_message->contains_mention << " to " << new_message->contains_mention - << ", is_outgoing = " << old_message->is_outgoing << ", message content type is " << old_type << '/' - << new_message->content->get_type(); + << ", is_outgoing = " << old_message->is_outgoing << ", message content type is " << old_content_type + << '/' << new_content_type; } // contains_mention flag shouldn't be changed, because the message will not be added to unread mention list // and we are unable to show/hide message notification @@ -33318,14 +33309,12 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr } else { if (old_message->reply_markup == nullptr) { if (new_message->reply_markup != nullptr) { - auto content_type = old_message->content->get_type(); // MessageGame and MessageInvoice reply markup can be generated server side // some forwards retain their reply markup - if (content_type != MessageContentType::Game && content_type != MessageContentType::Invoice && + if (old_content_type != MessageContentType::Game && old_content_type != MessageContentType::Invoice && need_message_changed_warning(old_message) && !replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has received reply markup " << *new_message->reply_markup - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + << ", message content type is " << old_content_type << '/' << new_content_type; } else { LOG(DEBUG) << "Add message reply keyboard"; } From 16913bff03e6fa3f1083f1f7a9070aa8de0b8c59 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 20 Feb 2021 21:32:07 +0300 Subject: [PATCH 193/232] Improve date changed warning. --- td/telegram/MessagesManager.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index f59adcc02..8c0366879 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -33021,9 +33021,14 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr bool was_visible_message_reply_info = is_visible_message_reply_info(dialog_id, old_message); if (old_message->date != new_message->date) { if (new_message->date > 0) { - LOG_IF(ERROR, !is_scheduled && !new_message->is_outgoing && dialog_id != get_my_dialog_id()) - << "Date has changed for incoming " << message_id << " in " << dialog_id << " from " << old_message->date - << " to " << new_message->date << ", message content type is " << old_content_type << '/' << new_content_type; + if (!(is_scheduled || message_id.is_yet_unsent() || + (message_id.is_server() && message_id.get_server_message_id().get() == 1) || + old_content_type == MessageContentType::ChannelMigrateFrom || + old_content_type == MessageContentType::ChannelCreate)) { + LOG(ERROR) << "Date has changed for " << message_id << " in " << dialog_id << " from " << old_message->date + << " to " << new_message->date << ", message content type is " << old_content_type << '/' + << new_content_type; + } CHECK(old_message->date > 0); LOG(DEBUG) << "Message date has changed from " << old_message->date << " to " << new_message->date; old_message->date = new_message->date; From 6bbc251cb5d38e01dbd642cc649abe52b654329e Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 20 Feb 2021 22:01:12 +0300 Subject: [PATCH 194/232] tg_cli: treat strings as usernames. --- td/telegram/cli.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 38d233285..5128af018 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -454,12 +454,18 @@ class CliClient final : public Actor { int64 as_chat_id(Slice str) const { str = trim(str); + if (str == "me") { + return my_id_; + } if (str[0] == '@') { - auto it = username_to_user_id_.find(to_lower(str.substr(1))); + str.remove_prefix(1); + } + if (is_alpha(str[0])) { + auto it = username_to_user_id_.find(to_lower(str)); if (it != username_to_user_id_.end()) { return it->second; } - auto it2 = username_to_supergroup_id_.find(to_lower(str.substr(1))); + auto it2 = username_to_supergroup_id_.find(to_lower(str)); if (it2 != username_to_supergroup_id_.end()) { auto supergroup_id = it2->second; return static_cast(-1000'000'000'000ll) - supergroup_id; @@ -467,9 +473,6 @@ class CliClient final : public Actor { LOG(ERROR) << "Can't resolve " << str; return 0; } - if (str == "me") { - return my_id_; - } return to_integer(str); } @@ -535,17 +538,20 @@ class CliClient final : public Actor { int32 as_user_id(Slice str) const { str = trim(str); + if (str == "me") { + return my_id_; + } if (str[0] == '@') { - auto it = username_to_user_id_.find(to_lower(str.substr(1))); + str.remove_prefix(1); + } + if (is_alpha(str[0])) { + auto it = username_to_user_id_.find(to_lower(str)); if (it != username_to_user_id_.end()) { return it->second; } LOG(ERROR) << "Can't find user " << str; return 0; } - if (str == "me") { - return my_id_; - } return to_integer(str); } @@ -565,7 +571,10 @@ class CliClient final : public Actor { int32 as_supergroup_id(Slice str) { str = trim(str); if (str[0] == '@') { - return username_to_supergroup_id_[to_lower(str.substr(1))]; + str.remove_prefix(1); + } + if (is_alpha(str[0])) { + return username_to_supergroup_id_[to_lower(str)]; } auto result = to_integer(str); int64 shift = static_cast(-1000000000000ll); From c07a1dac22c14038e1703d7bebe6f0e7f1b1c97b Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 20 Feb 2021 22:20:02 +0300 Subject: [PATCH 195/232] Unify UserFull/ChatFull/ChannelFull variable names. --- td/telegram/ContactsManager.cpp | 331 ++++++++++++++++---------------- td/telegram/ContactsManager.h | 2 +- 2 files changed, 167 insertions(+), 166 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 452b99d5c..0aaef6df8 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -9800,96 +9800,96 @@ void ContactsManager::on_get_users(vector> &&u } } -void ContactsManager::on_get_user_full(tl_object_ptr &&user_full) { - UserId user_id = get_user_id(user_full->user_); +void ContactsManager::on_get_user_full(tl_object_ptr &&user) { + UserId user_id = get_user_id(user->user_); if (!user_id.is_valid()) { LOG(ERROR) << "Receive invalid " << user_id; return; } - on_get_user(std::move(user_full->user_), "on_get_user_full"); + on_get_user(std::move(user->user_), "on_get_user_full"); User *u = get_user(user_id); if (u == nullptr) { return; } - td_->messages_manager_->on_update_dialog_notify_settings(DialogId(user_id), std::move(user_full->notify_settings_), + td_->messages_manager_->on_update_dialog_notify_settings(DialogId(user_id), std::move(user->notify_settings_), "on_get_user_full"); { MessageId pinned_message_id; - if ((user_full->flags_ & USER_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { - pinned_message_id = MessageId(ServerMessageId(user_full->pinned_msg_id_)); + if ((user->flags_ & USER_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { + pinned_message_id = MessageId(ServerMessageId(user->pinned_msg_id_)); } td_->messages_manager_->on_update_dialog_last_pinned_message_id(DialogId(user_id), pinned_message_id); } { FolderId folder_id; - if ((user_full->flags_ & USER_FULL_FLAG_HAS_FOLDER_ID) != 0) { - folder_id = FolderId(user_full->folder_id_); + if ((user->flags_ & USER_FULL_FLAG_HAS_FOLDER_ID) != 0) { + folder_id = FolderId(user->folder_id_); } td_->messages_manager_->on_update_dialog_folder_id(DialogId(user_id), folder_id); } td_->messages_manager_->on_update_dialog_has_scheduled_server_messages( - DialogId(user_id), (user_full->flags_ & USER_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); + DialogId(user_id), (user->flags_ & USER_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); { MessageTtlSetting message_ttl_setting; - if ((user_full->flags_ & USER_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { - message_ttl_setting = MessageTtlSetting(user_full->ttl_period_); + if ((user->flags_ & USER_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { + message_ttl_setting = MessageTtlSetting(user->ttl_period_); } td_->messages_manager_->on_update_dialog_message_ttl_setting(DialogId(user_id), message_ttl_setting); } - UserFull *user = add_user_full(user_id); - user->expires_at = Time::now() + USER_FULL_EXPIRE_TIME; + UserFull *user_full = add_user_full(user_id); + user_full->expires_at = Time::now() + USER_FULL_EXPIRE_TIME; { - bool is_blocked = (user_full->flags_ & USER_FULL_FLAG_IS_BLOCKED) != 0; - on_update_user_full_is_blocked(user, user_id, is_blocked); + bool is_blocked = (user->flags_ & USER_FULL_FLAG_IS_BLOCKED) != 0; + on_update_user_full_is_blocked(user_full, user_id, is_blocked); td_->messages_manager_->on_update_dialog_is_blocked(DialogId(user_id), is_blocked); } - on_update_user_full_common_chat_count(user, user_id, user_full->common_chats_count_); + on_update_user_full_common_chat_count(user_full, user_id, user->common_chats_count_); on_update_user_full_need_phone_number_privacy_exception( - user, user_id, (user_full->settings_->flags_ & telegram_api::peerSettings::NEED_CONTACTS_EXCEPTION_MASK) != 0); + user_full, user_id, (user->settings_->flags_ & telegram_api::peerSettings::NEED_CONTACTS_EXCEPTION_MASK) != 0); - bool can_pin_messages = user_full->can_pin_message_; - if (user->can_pin_messages != can_pin_messages) { - user->can_pin_messages = can_pin_messages; - user->is_changed = true; + bool can_pin_messages = user->can_pin_message_; + if (user_full->can_pin_messages != can_pin_messages) { + user_full->can_pin_messages = can_pin_messages; + user_full->is_changed = true; } - bool can_be_called = user_full->phone_calls_available_ && !user_full->phone_calls_private_; - bool supports_video_calls = user_full->video_calls_available_ && !user_full->phone_calls_private_; - bool has_private_calls = user_full->phone_calls_private_; - if (user->can_be_called != can_be_called || user->supports_video_calls != supports_video_calls || - user->has_private_calls != has_private_calls || user->about != user_full->about_) { - user->can_be_called = can_be_called; - user->supports_video_calls = supports_video_calls; - user->has_private_calls = has_private_calls; - user->about = std::move(user_full->about_); + bool can_be_called = user->phone_calls_available_ && !user->phone_calls_private_; + bool supports_video_calls = user->video_calls_available_ && !user->phone_calls_private_; + bool has_private_calls = user->phone_calls_private_; + if (user_full->can_be_called != can_be_called || user_full->supports_video_calls != supports_video_calls || + user_full->has_private_calls != has_private_calls || user_full->about != user->about_) { + user_full->can_be_called = can_be_called; + user_full->supports_video_calls = supports_video_calls; + user_full->has_private_calls = has_private_calls; + user_full->about = std::move(user->about_); - user->is_changed = true; + user_full->is_changed = true; } - auto photo = get_photo(td_->file_manager_.get(), std::move(user_full->profile_photo_), DialogId(user_id)); - if (photo != user->photo) { - user->photo = std::move(photo); - user->is_changed = true; + auto photo = get_photo(td_->file_manager_.get(), std::move(user->profile_photo_), DialogId(user_id)); + if (photo != user_full->photo) { + user_full->photo = std::move(photo); + user_full->is_changed = true; } - if (user->photo.is_empty()) { + if (user_full->photo.is_empty()) { drop_user_photos(user_id, true, false, "on_get_user_full"); } else { - register_user_photo(u, user_id, user->photo); + register_user_photo(u, user_id, user_full->photo); } - if (user_full->bot_info_ != nullptr && on_update_bot_info(std::move(user_full->bot_info_), false)) { - user->need_send_update = true; + if (user->bot_info_ != nullptr && on_update_bot_info(std::move(user->bot_info_), false)) { + user_full->need_send_update = true; } - update_user_full(user, user_id); + update_user_full(user_full, user_id); // update peer settings after UserFull is created and updated to not update twice need_phone_number_privacy_exception - td_->messages_manager_->on_get_peer_settings(DialogId(user_id), std::move(user_full->settings_)); + td_->messages_manager_->on_get_peer_settings(DialogId(user_id), std::move(user->settings_)); } void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 limit, int32 total_count, @@ -10051,8 +10051,8 @@ void ContactsManager::on_get_chats(vector> &&c void ContactsManager::on_get_chat_full(tl_object_ptr &&chat_full_ptr, Promise &&promise) { LOG(INFO) << "Receive " << to_string(chat_full_ptr); if (chat_full_ptr->get_id() == telegram_api::chatFull::ID) { - auto chat_full = move_tl_object_as(chat_full_ptr); - ChatId chat_id(chat_full->id_); + auto chat = move_tl_object_as(chat_full_ptr); + ChatId chat_id(chat->id_); if (!chat_id.is_valid()) { LOG(ERROR) << "Receive invalid " << chat_id; return promise.set_value(Unit()); @@ -10060,8 +10060,8 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c { MessageId pinned_message_id; - if ((chat_full->flags_ & CHAT_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { - pinned_message_id = MessageId(ServerMessageId(chat_full->pinned_msg_id_)); + if ((chat->flags_ & CHAT_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { + pinned_message_id = MessageId(ServerMessageId(chat->pinned_msg_id_)); } Chat *c = get_chat(chat_id); if (c == nullptr) { @@ -10080,86 +10080,87 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c } { FolderId folder_id; - if ((chat_full->flags_ & CHAT_FULL_FLAG_HAS_FOLDER_ID) != 0) { - folder_id = FolderId(chat_full->folder_id_); + if ((chat->flags_ & CHAT_FULL_FLAG_HAS_FOLDER_ID) != 0) { + folder_id = FolderId(chat->folder_id_); } td_->messages_manager_->on_update_dialog_folder_id(DialogId(chat_id), folder_id); } td_->messages_manager_->on_update_dialog_has_scheduled_server_messages( - DialogId(chat_id), (chat_full->flags_ & CHAT_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); + DialogId(chat_id), (chat->flags_ & CHAT_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); { InputGroupCallId input_group_call_id; - if (chat_full->call_ != nullptr) { - input_group_call_id = InputGroupCallId(chat_full->call_); + if (chat->call_ != nullptr) { + input_group_call_id = InputGroupCallId(chat->call_); } td_->messages_manager_->on_update_dialog_group_call_id(DialogId(chat_id), input_group_call_id); } { MessageTtlSetting message_ttl_setting; - if ((chat_full->flags_ & CHAT_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { - message_ttl_setting = MessageTtlSetting(chat_full->ttl_period_); + if ((chat->flags_ & CHAT_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { + message_ttl_setting = MessageTtlSetting(chat->ttl_period_); } td_->messages_manager_->on_update_dialog_message_ttl_setting(DialogId(chat_id), message_ttl_setting); } - ChatFull *chat = add_chat_full(chat_id); - on_update_chat_full_invite_link(chat, std::move(chat_full->exported_invite_)); - on_update_chat_full_photo( - chat, chat_id, get_photo(td_->file_manager_.get(), std::move(chat_full->chat_photo_), DialogId(chat_id))); + ChatFull *chat_full = add_chat_full(chat_id); + on_update_chat_full_invite_link(chat_full, std::move(chat->exported_invite_)); + on_update_chat_full_photo(chat_full, chat_id, + get_photo(td_->file_manager_.get(), std::move(chat->chat_photo_), DialogId(chat_id))); - for (auto &bot_info : chat_full->bot_info_) { + for (auto &bot_info : chat->bot_info_) { if (on_update_bot_info(std::move(bot_info))) { - chat->need_send_update = true; + chat_full->need_send_update = true; } } - if (chat->description != chat_full->about_) { - chat->description = std::move(chat_full->about_); - chat->is_changed = true; + if (chat_full->description != chat->about_) { + chat_full->description = std::move(chat->about_); + chat_full->is_changed = true; } - if (chat->can_set_username != chat_full->can_set_username_) { - chat->can_set_username = chat_full->can_set_username_; - chat->is_changed = true; + if (chat_full->can_set_username != chat->can_set_username_) { + chat_full->can_set_username = chat->can_set_username_; + chat_full->is_changed = true; } - on_get_chat_participants(std::move(chat_full->participants_), false); - td_->messages_manager_->on_update_dialog_notify_settings(DialogId(chat_id), std::move(chat_full->notify_settings_), + on_get_chat_participants(std::move(chat->participants_), false); + td_->messages_manager_->on_update_dialog_notify_settings(DialogId(chat_id), std::move(chat->notify_settings_), "on_get_chat_full"); - update_chat_full(chat, chat_id); + update_chat_full(chat_full, chat_id); } else { CHECK(chat_full_ptr->get_id() == telegram_api::channelFull::ID); - auto channel_full = move_tl_object_as(chat_full_ptr); - ChannelId channel_id(channel_full->id_); + auto channel = move_tl_object_as(chat_full_ptr); + ChannelId channel_id(channel->id_); if (!channel_id.is_valid()) { LOG(ERROR) << "Receive invalid " << channel_id; return promise.set_value(Unit()); } if (!G()->close_flag()) { - auto channel = get_channel_full(channel_id, "on_get_channel_full"); - if (channel != nullptr) { - if (channel->repair_request_version != 0 && channel->repair_request_version < channel->speculative_version) { - LOG(INFO) << "Receive ChannelFull with request version " << channel->repair_request_version - << ", but current speculative version is " << channel->speculative_version; + auto channel_full = get_channel_full(channel_id, "on_get_channel_full"); + if (channel_full != nullptr) { + if (channel_full->repair_request_version != 0 && + channel_full->repair_request_version < channel_full->speculative_version) { + LOG(INFO) << "Receive ChannelFull with request version " << channel_full->repair_request_version + << ", but current speculative version is " << channel_full->speculative_version; - channel->repair_request_version = channel->speculative_version; + channel_full->repair_request_version = channel_full->speculative_version; auto input_channel = get_input_channel(channel_id); CHECK(input_channel != nullptr); td_->create_handler(std::move(promise))->send(channel_id, std::move(input_channel)); return; } - channel->repair_request_version = 0; + channel_full->repair_request_version = 0; } } - td_->messages_manager_->on_update_dialog_notify_settings( - DialogId(channel_id), std::move(channel_full->notify_settings_), "on_get_channel_full"); + td_->messages_manager_->on_update_dialog_notify_settings(DialogId(channel_id), std::move(channel->notify_settings_), + "on_get_channel_full"); { MessageTtlSetting message_ttl_setting; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { - message_ttl_setting = MessageTtlSetting(channel_full->ttl_period_); + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { + message_ttl_setting = MessageTtlSetting(channel->ttl_period_); } td_->messages_manager_->on_update_dialog_message_ttl_setting(DialogId(channel_id), message_ttl_setting); } @@ -10170,119 +10171,119 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c return promise.set_value(Unit()); } - ChannelFull *channel = add_channel_full(channel_id); + ChannelFull *channel_full = add_channel_full(channel_id); - bool have_participant_count = (channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT) != 0; - auto participant_count = have_participant_count ? channel_full->participants_count_ : channel->participant_count; + bool have_participant_count = (channel->flags_ & CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT) != 0; + auto participant_count = have_participant_count ? channel->participants_count_ : channel_full->participant_count; auto administrator_count = 0; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT) != 0) { - administrator_count = channel_full->admins_count_; + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT) != 0) { + administrator_count = channel->admins_count_; } else if (c->is_megagroup || c->status.is_administrator()) { // in megagroups and administered channels don't drop known number of administrators - administrator_count = channel->administrator_count; + administrator_count = channel_full->administrator_count; } if (participant_count < administrator_count) { participant_count = administrator_count; } - auto restricted_count = - (channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_BANNED_COUNT) != 0 ? channel_full->banned_count_ : 0; - auto banned_count = - (channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_BANNED_COUNT) != 0 ? channel_full->kicked_count_ : 0; - auto can_get_participants = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_GET_PARTICIPANTS) != 0; - auto can_set_username = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_SET_USERNAME) != 0; - auto can_set_sticker_set = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_SET_STICKER_SET) != 0; - auto can_set_location = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_SET_LOCATION) != 0; - auto is_all_history_available = (channel_full->flags_ & CHANNEL_FULL_FLAG_IS_ALL_HISTORY_HIDDEN) == 0; - auto can_view_statistics = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_VIEW_STATISTICS) != 0; + auto restricted_count = (channel->flags_ & CHANNEL_FULL_FLAG_HAS_BANNED_COUNT) != 0 ? channel->banned_count_ : 0; + auto banned_count = (channel->flags_ & CHANNEL_FULL_FLAG_HAS_BANNED_COUNT) != 0 ? channel->kicked_count_ : 0; + auto can_get_participants = (channel->flags_ & CHANNEL_FULL_FLAG_CAN_GET_PARTICIPANTS) != 0; + auto can_set_username = (channel->flags_ & CHANNEL_FULL_FLAG_CAN_SET_USERNAME) != 0; + auto can_set_sticker_set = (channel->flags_ & CHANNEL_FULL_FLAG_CAN_SET_STICKER_SET) != 0; + auto can_set_location = (channel->flags_ & CHANNEL_FULL_FLAG_CAN_SET_LOCATION) != 0; + auto is_all_history_available = (channel->flags_ & CHANNEL_FULL_FLAG_IS_ALL_HISTORY_HIDDEN) == 0; + auto can_view_statistics = (channel->flags_ & CHANNEL_FULL_FLAG_CAN_VIEW_STATISTICS) != 0; StickerSetId sticker_set_id; - if (channel_full->stickerset_ != nullptr) { + if (channel->stickerset_ != nullptr) { sticker_set_id = - td_->stickers_manager_->on_get_sticker_set(std::move(channel_full->stickerset_), true, "on_get_channel_full"); + td_->stickers_manager_->on_get_sticker_set(std::move(channel->stickerset_), true, "on_get_channel_full"); } DcId stats_dc_id; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_STATISTICS_DC_ID) != 0) { - stats_dc_id = DcId::create(channel_full->stats_dc_); + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_STATISTICS_DC_ID) != 0) { + stats_dc_id = DcId::create(channel->stats_dc_); } if (!stats_dc_id.is_exact() && can_view_statistics) { LOG(ERROR) << "Receive can_view_statistics == true, but invalid statistics DC ID in " << channel_id; can_view_statistics = false; } - channel->repair_request_version = 0; - channel->expires_at = Time::now() + CHANNEL_FULL_EXPIRE_TIME; - if (channel->description != channel_full->about_ || channel->participant_count != participant_count || - channel->administrator_count != administrator_count || channel->restricted_count != restricted_count || - channel->banned_count != banned_count || channel->can_get_participants != can_get_participants || - channel->can_set_username != can_set_username || channel->can_set_sticker_set != can_set_sticker_set || - channel->can_set_location != can_set_location || channel->can_view_statistics != can_view_statistics || - channel->stats_dc_id != stats_dc_id || channel->sticker_set_id != sticker_set_id || - channel->is_all_history_available != is_all_history_available) { - channel->description = std::move(channel_full->about_); - channel->participant_count = participant_count; - channel->administrator_count = administrator_count; - channel->restricted_count = restricted_count; - channel->banned_count = banned_count; - channel->can_get_participants = can_get_participants; - channel->can_set_username = can_set_username; - channel->can_set_sticker_set = can_set_sticker_set; - channel->can_set_location = can_set_location; - channel->can_view_statistics = can_view_statistics; - channel->stats_dc_id = stats_dc_id; - channel->is_all_history_available = is_all_history_available; - channel->sticker_set_id = sticker_set_id; + channel_full->repair_request_version = 0; + channel_full->expires_at = Time::now() + CHANNEL_FULL_EXPIRE_TIME; + if (channel_full->description != channel->about_ || channel_full->participant_count != participant_count || + channel_full->administrator_count != administrator_count || + channel_full->restricted_count != restricted_count || channel_full->banned_count != banned_count || + channel_full->can_get_participants != can_get_participants || + channel_full->can_set_username != can_set_username || + channel_full->can_set_sticker_set != can_set_sticker_set || + channel_full->can_set_location != can_set_location || + channel_full->can_view_statistics != can_view_statistics || channel_full->stats_dc_id != stats_dc_id || + channel_full->sticker_set_id != sticker_set_id || + channel_full->is_all_history_available != is_all_history_available) { + channel_full->description = std::move(channel->about_); + channel_full->participant_count = participant_count; + channel_full->administrator_count = administrator_count; + channel_full->restricted_count = restricted_count; + channel_full->banned_count = banned_count; + channel_full->can_get_participants = can_get_participants; + channel_full->can_set_username = can_set_username; + channel_full->can_set_sticker_set = can_set_sticker_set; + channel_full->can_set_location = can_set_location; + channel_full->can_view_statistics = can_view_statistics; + channel_full->stats_dc_id = stats_dc_id; + channel_full->is_all_history_available = is_all_history_available; + channel_full->sticker_set_id = sticker_set_id; - channel->is_changed = true; + channel_full->is_changed = true; } if (have_participant_count && c->participant_count != participant_count) { c->participant_count = participant_count; c->is_changed = true; update_channel(c, channel_id); } - if (!channel->is_can_view_statistics_inited) { - channel->is_can_view_statistics_inited = true; - channel->need_save_to_database = true; + if (!channel_full->is_can_view_statistics_inited) { + channel_full->is_can_view_statistics_inited = true; + channel_full->need_save_to_database = true; } on_update_channel_full_photo( - channel, channel_id, - get_photo(td_->file_manager_.get(), std::move(channel_full->chat_photo_), DialogId(channel_id))); + channel_full, channel_id, + get_photo(td_->file_manager_.get(), std::move(channel->chat_photo_), DialogId(channel_id))); td_->messages_manager_->on_read_channel_outbox(channel_id, - MessageId(ServerMessageId(channel_full->read_outbox_max_id_))); - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_AVAILABLE_MIN_MESSAGE_ID) != 0) { + MessageId(ServerMessageId(channel->read_outbox_max_id_))); + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_AVAILABLE_MIN_MESSAGE_ID) != 0) { td_->messages_manager_->on_update_channel_max_unavailable_message_id( - channel_id, MessageId(ServerMessageId(channel_full->available_min_id_))); + channel_id, MessageId(ServerMessageId(channel->available_min_id_))); } - td_->messages_manager_->on_read_channel_inbox(channel_id, - MessageId(ServerMessageId(channel_full->read_inbox_max_id_)), - channel_full->unread_count_, channel_full->pts_, "ChannelFull"); + td_->messages_manager_->on_read_channel_inbox(channel_id, MessageId(ServerMessageId(channel->read_inbox_max_id_)), + channel->unread_count_, channel->pts_, "ChannelFull"); - on_update_channel_full_invite_link(channel, std::move(channel_full->exported_invite_)); + on_update_channel_full_invite_link(channel_full, std::move(channel->exported_invite_)); { - auto is_blocked = (channel_full->flags_ & CHANNEL_FULL_FLAG_IS_BLOCKED) != 0; + auto is_blocked = (channel->flags_ & CHANNEL_FULL_FLAG_IS_BLOCKED) != 0; td_->messages_manager_->on_update_dialog_is_blocked(DialogId(channel_id), is_blocked); } { MessageId pinned_message_id; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { - pinned_message_id = MessageId(ServerMessageId(channel_full->pinned_msg_id_)); + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { + pinned_message_id = MessageId(ServerMessageId(channel->pinned_msg_id_)); } td_->messages_manager_->on_update_dialog_last_pinned_message_id(DialogId(channel_id), pinned_message_id); } { FolderId folder_id; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_FOLDER_ID) != 0) { - folder_id = FolderId(channel_full->folder_id_); + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_FOLDER_ID) != 0) { + folder_id = FolderId(channel->folder_id_); } td_->messages_manager_->on_update_dialog_folder_id(DialogId(channel_id), folder_id); } td_->messages_manager_->on_update_dialog_has_scheduled_server_messages( - DialogId(channel_id), (channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); + DialogId(channel_id), (channel->flags_ & CHANNEL_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); { InputGroupCallId input_group_call_id; - if (channel_full->call_ != nullptr) { - input_group_call_id = InputGroupCallId(channel_full->call_); + if (channel->call_ != nullptr) { + input_group_call_id = InputGroupCallId(channel->call_); if (input_group_call_id.is_valid() && !c->is_megagroup) { LOG(ERROR) << "Receive " << input_group_call_id << " in " << channel_id; input_group_call_id = InputGroupCallId(); @@ -10293,14 +10294,14 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c if (participant_count >= 190) { int32 online_member_count = 0; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_ONLINE_MEMBER_COUNT) != 0) { - online_member_count = channel_full->online_count_; + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_ONLINE_MEMBER_COUNT) != 0) { + online_member_count = channel->online_count_; } td_->messages_manager_->on_update_dialog_online_member_count(DialogId(channel_id), online_member_count, true); } vector bot_user_ids; - for (auto &bot_info : channel_full->bot_info_) { + for (auto &bot_info : channel->bot_info_) { UserId user_id(bot_info->user_id_); if (!is_user_bot(user_id)) { continue; @@ -10309,11 +10310,11 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c bot_user_ids.push_back(user_id); on_update_bot_info(std::move(bot_info)); } - on_update_channel_full_bot_user_ids(channel, channel_id, std::move(bot_user_ids)); + on_update_channel_full_bot_user_ids(channel_full, channel_id, std::move(bot_user_ids)); ChannelId linked_channel_id; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_LINKED_CHANNEL_ID) != 0) { - linked_channel_id = ChannelId(channel_full->linked_chat_id_); + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_LINKED_CHANNEL_ID) != 0) { + linked_channel_id = ChannelId(channel->linked_chat_id_); auto linked_channel = get_channel_force(linked_channel_id); if (linked_channel == nullptr || c->is_megagroup == linked_channel->is_megagroup || channel_id == linked_channel_id) { @@ -10321,38 +10322,38 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c linked_channel_id = ChannelId(); } } - on_update_channel_full_linked_channel_id(channel, channel_id, linked_channel_id); + on_update_channel_full_linked_channel_id(channel_full, channel_id, linked_channel_id); - on_update_channel_full_location(channel, channel_id, DialogLocation(std::move(channel_full->location_))); + on_update_channel_full_location(channel_full, channel_id, DialogLocation(std::move(channel->location_))); if (c->is_megagroup) { int32 slow_mode_delay = 0; int32 slow_mode_next_send_date = 0; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_SLOW_MODE_DELAY) != 0) { - slow_mode_delay = channel_full->slowmode_seconds_; + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_SLOW_MODE_DELAY) != 0) { + slow_mode_delay = channel->slowmode_seconds_; } - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_SLOW_MODE_NEXT_SEND_DATE) != 0) { - slow_mode_next_send_date = channel_full->slowmode_next_send_date_; + if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_SLOW_MODE_NEXT_SEND_DATE) != 0) { + slow_mode_next_send_date = channel->slowmode_next_send_date_; } - on_update_channel_full_slow_mode_delay(channel, channel_id, slow_mode_delay, slow_mode_next_send_date); + on_update_channel_full_slow_mode_delay(channel_full, channel_id, slow_mode_delay, slow_mode_next_send_date); } ChatId migrated_from_chat_id; MessageId migrated_from_max_message_id; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_MIGRATED_FROM) != 0) { - migrated_from_chat_id = ChatId(channel_full->migrated_from_chat_id_); - migrated_from_max_message_id = MessageId(ServerMessageId(channel_full->migrated_from_max_id_)); + if ((channel->flags_ & CHANNEL_FULL_FLAG_MIGRATED_FROM) != 0) { + migrated_from_chat_id = ChatId(channel->migrated_from_chat_id_); + migrated_from_max_message_id = MessageId(ServerMessageId(channel->migrated_from_max_id_)); } - if (channel->migrated_from_chat_id != migrated_from_chat_id || - channel->migrated_from_max_message_id != migrated_from_max_message_id) { - channel->migrated_from_chat_id = migrated_from_chat_id; - channel->migrated_from_max_message_id = migrated_from_max_message_id; - channel->is_changed = true; + if (channel_full->migrated_from_chat_id != migrated_from_chat_id || + channel_full->migrated_from_max_message_id != migrated_from_max_message_id) { + channel_full->migrated_from_chat_id = migrated_from_chat_id; + channel_full->migrated_from_max_message_id = migrated_from_max_message_id; + channel_full->is_changed = true; } - update_channel_full(channel, channel_id); + update_channel_full(channel_full, channel_id); if (linked_channel_id.is_valid()) { auto linked_channel_full = get_channel_full_force(linked_channel_id, "on_get_chat_full"); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index cf04e26c4..efbd88718 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -164,7 +164,7 @@ class ContactsManager : public Actor { void on_binlog_channel_event(BinlogEvent &&event); void on_binlog_secret_chat_event(BinlogEvent &&event); - void on_get_user_full(tl_object_ptr &&user_full); + void on_get_user_full(tl_object_ptr &&user); void on_get_user_photos(UserId user_id, int32 offset, int32 limit, int32 total_count, vector> photos); From a41bca4b0e9ad11b877a6c8a4592f585fb9dbb4f Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 21 Feb 2021 00:21:14 +0300 Subject: [PATCH 196/232] Move get_update_suggested_actions_object to SuggestedAction.cpp. --- td/telegram/ConfigManager.cpp | 12 +++--------- td/telegram/ConfigManager.h | 3 --- td/telegram/SuggestedAction.cpp | 8 ++++++++ td/telegram/SuggestedAction.h | 3 +++ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 7a1cc62b3..f85169ef2 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -1130,12 +1130,6 @@ void ConfigManager::do_set_archive_and_mute(bool archive_and_mute) { G()->shared_config().set_option_boolean("archive_and_mute_new_chats_from_unknown_users", archive_and_mute); } -td_api::object_ptr ConfigManager::get_update_suggested_actions( - const vector &added_actions, const vector &removed_actions) { - return td_api::make_object(transform(added_actions, get_suggested_action_object), - transform(removed_actions, get_suggested_action_object)); -} - void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, Promise &&promise) { if (suggested_action == SuggestedAction::Empty) { return promise.set_error(Status::Error(400, "Action must be non-empty")); @@ -1162,7 +1156,7 @@ void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, P void ConfigManager::do_dismiss_suggested_action(SuggestedAction suggested_action) { if (td::remove(suggested_actions_, suggested_action)) { - send_closure(G()->td(), &Td::send_update, get_update_suggested_actions({}, {suggested_action})); + send_closure(G()->td(), &Td::send_update, get_update_suggested_actions_object({}, {suggested_action})); } } @@ -1809,14 +1803,14 @@ void ConfigManager::process_app_config(tl_object_ptr &c CHECK(!added_actions.empty() || !removed_actions.empty()); suggested_actions_ = std::move(suggested_actions); send_closure(G()->td(), &Td::send_update, - get_update_suggested_actions(std::move(added_actions), std::move(removed_actions))); + get_update_suggested_actions_object(std::move(added_actions), std::move(removed_actions))); } } } void ConfigManager::get_current_state(vector> &updates) const { if (!suggested_actions_.empty()) { - updates.push_back(get_update_suggested_actions(suggested_actions_, {})); + updates.push_back(get_update_suggested_actions_object(suggested_actions_, {})); } } diff --git a/td/telegram/ConfigManager.h b/td/telegram/ConfigManager.h index 57f4a72a6..6ff759a16 100644 --- a/td/telegram/ConfigManager.h +++ b/td/telegram/ConfigManager.h @@ -157,9 +157,6 @@ class ConfigManager : public NetQueryCallback { void do_set_archive_and_mute(bool archive_and_mute); - static td_api::object_ptr get_update_suggested_actions( - const vector &added_actions, const vector &removed_actions); - void do_dismiss_suggested_action(SuggestedAction suggested_action); static Timestamp load_config_expire_time(); diff --git a/td/telegram/SuggestedAction.cpp b/td/telegram/SuggestedAction.cpp index 93c892997..bfc68f178 100644 --- a/td/telegram/SuggestedAction.cpp +++ b/td/telegram/SuggestedAction.cpp @@ -6,6 +6,8 @@ // #include "td/telegram/SuggestedAction.h" +#include "td/utils/algorithm.h" + namespace td { SuggestedAction get_suggested_action(Slice action_str) { @@ -62,4 +64,10 @@ td_api::object_ptr get_suggested_action_object(Suggeste } } +td_api::object_ptr get_update_suggested_actions_object( + const vector &added_actions, const vector &removed_actions) { + return td_api::make_object(transform(added_actions, get_suggested_action_object), + transform(removed_actions, get_suggested_action_object)); +} + } // namespace td diff --git a/td/telegram/SuggestedAction.h b/td/telegram/SuggestedAction.h index 13607948a..5f08daa24 100644 --- a/td/telegram/SuggestedAction.h +++ b/td/telegram/SuggestedAction.h @@ -23,4 +23,7 @@ SuggestedAction get_suggested_action(const td_api::object_ptr get_suggested_action_object(SuggestedAction action); +td_api::object_ptr get_update_suggested_actions_object( + const vector &added_actions, const vector &removed_actions); + } // namespace td From a14c9071f1ad1aaf671cb972b1ee9803705cebb7 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 21 Feb 2021 01:06:45 +0300 Subject: [PATCH 197/232] Use struct for SuggestedAction. --- td/telegram/ConfigManager.cpp | 35 +++++++------- td/telegram/ConfigManager.h | 2 +- td/telegram/SuggestedAction.cpp | 81 ++++++++++++++++++--------------- td/telegram/SuggestedAction.h | 32 +++++++++++-- td/telegram/Td.cpp | 2 +- 5 files changed, 91 insertions(+), 61 deletions(-) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index f85169ef2..4e3e28026 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -58,7 +58,6 @@ #include "td/utils/UInt.h" #include -#include #include #include #include @@ -1080,7 +1079,7 @@ void ConfigManager::set_archive_and_mute(bool archive_and_mute, Promise && return promise.set_error(Status::Error(500, "Request aborted")); } if (archive_and_mute) { - do_dismiss_suggested_action(SuggestedAction::EnableArchiveAndMuteNewChats); + do_dismiss_suggested_action(SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}); } last_set_archive_and_mute_ = archive_and_mute; @@ -1125,16 +1124,16 @@ void ConfigManager::do_set_ignore_sensitive_content_restrictions(bool ignore_sen void ConfigManager::do_set_archive_and_mute(bool archive_and_mute) { if (archive_and_mute) { - do_dismiss_suggested_action(SuggestedAction::EnableArchiveAndMuteNewChats); + do_dismiss_suggested_action(SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}); } G()->shared_config().set_option_boolean("archive_and_mute_new_chats_from_unknown_users", archive_and_mute); } void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, Promise &&promise) { - if (suggested_action == SuggestedAction::Empty) { + if (suggested_action == SuggestedAction()) { return promise.set_error(Status::Error(400, "Action must be non-empty")); } - auto action_str = get_suggested_action_str(suggested_action); + auto action_str = suggested_action.get_suggested_action_str(); if (action_str.empty()) { return promise.set_value(Unit()); } @@ -1144,13 +1143,14 @@ void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, P } dismiss_suggested_action_request_count_++; - auto &queries = dismiss_suggested_action_queries_[suggested_action]; + auto type = static_cast(suggested_action.type_); + auto &queries = dismiss_suggested_action_queries_[type]; queries.push_back(std::move(promise)); if (queries.size() == 1) { G()->net_query_dispatcher().dispatch_with_callback( G()->net_query_creator().create( telegram_api::help_dismissSuggestion(make_tl_object(), action_str)), - actor_shared(this, 100 + static_cast(suggested_action))); + actor_shared(this, 100 + type)); } } @@ -1163,9 +1163,10 @@ void ConfigManager::do_dismiss_suggested_action(SuggestedAction suggested_action void ConfigManager::on_result(NetQueryPtr res) { auto token = get_link_token(); if (token >= 100 && token <= 200) { - SuggestedAction suggested_action = static_cast(static_cast(token - 100)); - auto promises = std::move(dismiss_suggested_action_queries_[suggested_action]); - dismiss_suggested_action_queries_.erase(suggested_action); + auto type = static_cast(token - 100); + SuggestedAction suggested_action{static_cast(type)}; + auto promises = std::move(dismiss_suggested_action_queries_[type]); + dismiss_suggested_action_queries_.erase(type); CHECK(!promises.empty()); CHECK(dismiss_suggested_action_request_count_ >= promises.size()); dismiss_suggested_action_request_count_ -= promises.size(); @@ -1668,10 +1669,11 @@ void ConfigManager::process_app_config(tl_object_ptr &c CHECK(action != nullptr); if (action->get_id() == telegram_api::jsonString::ID) { Slice action_str = static_cast(action.get())->value_; - auto suggested_action = get_suggested_action(action_str); - if (suggested_action != SuggestedAction::Empty) { - if (archive_and_mute && suggested_action == SuggestedAction::EnableArchiveAndMuteNewChats) { - LOG(INFO) << "Skip SuggestedAction::EnableArchiveAndMuteNewChats"; + SuggestedAction suggested_action(action_str); + if (suggested_action != SuggestedAction()) { + if (archive_and_mute && + suggested_action == SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}) { + LOG(INFO) << "Skip EnableArchiveAndMuteNewChats suggested action"; } else { suggested_actions.push_back(suggested_action); } @@ -1790,10 +1792,9 @@ void ConfigManager::process_app_config(tl_object_ptr &c auto old_it = suggested_actions_.begin(); auto new_it = suggested_actions.begin(); while (old_it != suggested_actions_.end() || new_it != suggested_actions.end()) { - if (old_it != suggested_actions_.end() && - (new_it == suggested_actions.end() || std::less()(*old_it, *new_it))) { + if (old_it != suggested_actions_.end() && (new_it == suggested_actions.end() || *old_it < *new_it)) { removed_actions.push_back(*old_it++); - } else if (old_it == suggested_actions_.end() || std::less()(*new_it, *old_it)) { + } else if (old_it == suggested_actions_.end() || *new_it < *old_it) { added_actions.push_back(*new_it++); } else { old_it++; diff --git a/td/telegram/ConfigManager.h b/td/telegram/ConfigManager.h index 6ff759a16..9f4e435c9 100644 --- a/td/telegram/ConfigManager.h +++ b/td/telegram/ConfigManager.h @@ -136,7 +136,7 @@ class ConfigManager : public NetQueryCallback { vector suggested_actions_; size_t dismiss_suggested_action_request_count_ = 0; - std::map>> dismiss_suggested_action_queries_; + std::map>> dismiss_suggested_action_queries_; static constexpr uint64 REFCNT_TOKEN = std::numeric_limits::max() - 2; diff --git a/td/telegram/SuggestedAction.cpp b/td/telegram/SuggestedAction.cpp index bfc68f178..9d2cad2f4 100644 --- a/td/telegram/SuggestedAction.cpp +++ b/td/telegram/SuggestedAction.cpp @@ -10,53 +10,57 @@ namespace td { -SuggestedAction get_suggested_action(Slice action_str) { - if (action_str == Slice("AUTOARCHIVE_POPULAR")) { - return SuggestedAction::EnableArchiveAndMuteNewChats; - } - if (action_str == Slice("NEWCOMER_TICKS")) { - return SuggestedAction::SeeTicksHint; - } - return SuggestedAction::Empty; +void SuggestedAction::init(Type type) { + type_ = type; } -string get_suggested_action_str(SuggestedAction action) { - switch (action) { - case SuggestedAction::EnableArchiveAndMuteNewChats: +SuggestedAction::SuggestedAction(Slice action_str) { + if (action_str == Slice("AUTOARCHIVE_POPULAR")) { + init(Type::EnableArchiveAndMuteNewChats); + } else if (action_str == Slice("NEWCOMER_TICKS")) { + init(Type::SeeTicksHint); + } +} + +SuggestedAction::SuggestedAction(const td_api::object_ptr &action_object) { + if (action_object == nullptr) { + return; + } + switch (action_object->get_id()) { + case td_api::suggestedActionEnableArchiveAndMuteNewChats::ID: + init(Type::EnableArchiveAndMuteNewChats); + break; + case td_api::suggestedActionCheckPhoneNumber::ID: + init(Type::CheckPhoneNumber); + break; + case td_api::suggestedActionSeeTicksHint::ID: + init(Type::SeeTicksHint); + break; + default: + UNREACHABLE(); + } +} + +string SuggestedAction::get_suggested_action_str() const { + switch (type_) { + case Type::EnableArchiveAndMuteNewChats: return "AUTOARCHIVE_POPULAR"; - case SuggestedAction::SeeTicksHint: + case Type::SeeTicksHint: return "NEWCOMER_TICKS"; default: return string(); } } -SuggestedAction get_suggested_action(const td_api::object_ptr &action_object) { - if (action_object == nullptr) { - return SuggestedAction::Empty; - } - switch (action_object->get_id()) { - case td_api::suggestedActionEnableArchiveAndMuteNewChats::ID: - return SuggestedAction::EnableArchiveAndMuteNewChats; - case td_api::suggestedActionCheckPhoneNumber::ID: - return SuggestedAction::CheckPhoneNumber; - case td_api::suggestedActionSeeTicksHint::ID: - return SuggestedAction::SeeTicksHint; - default: - UNREACHABLE(); - return SuggestedAction::Empty; - } -} - -td_api::object_ptr get_suggested_action_object(SuggestedAction action) { - switch (action) { - case SuggestedAction::Empty: +td_api::object_ptr SuggestedAction::get_suggested_action_object() const { + switch (type_) { + case Type::Empty: return nullptr; - case SuggestedAction::EnableArchiveAndMuteNewChats: + case Type::EnableArchiveAndMuteNewChats: return td_api::make_object(); - case SuggestedAction::CheckPhoneNumber: + case Type::CheckPhoneNumber: return td_api::make_object(); - case SuggestedAction::SeeTicksHint: + case Type::SeeTicksHint: return td_api::make_object(); default: UNREACHABLE(); @@ -66,8 +70,11 @@ td_api::object_ptr get_suggested_action_object(Suggeste td_api::object_ptr get_update_suggested_actions_object( const vector &added_actions, const vector &removed_actions) { - return td_api::make_object(transform(added_actions, get_suggested_action_object), - transform(removed_actions, get_suggested_action_object)); + auto get_object = [](const SuggestedAction &action) { + return action.get_suggested_action_object(); + }; + return td_api::make_object(transform(added_actions, get_object), + transform(removed_actions, get_object)); } } // namespace td diff --git a/td/telegram/SuggestedAction.h b/td/telegram/SuggestedAction.h index 5f08daa24..6ecf296b5 100644 --- a/td/telegram/SuggestedAction.h +++ b/td/telegram/SuggestedAction.h @@ -13,15 +13,37 @@ namespace td { -enum class SuggestedAction : int32 { Empty, EnableArchiveAndMuteNewChats, CheckPhoneNumber, SeeTicksHint }; +struct SuggestedAction { + enum class Type : int32 { Empty, EnableArchiveAndMuteNewChats, CheckPhoneNumber, SeeTicksHint }; + Type type_ = Type::Empty; -SuggestedAction get_suggested_action(Slice action_str); + void init(Type type); -string get_suggested_action_str(SuggestedAction action); + SuggestedAction() = default; -SuggestedAction get_suggested_action(const td_api::object_ptr &action_object); + explicit SuggestedAction(Type type) : type_(type) { + } -td_api::object_ptr get_suggested_action_object(SuggestedAction action); + explicit SuggestedAction(Slice action_str); + + explicit SuggestedAction(const td_api::object_ptr &action_object); + + string get_suggested_action_str() const; + + td_api::object_ptr get_suggested_action_object() const; +}; + +inline bool operator==(const SuggestedAction &lhs, const SuggestedAction &rhs) { + return lhs.type_ == rhs.type_; +} + +inline bool operator!=(const SuggestedAction &lhs, const SuggestedAction &rhs) { + return !(lhs == rhs); +} + +inline bool operator<(const SuggestedAction &lhs, const SuggestedAction &rhs) { + return static_cast(lhs.type_) < static_cast(rhs.type_); +} td_api::object_ptr get_update_suggested_actions_object( const vector &added_actions, const vector &removed_actions); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index c2c5f24a3..f53b51b31 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -7530,7 +7530,7 @@ void Td::on_request(uint64 id, td_api::stopPoll &request) { void Td::on_request(uint64 id, const td_api::hideSuggestedAction &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - send_closure_later(config_manager_, &ConfigManager::dismiss_suggested_action, get_suggested_action(request.action_), + send_closure_later(config_manager_, &ConfigManager::dismiss_suggested_action, SuggestedAction(request.action_), std::move(promise)); } From b67bafb23c0593470b6bbf5e932debd41512fe3f Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 22 Feb 2021 18:06:18 +0300 Subject: [PATCH 198/232] Add td_api::suggestedActionConvertToBroadcastGroup. --- td/generate/scheme/td_api.tl | 3 +++ td/generate/scheme/td_api.tlo | Bin 197812 -> 197932 bytes td/telegram/ContactsManager.cpp | 9 +++++++++ td/telegram/ContactsManager.h | 3 +++ td/telegram/SuggestedAction.cpp | 26 +++++++++++++++++++++++--- td/telegram/SuggestedAction.h | 8 ++++++-- td/telegram/Td.cpp | 3 +-- td/telegram/cli.cpp | 13 ++++++++++--- 8 files changed, 55 insertions(+), 10 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 193994626..a7d9dfafa 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3153,6 +3153,9 @@ suggestedActionCheckPhoneNumber = SuggestedAction; //@description Suggests the user to see a hint about meaning of one and two ticks on sent message suggestedActionSeeTicksHint = SuggestedAction; +//@description Suggests the user to convert specified supergroup to a broadcast group @supergroup_id Supergroup identifier +suggestedActionConvertToBroadcastGroup supergroup_id:int32 = SuggestedAction; + //@description Contains a counter @count Count count count:int32 = Count; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 2a8e444092c03ed7cc868d9da69bf37f3445811f..89689edc66d5edce35b56c2d38dc13b5c9c29f89 100644 GIT binary patch delta 254 zcmdne$+M=5XM>BbXt9U%7yjVV^z_u?lGGH(n^U6|-N<#9Tit-avk`s$d+>7!{3mCxm8eC*lnYMsQgayRgZg8Bj!5z$;zJ-s; c0wVZBb$g%1LiTuH(>FKG(C8;Tn$t9Wjd6OqFNo+3Abzo;KZQf|IeWMBE z?THc~g*T50`7i)Mku#ePhi%(Ikl6Gk#~2N8%f30rsDo2>`- &&promise) { + if (!action.dialog_id_.is_valid()) { + send_closure_later(G()->config_manager(), &ConfigManager::dismiss_suggested_action, std::move(action), + std::move(promise)); + return; + } +} + void ContactsManager::on_imported_contacts(int64 random_id, vector imported_contact_user_ids, vector unimported_contact_invites) { LOG(INFO) << "Contacts import with random_id " << random_id diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index efbd88718..bdf258628 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -31,6 +31,7 @@ #include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/StickerSetId.h" +#include "td/telegram/SuggestedAction.h" #include "td/telegram/UserId.h" #include "td/actor/actor.h" @@ -429,6 +430,8 @@ class ContactsManager : public Actor { vector get_inactive_channels(Promise &&promise); + void dismiss_suggested_action(SuggestedAction action, Promise &&promise); + bool is_user_contact(UserId user_id, bool is_mutual = false) const; bool is_user_deleted(UserId user_id) const; diff --git a/td/telegram/SuggestedAction.cpp b/td/telegram/SuggestedAction.cpp index 9d2cad2f4..e3760b25d 100644 --- a/td/telegram/SuggestedAction.cpp +++ b/td/telegram/SuggestedAction.cpp @@ -6,6 +6,8 @@ // #include "td/telegram/SuggestedAction.h" +#include "td/telegram/ChannelId.h" + #include "td/utils/algorithm.h" namespace td { @@ -22,11 +24,19 @@ SuggestedAction::SuggestedAction(Slice action_str) { } } -SuggestedAction::SuggestedAction(const td_api::object_ptr &action_object) { - if (action_object == nullptr) { +SuggestedAction::SuggestedAction(Slice action_str, DialogId dialog_id) { + CHECK(dialog_id.is_valid()); + if (action_str == Slice("CONVERT_GIGAGROUP")) { + type_ = Type::ConvertToGigagroup; + dialog_id_ = dialog_id; + } +} + +SuggestedAction::SuggestedAction(const td_api::object_ptr &suggested_action) { + if (suggested_action == nullptr) { return; } - switch (action_object->get_id()) { + switch (suggested_action->get_id()) { case td_api::suggestedActionEnableArchiveAndMuteNewChats::ID: init(Type::EnableArchiveAndMuteNewChats); break; @@ -36,6 +46,12 @@ SuggestedAction::SuggestedAction(const td_api::object_ptr(suggested_action.get()); + type_ = Type::ConvertToGigagroup; + dialog_id_ = DialogId(ChannelId(action->supergroup_id_)); + break; + } default: UNREACHABLE(); } @@ -47,6 +63,8 @@ string SuggestedAction::get_suggested_action_str() const { return "AUTOARCHIVE_POPULAR"; case Type::SeeTicksHint: return "NEWCOMER_TICKS"; + case Type::ConvertToGigagroup: + return "CONVERT_GIGAGROUP"; default: return string(); } @@ -62,6 +80,8 @@ td_api::object_ptr SuggestedAction::get_suggested_actio return td_api::make_object(); case Type::SeeTicksHint: return td_api::make_object(); + case Type::ConvertToGigagroup: + return td_api::make_object(dialog_id_.get_channel_id().get()); default: UNREACHABLE(); return nullptr; diff --git a/td/telegram/SuggestedAction.h b/td/telegram/SuggestedAction.h index 6ecf296b5..c95c111d8 100644 --- a/td/telegram/SuggestedAction.h +++ b/td/telegram/SuggestedAction.h @@ -6,6 +6,7 @@ // #pragma once +#include "td/telegram/DialogId.h" #include "td/telegram/td_api.h" #include "td/utils/common.h" @@ -14,8 +15,9 @@ namespace td { struct SuggestedAction { - enum class Type : int32 { Empty, EnableArchiveAndMuteNewChats, CheckPhoneNumber, SeeTicksHint }; + enum class Type : int32 { Empty, EnableArchiveAndMuteNewChats, CheckPhoneNumber, SeeTicksHint, ConvertToGigagroup }; Type type_ = Type::Empty; + DialogId dialog_id_; void init(Type type); @@ -26,7 +28,9 @@ struct SuggestedAction { explicit SuggestedAction(Slice action_str); - explicit SuggestedAction(const td_api::object_ptr &action_object); + SuggestedAction(Slice action_str, DialogId dialog_id); + + explicit SuggestedAction(const td_api::object_ptr &suggested_action); string get_suggested_action_str() const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index f53b51b31..d012f7a14 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -7530,8 +7530,7 @@ void Td::on_request(uint64 id, td_api::stopPoll &request) { void Td::on_request(uint64 id, const td_api::hideSuggestedAction &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - send_closure_later(config_manager_, &ConfigManager::dismiss_suggested_action, SuggestedAction(request.action_), - std::move(promise)); + contacts_manager_->dismiss_suggested_action(SuggestedAction(request.action_), std::move(promise)); } void Td::on_request(uint64 id, const td_api::getLoginUrlInfo &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 5128af018..18cf32ba9 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -568,13 +568,17 @@ class CliClient final : public Actor { return result; } - int32 as_supergroup_id(Slice str) { + int32 as_supergroup_id(Slice str) const { str = trim(str); if (str[0] == '@') { str.remove_prefix(1); } if (is_alpha(str[0])) { - return username_to_supergroup_id_[to_lower(str)]; + auto it = username_to_supergroup_id_.find(to_lower(str)); + if (it == username_to_supergroup_id_.end()) { + return 0; + } + return it->second; } auto result = to_integer(str); int64 shift = static_cast(-1000000000000ll); @@ -1374,7 +1378,7 @@ class CliClient final : public Actor { return nullptr; } - static td_api::object_ptr as_suggested_action(Slice action) { + td_api::object_ptr as_suggested_action(Slice action) const { if (action == "unarchive") { return td_api::make_object(); } @@ -1384,6 +1388,9 @@ class CliClient final : public Actor { if (action == "ticks") { return td_api::make_object(); } + if (begins_with(action, "giga")) { + return td_api::make_object(as_supergroup_id(action.substr(4))); + } return nullptr; } From e19f59662e6c12b8b33630c3cb23f9fd9b9275ec Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 22 Feb 2021 18:25:49 +0300 Subject: [PATCH 199/232] Move functions for handling suggested actions to SuggestedActions.cpp. --- td/telegram/ConfigManager.cpp | 37 +++++---------------------------- td/telegram/ConfigManager.h | 2 -- td/telegram/SuggestedAction.cpp | 37 +++++++++++++++++++++++++++++++++ td/telegram/SuggestedAction.h | 5 +++++ 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 4e3e28026..a17c7c2da 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -57,7 +57,6 @@ #include "td/utils/tl_parsers.h" #include "td/utils/UInt.h" -#include #include #include #include @@ -100,7 +99,7 @@ Result HttpDate::to_unix_time(int32 year, int32 month, int32 day, int32 h return res; } -Result HttpDate::parse_http_date(std::string slice) { +Result HttpDate::parse_http_date(string slice) { Parser p(slice); p.read_till(','); // ignore week day p.skip(','); @@ -1079,7 +1078,7 @@ void ConfigManager::set_archive_and_mute(bool archive_and_mute, Promise && return promise.set_error(Status::Error(500, "Request aborted")); } if (archive_and_mute) { - do_dismiss_suggested_action(SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}); + remove_suggested_action(suggested_actions_, SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}); } last_set_archive_and_mute_ = archive_and_mute; @@ -1124,7 +1123,7 @@ void ConfigManager::do_set_ignore_sensitive_content_restrictions(bool ignore_sen void ConfigManager::do_set_archive_and_mute(bool archive_and_mute) { if (archive_and_mute) { - do_dismiss_suggested_action(SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}); + remove_suggested_action(suggested_actions_, SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}); } G()->shared_config().set_option_boolean("archive_and_mute_new_chats_from_unknown_users", archive_and_mute); } @@ -1154,12 +1153,6 @@ void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, P } } -void ConfigManager::do_dismiss_suggested_action(SuggestedAction suggested_action) { - if (td::remove(suggested_actions_, suggested_action)) { - send_closure(G()->td(), &Td::send_update, get_update_suggested_actions_object({}, {suggested_action})); - } -} - void ConfigManager::on_result(NetQueryPtr res) { auto token = get_link_token(); if (token >= 100 && token <= 200) { @@ -1178,7 +1171,7 @@ void ConfigManager::on_result(NetQueryPtr res) { } return; } - do_dismiss_suggested_action(suggested_action); + remove_suggested_action(suggested_actions_, suggested_action); get_app_config(Auto()); for (auto &promise : promises) { @@ -1785,27 +1778,7 @@ void ConfigManager::process_app_config(tl_object_ptr &c // do not update suggested actions while changing content settings or dismissing an action if (!is_set_content_settings_request_sent_ && dismiss_suggested_action_request_count_ == 0) { - td::unique(suggested_actions); - if (suggested_actions != suggested_actions_) { - vector added_actions; - vector removed_actions; - auto old_it = suggested_actions_.begin(); - auto new_it = suggested_actions.begin(); - while (old_it != suggested_actions_.end() || new_it != suggested_actions.end()) { - if (old_it != suggested_actions_.end() && (new_it == suggested_actions.end() || *old_it < *new_it)) { - removed_actions.push_back(*old_it++); - } else if (old_it == suggested_actions_.end() || *new_it < *old_it) { - added_actions.push_back(*new_it++); - } else { - old_it++; - new_it++; - } - } - CHECK(!added_actions.empty() || !removed_actions.empty()); - suggested_actions_ = std::move(suggested_actions); - send_closure(G()->td(), &Td::send_update, - get_update_suggested_actions_object(std::move(added_actions), std::move(removed_actions))); - } + update_suggested_actions(suggested_actions_, std::move(suggested_actions)); } } diff --git a/td/telegram/ConfigManager.h b/td/telegram/ConfigManager.h index 9f4e435c9..ecc3569c6 100644 --- a/td/telegram/ConfigManager.h +++ b/td/telegram/ConfigManager.h @@ -157,8 +157,6 @@ class ConfigManager : public NetQueryCallback { void do_set_archive_and_mute(bool archive_and_mute); - void do_dismiss_suggested_action(SuggestedAction suggested_action); - static Timestamp load_config_expire_time(); static void save_config_expire(Timestamp timestamp); static void save_dc_options_update(DcOptions dc_options); diff --git a/td/telegram/SuggestedAction.cpp b/td/telegram/SuggestedAction.cpp index e3760b25d..e7d980c8e 100644 --- a/td/telegram/SuggestedAction.cpp +++ b/td/telegram/SuggestedAction.cpp @@ -7,9 +7,13 @@ #include "td/telegram/SuggestedAction.h" #include "td/telegram/ChannelId.h" +#include "td/telegram/Global.h" +#include "td/telegram/Td.h" #include "td/utils/algorithm.h" +#include + namespace td { void SuggestedAction::init(Type type) { @@ -97,4 +101,37 @@ td_api::object_ptr get_update_suggested_actions_ transform(removed_actions, get_object)); } +void update_suggested_actions(vector &suggested_actions, + vector &&new_suggested_actions) { + td::unique(new_suggested_actions); + if (new_suggested_actions == suggested_actions) { + return; + } + + vector added_actions; + vector removed_actions; + auto old_it = suggested_actions.begin(); + auto new_it = new_suggested_actions.begin(); + while (old_it != suggested_actions.end() || new_it != new_suggested_actions.end()) { + if (old_it != suggested_actions.end() && (new_it == new_suggested_actions.end() || *old_it < *new_it)) { + removed_actions.push_back(*old_it++); + } else if (old_it == suggested_actions.end() || *new_it < *old_it) { + added_actions.push_back(*new_it++); + } else { + old_it++; + new_it++; + } + } + CHECK(!added_actions.empty() || !removed_actions.empty()); + suggested_actions = std::move(new_suggested_actions); + send_closure(G()->td(), &Td::send_update, + get_update_suggested_actions_object(std::move(added_actions), std::move(removed_actions))); +} + +void remove_suggested_action(vector &suggested_actions, SuggestedAction suggested_action) { + if (td::remove(suggested_actions, suggested_action)) { + send_closure(G()->td(), &Td::send_update, get_update_suggested_actions_object({}, {suggested_action})); + } +} + } // namespace td diff --git a/td/telegram/SuggestedAction.h b/td/telegram/SuggestedAction.h index c95c111d8..8b39a18c6 100644 --- a/td/telegram/SuggestedAction.h +++ b/td/telegram/SuggestedAction.h @@ -52,4 +52,9 @@ inline bool operator<(const SuggestedAction &lhs, const SuggestedAction &rhs) { td_api::object_ptr get_update_suggested_actions_object( const vector &added_actions, const vector &removed_actions); +void update_suggested_actions(vector &suggested_actions, + vector &&new_suggested_actions); + +void remove_suggested_action(vector &suggested_actions, SuggestedAction suggested_action); + } // namespace td From 332b7a6edb3d1d2de8651eb351bbc73a72b70865 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 22 Feb 2021 19:38:40 +0300 Subject: [PATCH 200/232] Support dismissing of chat-specific suggested actions. --- td/telegram/ConfigManager.cpp | 3 - td/telegram/ContactsManager.cpp | 97 ++++++++++++++++++++++++++++++++- td/telegram/ContactsManager.h | 7 +++ td/telegram/SuggestedAction.h | 4 +- 4 files changed, 106 insertions(+), 5 deletions(-) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index a17c7c2da..05a3ab1f0 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -1129,9 +1129,6 @@ void ConfigManager::do_set_archive_and_mute(bool archive_and_mute) { } void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, Promise &&promise) { - if (suggested_action == SuggestedAction()) { - return promise.set_error(Status::Error(400, "Action must be non-empty")); - } auto action_str = suggested_action.get_suggested_action_str(); if (action_str.empty()) { return promise.set_value(Unit()); diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index c04a8f503..909317793 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -72,6 +72,38 @@ namespace td { +class DismissSuggestionQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit DismissSuggestionQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(SuggestedAction action) { + dialog_id_ = action.dialog_id_; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read); + CHECK(input_peer != nullptr); + + send_query(G()->net_query_creator().create( + telegram_api::help_dismissSuggestion(std::move(input_peer), action.get_suggested_action_str()))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "DismissSuggestionQuery"); + promise_.set_error(std::move(status)); + } +}; + class SetAccountTtlQuery : public Td::ResultHandler { Promise promise_; @@ -6211,6 +6243,8 @@ void ContactsManager::convert_channel_to_gigagroup(ChannelId channel_id, Promise return promise.set_error(Status::Error(6, "Chat must be a supergroup")); } + remove_dialog_suggested_action(SuggestedAction{SuggestedAction::Type::ConvertToGigagroup, DialogId(channel_id)}); + td_->create_handler(std::move(promise))->send(channel_id); } @@ -7439,12 +7473,73 @@ void ContactsManager::remove_inactive_channel(ChannelId channel_id) { } } +void ContactsManager::remove_dialog_suggested_action(SuggestedAction action) { + auto it = dialog_suggested_actions_.find(action.dialog_id_); + if (it == dialog_suggested_actions_.end()) { + return; + } + remove_suggested_action(it->second, action); + if (it->second.empty()) { + dialog_suggested_actions_.erase(it); + } +} + void ContactsManager::dismiss_suggested_action(SuggestedAction action, Promise &&promise) { - if (!action.dialog_id_.is_valid()) { + if (action == SuggestedAction()) { + return promise.set_error(Status::Error(400, "Action must be non-empty")); + } + auto dialog_id = action.dialog_id_; + if (!dialog_id.is_valid()) { send_closure_later(G()->config_manager(), &ConfigManager::dismiss_suggested_action, std::move(action), std::move(promise)); return; } + + if (!td_->messages_manager_->have_dialog(dialog_id)) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(400, "Can't access the chat")); + } + + auto it = dialog_suggested_actions_.find(dialog_id); + if (it == dialog_suggested_actions_.end() || !td::contains(it->second, action)) { + return promise.set_value(Unit()); + } + + auto action_str = action.get_suggested_action_str(); + if (action_str.empty()) { + return promise.set_value(Unit()); + } + + auto &queries = dismiss_suggested_action_queries_[dialog_id]; + queries.push_back(std::move(promise)); + if (queries.size() == 1) { + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), action](Result &&result) { + send_closure(actor_id, &ContactsManager::on_dismiss_suggested_action, action, std::move(result)); + }); + td_->create_handler(std::move(query_promise))->send(std::move(action)); + } +} + +void ContactsManager::on_dismiss_suggested_action(SuggestedAction action, Result &&result) { + auto it = dismiss_suggested_action_queries_.find(action.dialog_id_); + CHECK(it != dismiss_suggested_action_queries_.end()); + auto promises = std::move(it->second); + dismiss_suggested_action_queries_.erase(it); + + if (result.is_error()) { + for (auto &promise : promises) { + promise.set_error(result.error().clone()); + } + return; + } + + remove_dialog_suggested_action(action); + + for (auto &promise : promises) { + promise.set_value(Unit()); + } } void ContactsManager::on_imported_contacts(int64 random_id, vector imported_contact_user_ids, diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index bdf258628..8f893a820 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -1435,6 +1435,10 @@ class ContactsManager : public Actor { void reload_dialog_administrators(DialogId dialog_id, int32 hash, Promise &&promise); + void remove_dialog_suggested_action(SuggestedAction action); + + void on_dismiss_suggested_action(SuggestedAction action, Result &&result); + static td_api::object_ptr get_update_unknown_user_object(UserId user_id); td_api::object_ptr get_user_status_object(UserId user_id, const User *u) const; @@ -1622,6 +1626,9 @@ class ContactsManager : public Actor { std::unordered_map, DialogIdHash> dialog_administrators_; + std::unordered_map, DialogIdHash> dialog_suggested_actions_; + std::unordered_map>, DialogIdHash> dismiss_suggested_action_queries_; + class UploadProfilePhotoCallback; std::shared_ptr upload_profile_photo_callback_; diff --git a/td/telegram/SuggestedAction.h b/td/telegram/SuggestedAction.h index 8b39a18c6..774e024b1 100644 --- a/td/telegram/SuggestedAction.h +++ b/td/telegram/SuggestedAction.h @@ -23,7 +23,7 @@ struct SuggestedAction { SuggestedAction() = default; - explicit SuggestedAction(Type type) : type_(type) { + explicit SuggestedAction(Type type, DialogId dialog_id = DialogId()) : type_(type), dialog_id_(dialog_id) { } explicit SuggestedAction(Slice action_str); @@ -38,6 +38,7 @@ struct SuggestedAction { }; inline bool operator==(const SuggestedAction &lhs, const SuggestedAction &rhs) { + CHECK(lhs.dialog_id_ == rhs.dialog_id_); return lhs.type_ == rhs.type_; } @@ -46,6 +47,7 @@ inline bool operator!=(const SuggestedAction &lhs, const SuggestedAction &rhs) { } inline bool operator<(const SuggestedAction &lhs, const SuggestedAction &rhs) { + CHECK(lhs.dialog_id_ == rhs.dialog_id_); return static_cast(lhs.type_) < static_cast(rhs.type_); } From c5f84de9cd494e89525f8774d614cf700ab78944 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 22 Feb 2021 23:34:10 +0300 Subject: [PATCH 201/232] Support channel suggested actions. --- td/telegram/ConfigManager.cpp | 2 +- td/telegram/ContactsManager.cpp | 38 +++++++++++++++++++++++++++++++-- td/telegram/SuggestedAction.cpp | 7 ++++-- td/telegram/SuggestedAction.h | 4 ++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 05a3ab1f0..cdfee4540 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -1660,7 +1660,7 @@ void ConfigManager::process_app_config(tl_object_ptr &c if (action->get_id() == telegram_api::jsonString::ID) { Slice action_str = static_cast(action.get())->value_; SuggestedAction suggested_action(action_str); - if (suggested_action != SuggestedAction()) { + if (!suggested_action.is_empty()) { if (archive_and_mute && suggested_action == SuggestedAction{SuggestedAction::Type::EnableArchiveAndMuteNewChats}) { LOG(INFO) << "Skip EnableArchiveAndMuteNewChats suggested action"; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 909317793..c977d08b1 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -7485,11 +7485,11 @@ void ContactsManager::remove_dialog_suggested_action(SuggestedAction action) { } void ContactsManager::dismiss_suggested_action(SuggestedAction action, Promise &&promise) { - if (action == SuggestedAction()) { + if (action.is_empty()) { return promise.set_error(Status::Error(400, "Action must be non-empty")); } auto dialog_id = action.dialog_id_; - if (!dialog_id.is_valid()) { + if (dialog_id == DialogId()) { send_closure_later(G()->config_manager(), &ConfigManager::dismiss_suggested_action, std::move(action), std::move(promise)); return; @@ -9685,6 +9685,10 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from } if (c->is_default_permissions_changed) { td_->messages_manager_->on_dialog_permissions_updated(DialogId(channel_id)); + if (c->default_permissions != + RestrictedRights(false, false, false, false, false, false, false, false, false, false, false)) { + remove_dialog_suggested_action(SuggestedAction{SuggestedAction::Type::ConvertToGigagroup, DialogId(channel_id)}); + } } if (!td_->auth_manager_->is_bot()) { if (c->restriction_reasons.empty()) { @@ -10466,6 +10470,32 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c update_channel_full(linked_channel_full, linked_channel_id); } } + + if (dismiss_suggested_action_queries_.count(DialogId(channel_id)) == 0) { + auto it = dialog_suggested_actions_.find(DialogId(channel_id)); + if (it != dialog_suggested_actions_.end() || !channel->pending_suggestions_.empty()) { + vector suggested_actions; + for (auto &action_str : channel->pending_suggestions_) { + SuggestedAction suggested_action(action_str, DialogId(channel_id)); + if (!suggested_action.is_empty()) { + if (suggested_action == SuggestedAction{SuggestedAction::Type::ConvertToGigagroup, DialogId(channel_id)} && + (c->is_gigagroup || c->default_permissions != RestrictedRights(false, false, false, false, false, false, + false, false, false, false, false))) { + LOG(INFO) << "Skip ConvertToGigagroup suggested action"; + } else { + suggested_actions.push_back(suggested_action); + } + } + } + if (it == dialog_suggested_actions_.end()) { + it = dialog_suggested_actions_.emplace(DialogId(channel_id), vector()).first; + } + update_suggested_actions(it->second, std::move(suggested_actions)); + if (it->second.empty()) { + dialog_suggested_actions_.erase(it); + } + } + } } promise.set_value(Unit()); } @@ -12752,6 +12782,7 @@ void ContactsManager::on_channel_status_changed(Channel *c, ChannelId channel_id send_get_channel_full_query(nullptr, channel_id, Auto(), "update channel owner"); reload_dialog_administrators(DialogId(channel_id), 0, Auto()); + remove_dialog_suggested_action(SuggestedAction{SuggestedAction::Type::ConvertToGigagroup, DialogId(channel_id)}); } if (need_reload_group_call) { send_closure_later(G()->messages_manager(), &MessagesManager::on_update_dialog_group_call_rights, @@ -14997,6 +15028,9 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char is_slow_mode_enabled = false; is_gigagroup = false; } + if (is_gigagroup) { + remove_dialog_suggested_action(SuggestedAction{SuggestedAction::Type::ConvertToGigagroup, DialogId(channel_id)}); + } DialogParticipantStatus status = [&] { bool has_left = (channel.flags_ & CHANNEL_FLAG_USER_HAS_LEFT) != 0; diff --git a/td/telegram/SuggestedAction.cpp b/td/telegram/SuggestedAction.cpp index e7d980c8e..f04df3fa9 100644 --- a/td/telegram/SuggestedAction.cpp +++ b/td/telegram/SuggestedAction.cpp @@ -52,8 +52,11 @@ SuggestedAction::SuggestedAction(const td_api::object_ptr(suggested_action.get()); - type_ = Type::ConvertToGigagroup; - dialog_id_ = DialogId(ChannelId(action->supergroup_id_)); + ChannelId channel_id(action->supergroup_id_); + if (channel_id.is_valid()) { + type_ = Type::ConvertToGigagroup; + dialog_id_ = DialogId(channel_id); + } break; } default: diff --git a/td/telegram/SuggestedAction.h b/td/telegram/SuggestedAction.h index 774e024b1..bb8477923 100644 --- a/td/telegram/SuggestedAction.h +++ b/td/telegram/SuggestedAction.h @@ -32,6 +32,10 @@ struct SuggestedAction { explicit SuggestedAction(const td_api::object_ptr &suggested_action); + bool is_empty() const { + return type_ == Type::Empty; + } + string get_suggested_action_str() const; td_api::object_ptr get_suggested_action_object() const; From eafdf2ebed2aa2a19cd12e0a648431efaa5cd800 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 23 Feb 2021 02:03:24 +0300 Subject: [PATCH 202/232] Add td_api::getMessageImportConfirmationText. --- td/generate/scheme/td_api.tl | 4 ++ td/generate/scheme/td_api.tlo | Bin 197932 -> 198040 bytes td/telegram/MessagesManager.cpp | 64 +++++++++++++++++++++++++++----- td/telegram/MessagesManager.h | 4 ++ td/telegram/Td.cpp | 13 +++++++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 4 ++ 7 files changed, 81 insertions(+), 10 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index a7d9dfafa..61a977498 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4455,6 +4455,10 @@ deleteFile file_id:int32 = Ok; //@description Returns information about a file with messages exported from another app @message_file_head Beginning of the message file; up to 100 first lines getMessageFileType message_file_head:string = MessageFileType; +//@description Returns a confirmation text to be shown to the user before starting message import +//@chat_id Identifier of a chat to which the messages will be imported. It must be an identifier of a private chat with a mutual contact or an identifier of a supergroup chat with can_change_info administrator right +getMessageImportConfirmationText chat_id:int53 = Text; + //@description Imports messages exported from another app //@chat_id Identifier of a chat to which the messages will be imported. It must be an identifier of a private chat with a mutual contact or an identifier of a supergroup chat with can_change_info administrator right //@message_file File with messages to import. Only inputFileLocal and inputFileGenerated are supported. The file must not be previously uploaded diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 89689edc66d5edce35b56c2d38dc13b5c9c29f89..6399a12ddf0953dfcece08caafa4972545bf896d 100644 GIT binary patch delta 82 zcmV-Y0ImP5iVT>G41k0Iv;vee0im~*G6EA@EU1A<7a(V4bWLS*b75y?No{a%a&$v) oZf0q6ZDDk2Z*Ej&cys^&0GL>IBA3ug0Wg=)fC3P=sEq=Icj?Ib%7 delta 27 jcmbQy&9kP9r=f*$3)2)M#s%A_8Zik+Zx`ufYAFW*kA(@? diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 8c0366879..e35613e95 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1049,6 +1049,38 @@ class CheckHistoryImportQuery : public Td::ResultHandler { } }; +class CheckHistoryImportPeerQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit CheckHistoryImportPeerQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + CHECK(input_peer != nullptr); + send_query(G()->net_query_creator().create(telegram_api::messages_checkHistoryImportPeer(std::move(input_peer)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for CheckHistoryImportPeerQuery: " << to_string(ptr); + promise_.set_value(std::move(ptr->confirm_text_)); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "CheckHistoryImportPeerQuery"); + promise_.set_error(std::move(status)); + } +}; + class InitHistoryImportQuery : public Td::ResultHandler { Promise promise_; FileId file_id_; @@ -26664,38 +26696,50 @@ void MessagesManager::get_message_file_type(const string &message_file_head, td_->create_handler(std::move(promise))->send(message_file_head); } -void MessagesManager::import_messages(DialogId dialog_id, const td_api::object_ptr &message_file, - const vector> &attached_files, - Promise &&promise) { +Status MessagesManager::can_import_messages(DialogId dialog_id) { if (!have_dialog_force(dialog_id)) { - return promise.set_error(Status::Error(400, "Chat not found")); + return Status::Error(400, "Chat not found"); } - TRY_STATUS_PROMISE(promise, can_send_message(dialog_id)); + TRY_STATUS(can_send_message(dialog_id)); switch (dialog_id.get_type()) { case DialogType::User: if (!td_->contacts_manager_->is_user_contact(dialog_id.get_user_id(), true)) { - return promise.set_error(Status::Error(400, "User must be a mutual contact")); + return Status::Error(400, "User must be a mutual contact"); } break; case DialogType::Chat: - return promise.set_error(Status::Error(400, "Basic groups must be updagraded to supergroups first")); + return Status::Error(400, "Basic groups must be updagraded to supergroups first"); case DialogType::Channel: if (is_broadcast_channel(dialog_id)) { - return promise.set_error(Status::Error(400, "Can't import messages to channels")); + return Status::Error(400, "Can't import messages to channels"); } if (!td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).can_change_info_and_settings()) { - return promise.set_error(Status::Error(400, "Not enough rights to import messages")); + return Status::Error(400, "Not enough rights to import messages"); } break; case DialogType::SecretChat: - return promise.set_error(Status::Error(400, "Can't import messages to secret chats")); + return Status::Error(400, "Can't import messages to secret chats"); case DialogType::None: default: UNREACHABLE(); } + return Status::OK(); +} + +void MessagesManager::get_message_import_confirmation_text(DialogId dialog_id, Promise &&promise) { + TRY_STATUS_PROMISE(promise, can_import_messages(dialog_id)); + + td_->create_handler(std::move(promise))->send(dialog_id); +} + +void MessagesManager::import_messages(DialogId dialog_id, const td_api::object_ptr &message_file, + const vector> &attached_files, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, can_import_messages(dialog_id)); + auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Document, message_file, dialog_id, false, false); if (r_file_id.is_error()) { // TODO TRY_RESULT_PROMISE(promise, ...); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 50f6f077e..676a691a9 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -401,6 +401,8 @@ class MessagesManager : public Actor { void get_message_file_type(const string &message_file_head, Promise> &&promise); + void get_message_import_confirmation_text(DialogId dialog_id, Promise &&promise); + void import_messages(DialogId dialog_id, const td_api::object_ptr &message_file, const vector> &attached_files, Promise &&promise); @@ -2842,6 +2844,8 @@ class MessagesManager : public Actor { void on_imported_message_attachments_uploaded(int64 random_id, Result &&result); + Status can_import_messages(DialogId dialog_id); + void add_sponsored_dialog(const Dialog *d, DialogSource source); void save_sponsored_dialog(); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index d012f7a14..6d7cab580 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6561,6 +6561,19 @@ void Td::on_request(uint64 id, td_api::getMessageFileType &request) { messages_manager_->get_message_file_type(request.message_file_head_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getMessageImportConfirmationText &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(make_tl_object(result.move_as_ok())); + } + }); + messages_manager_->get_message_import_confirmation_text(DialogId(request.chat_id_), std::move(query_promise)); +} + void Td::on_request(uint64 id, const td_api::importMessages &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 95c543cac..b97476b32 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -842,6 +842,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getMessageFileType &request); + void on_request(uint64 id, const td_api::getMessageImportConfirmationText &request); + void on_request(uint64 id, const td_api::importMessages &request); void on_request(uint64 id, const td_api::blockMessageSenderFromReplies &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 18cf32ba9..efa63ce81 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3002,6 +3002,10 @@ class CliClient final : public Actor { } send_request(td_api::make_object(message_file_head)); } + } else if (op == "gmict") { + string chat_id; + get_args(args, chat_id); + send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "im") { string chat_id; string message_file; From b41e36bea9e3bdd027f071192f46538b4b01a62d Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 23 Feb 2021 19:52:57 +0300 Subject: [PATCH 203/232] Fix SplitSource.php for PHP < 5.5. --- SplitSource.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SplitSource.php b/SplitSource.php index 35117f779..2c5da8fa6 100644 --- a/SplitSource.php +++ b/SplitSource.php @@ -151,7 +151,8 @@ function split_file($file, $chunks, $undo) { $in_define = false; } } - if (!empty(trim($current))) { + $current = trim($current); + if (!empty($current)) { fwrite(STDERR, "ERROR: $current".PHP_EOL); exit(); } From 11bbb5bfadb11296e1b8e07d31b85e632c453e2c Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 24 Feb 2021 11:24:48 +0300 Subject: [PATCH 204/232] tg_cli: add updateNewMessage processing in test DC. --- td/telegram/cli.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index efa63ce81..bd6eed041 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -834,6 +834,17 @@ class CliClient final : public Actor { } break; } + case td_api::updateNewMessage::ID: { + auto message = static_cast(result.get())->message_.get(); + if (message != nullptr && message->content_->get_id() == td_api::messageText::ID) { + auto chat_id = message->chat_id_; + auto text = static_cast(message->content_.get())->text_->text_; + if (text == "/start" && use_test_dc_) { + on_cmd(PSTRING() << "sm " << chat_id << " Hi!"); + } + } + break; + } case td_api::file::ID: on_get_file(*static_cast(result.get())); break; From 2dd85f92d8c15f88196992f54615999b4363781f Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 24 Feb 2021 12:19:55 +0300 Subject: [PATCH 205/232] Rename permanent invite links to primary invite links. --- td/generate/scheme/td_api.tl | 16 ++++++++-------- td/generate/scheme/td_api.tlo | Bin 198040 -> 198036 bytes td/telegram/Td.cpp | 2 +- td/telegram/Td.h | 2 +- td/telegram/cli.cpp | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 61a977498..3725372b7 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -553,9 +553,9 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@expire_date Point in time (Unix timestamp) when the link will expire; 0 if never //@member_limit Maximum number of members, which can join the chat using the link simultaneously; 0 if not limited //@member_count Number of chat members, which joined the chat using the link -//@is_permanent True, if the link is permanent. Permanent invite link can't have expire date or usage limit. There is exactly one permanent invite link for each administrator with can_invite_users right at a given time +//@is_primary True, if the link is primary. Primary invite link can't have expire date or usage limit. There is exactly one primary invite link for each administrator with can_invite_users right at a given time //@is_revoked True, if the link was revoked -chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_permanent:Bool is_revoked:Bool = ChatInviteLink; +chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_primary:Bool is_revoked:Bool = ChatInviteLink; //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; @@ -600,7 +600,7 @@ basicGroup id:int32 member_count:int32 status:ChatMemberStatus is_active:Bool up //@param_description Group description. Updated only after the basic group is opened //@creator_user_id User identifier of the creator of the group; 0 if unknown //@members Group members -//@invite_link Permanent invite link for this group; may be null. For chat administrators with can_invite_users right only. Updated only after the basic group is opened +//@invite_link Primary invite link for this group; may be null. For chat administrators with can_invite_users right only. Updated only after the basic group is opened basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 members:vector invite_link:chatInviteLink = BasicGroupFullInfo; @@ -640,7 +640,7 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co //@is_all_history_available True, if new chat members will have access to old messages. In public or discussion groups and both public and private channels, old messages are always available, so this option affects only private supergroups without a linked chat. The value of this field is only available for chat administrators //@sticker_set_id Identifier of the supergroup sticker set; 0 if none //@location Location to which the supergroup is connected; may be null -//@invite_link Permanent invite link for this chat; may be null. For chat administrators with can_invite_users right only +//@invite_link Primary invite link for this chat; may be null. For chat administrators with can_invite_users right only //@upgraded_from_basic_group_id Identifier of the basic group from which supergroup was upgraded; 0 if none //@upgraded_from_max_message_id Identifier of the last message in the basic group from which supergroup was upgraded; 0 if none supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_get_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:chatInviteLink upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo; @@ -4466,8 +4466,8 @@ getMessageImportConfirmationText chat_id:int53 = Text; importMessages chat_id:int53 message_file:InputFile attached_files:vector = Ok; -//@description Replaces current permanent invite link for a chat with a new permanent invite link. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right @chat_id Chat identifier -replacePermanentChatInviteLink chat_id:int53 = ChatInviteLink; +//@description Replaces current primary invite link for a chat with a new primary invite link. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right @chat_id Chat identifier +replacePrimaryChatInviteLink chat_id:int53 = ChatInviteLink; //@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier @@ -4475,7 +4475,7 @@ replacePermanentChatInviteLink chat_id:int53 = ChatInviteLink; //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-99999; pass 0 if not limited createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatInviteLink; -//@description Edits a non-permanent invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links +//@description Edits a non-primary invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links //@chat_id Chat identifier //@invite_link Invite link to be edited //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never @@ -4499,7 +4499,7 @@ getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool off getChatInviteLinkMembers chat_id:int53 invite_link:string offset_member:chatInviteLinkMember limit:int32 = ChatInviteLinkMembers; //@description Revokes invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links. -//-If a permanent link is revoked, then additionally to the revoked link returns new permanent link +//-If a primary link is revoked, then additionally to the revoked link returns new primary link //@chat_id Chat identifier //@invite_link Invite link to be revoked revokeChatInviteLink chat_id:int53 invite_link:string = ChatInviteLinks; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 6399a12ddf0953dfcece08caafa4972545bf896d..b87be208b71e4e4c9e643aa5f7961bb47547e1fe 100644 GIT binary patch delta 296 zcmbQy%`>H&XTuG4mfzDpwrsw`F5v`W{qUS34`EHn+`|cGO%5nlfiNCqC`_JF>;mN! zr@%!wXOskRNPvvuOU_6v@ysjBEJ^jr%*$o~8-Jljf^qVP1uPr|MVYyYMU@QG4@fb| zOphsJl9}w_#sM~FvrU~33z#+iK^c=m^O~mZYnm7fyud=+70xn7cte;q5=>J}APN__ zFzQTq@MQ{s$V~BN`oRy8nGnmg03v7-$222aV{Y|4dzqrtf}F(U)BuPhoKYRh0Cwv1 P3to%{5RDqUmB$XTuG4mW=e1`J3;sOE`g9(;f1e6gEqEmB>RRCS>m61hXaw6stfO4>A-c z&nR|*a*9*nqMI{H0yrc<=J6$GB$jyQm1UNs`ef#1Gl1>5P$R)Oy`YDYWxC2AM%Br@ zb?RW9n=R^mSir359}X}nG_PpdzM_e-zzZz2UFIxfgg1m)A;C1o1fo#Dm#GFKIKh|c z2R}s6pp+?Kx{WVW07S+hj%h};#)eyQl5$0<1v!bysR5})xruqHc_q%6uAANv#AE=` KsIrTxz#9OY;&ONZ diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 6d7cab580..8dd423b82 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6313,7 +6313,7 @@ void Td::on_request(uint64 id, td_api::getChatAdministrators &request) { CREATE_REQUEST(GetChatAdministratorsRequest, request.chat_id_); } -void Td::on_request(uint64 id, const td_api::replacePermanentChatInviteLink &request) { +void Td::on_request(uint64 id, const td_api::replacePrimaryChatInviteLink &request) { CREATE_REQUEST_PROMISE(); contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), 0, 0, true, std::move(promise)); } diff --git a/td/telegram/Td.h b/td/telegram/Td.h index b97476b32..b04d746cc 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -794,7 +794,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatAdministrators &request); - void on_request(uint64 id, const td_api::replacePermanentChatInviteLink &request); + void on_request(uint64 id, const td_api::replacePrimaryChatInviteLink &request); void on_request(uint64 id, const td_api::createChatInviteLink &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index bd6eed041..bd482c65f 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2721,7 +2721,7 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_group_call_id(args))); } else if (op == "rpcil") { string chat_id = args; - send_request(td_api::make_object(as_chat_id(chat_id))); + send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "ccilt") { string chat_id; int32 expire_date; From 37adbece28447a739de9f3dbbfbb0129a16029cb Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 24 Feb 2021 14:58:02 +0300 Subject: [PATCH 206/232] Allow to join already joined group call. --- td/telegram/GroupCallManager.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 69d703661..b76634c5b 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -500,7 +500,8 @@ void GroupCallManager::on_check_group_call_is_joined_timeout(GroupCallId group_c auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); - if (!group_call->is_joined || check_group_call_is_joined_timeout_.has_timeout(group_call_id.get())) { + if (!group_call->is_joined || pending_join_requests_.count(input_group_call_id) != 0 || + check_group_call_is_joined_timeout_.has_timeout(group_call_id.get())) { return; } @@ -846,7 +847,8 @@ void GroupCallManager::finish_check_group_call_is_joined(InputGroupCallId input_ auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr && group_call->is_inited); - if (!group_call->is_joined || check_group_call_is_joined_timeout_.has_timeout(group_call->group_call_id.get()) || + if (!group_call->is_joined || pending_join_requests_.count(input_group_call_id) != 0 || + check_group_call_is_joined_timeout_.has_timeout(group_call->group_call_id.get()) || group_call->audio_source != audio_source) { return; } @@ -1472,10 +1474,6 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, auto *group_call = get_group_call(input_group_call_id); CHECK(group_call != nullptr); - if (group_call->is_joined && !group_call->is_being_left) { - CHECK(group_call->is_inited); - return promise.set_error(Status::Error(400, "Group call is already joined")); - } if (group_call->is_inited && !group_call->is_active) { return promise.set_error(Status::Error(400, "Group call is finished")); } From 1e7795354d7e601cb601143c28710bc6323b9266 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 24 Feb 2021 22:25:10 +0300 Subject: [PATCH 207/232] Add td_api::getChatInviteLink. --- td/generate/scheme/td_api.tl | 5 +++ td/generate/scheme/td_api.tlo | Bin 198036 -> 198164 bytes td/telegram/ContactsManager.cpp | 57 ++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 3 ++ td/telegram/Td.cpp | 7 ++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 7 +++- 7 files changed, 80 insertions(+), 1 deletion(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 3725372b7..6e1e571d7 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4482,6 +4482,11 @@ createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatIn //@member_limit Maximum number of chat members that can join the chat by the link simultaneously; 0-99999; pass 0 if not limited editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; +//@description Returns information about an invite link. Requires administrator privileges and can_invite_users right in the chat to get own links and owner privileges to get other links +//@chat_id Chat identifier +//@invite_link Invite link to get +getChatInviteLink chat_id:int53 invite_link:string = ChatInviteLink; + //@description Returns list of chat administrators with number of their invite links. Requires owner privileges in the chat @chat_id Chat identifier getChatInviteLinkCounts chat_id:int53 = ChatInviteLinkCounts; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index b87be208b71e4e4c9e643aa5f7961bb47547e1fe..f5d348e2bb8d905b5c7370cfa60bcfc222ae9d04 100644 GIT binary patch delta 74 zcmV-Q0JZ;=iwu;E41k0Iv;vSa0iw5&G6EYy9Q{FR;}K_NbVF!ibV+V@X>?^wX>Mx( g0Q;8lgqLu>0xg$abpjufHVBs>x&jcltaJkCkSV> promise_; + DialogId dialog_id_; + + public: + explicit GetExportedChatInviteQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create( + telegram_api::messages_getExportedChatInvite(std::move(input_peer), invite_link))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + if (result_ptr.ok()->get_id() != telegram_api::messages_exportedChatInvite::ID) { + LOG(ERROR) << "Receive result for GetExportedChatInviteQuery: " << to_string(result_ptr.ok()); + return on_error(id, Status::Error(500, "Receive unexpected response")); + } + + auto result = move_tl_object_as(result_ptr.ok_ref()); + LOG(INFO) << "Receive result for GetExportedChatInviteQuery: " << to_string(result); + + td->contacts_manager_->on_get_users(std::move(result->users_), "GetExportedChatInviteQuery"); + + DialogInviteLink invite_link(std::move(result->invite_)); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Receive invalid invite link in " << dialog_id_; + return on_error(id, Status::Error(500, "Receive invalid invite link")); + } + promise_.set_value(invite_link.get_chat_invite_link_object(td->contacts_manager_.get())); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetExportedChatInviteQuery"); + promise_.set_error(std::move(status)); + } +}; + class GetExportedChatInvitesQuery : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -7115,6 +7165,13 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & ->send(dialog_id, invite_link, expire_date, usage_limit); } +void ContactsManager::get_dialog_invite_link(DialogId dialog_id, const string &invite_link, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, false)); + + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); +} + void ContactsManager::get_dialog_invite_link_counts( DialogId dialog_id, Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, true)); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 8f893a820..3f411463a 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -397,6 +397,9 @@ class ContactsManager : public Actor { void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, Promise> &&promise); + void get_dialog_invite_link(DialogId dialog_id, const string &invite_link, + Promise> &&promise); + void get_dialog_invite_link_counts(DialogId dialog_id, Promise> &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 8dd423b82..fed8aa6f2 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6331,6 +6331,13 @@ void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { request.member_limit_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::getChatInviteLink &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.invite_link_); + CREATE_REQUEST_PROMISE(); + contacts_manager_->get_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getChatInviteLinkCounts &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index b04d746cc..16ed25973 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -800,6 +800,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::editChatInviteLink &request); + void on_request(uint64 id, td_api::getChatInviteLink &request); + void on_request(uint64 id, const td_api::getChatInviteLinkCounts &request); void on_request(uint64 id, td_api::getChatInviteLinks &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index bd482c65f..09822f77e 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2744,7 +2744,12 @@ class CliClient final : public Actor { } else if (op == "gcilc") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id))); - } else if (op == "gcil" || op == "gcilr") { + } else if (op == "gcil") { + string chat_id; + string invite_link; + get_args(args, chat_id, invite_link); + send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); + } else if (op == "gcils" || op == "gcilr") { string chat_id; string administrator_user_id; int32 offset_date; From 4e8732a1978ce45f9c86ebbd6e598ededcb2edc8 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 25 Feb 2021 14:57:50 +0300 Subject: [PATCH 208/232] Minor test-crypto fixes. --- test/CMakeLists.txt | 17 +++++++++--- test/crypto.cpp | 66 ++++++++++++++++++++++++++------------------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 154a38153..7ad2117ea 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,9 +32,20 @@ set(TESTS_MAIN if (NOT CMAKE_CROSSCOMPILING OR EMSCRIPTEN) #Tests - add_executable(test-crypto EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/crypto.cpp) - target_include_directories(test-crypto PUBLIC ${OPENSSL_INCLUDE_DIR}) - target_link_libraries(test-crypto ${OPENSSL_LIBRARIES} tdutils tdcore) + if (OPENSSL_FOUND) + add_executable(test-crypto EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/crypto.cpp) + target_include_directories(test-crypto SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) + target_link_libraries(test-crypto PRIVATE ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES} tdutils tdcore) + + if (WIN32) + if (MINGW) + target_link_libraries(test-crypto PRIVATE ws2_32 mswsock crypt32) + else() + target_link_libraries(test-crypto PRIVATE ws2_32 Mswsock Crypt32) + endif() + endif() + endif() + add_executable(test-tdutils EXCLUDE_FROM_ALL ${TESTS_MAIN} ${TDUTILS_TEST_SOURCE}) add_executable(test-online EXCLUDE_FROM_ALL online.cpp) add_executable(run_all_tests ${TESTS_MAIN} ${TD_TEST_SOURCE}) diff --git a/test/crypto.cpp b/test/crypto.cpp index 198f02243..99e5ba5bd 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -1,20 +1,27 @@ - -#include -#include -#include -#include - -#include "td/utils/Status.h" -#include "td/utils/SharedSlice.h" -#include "td/utils/base64.h" -#include "td/utils/tl_storers.h" - -#include "td/utils/crypto.h" - +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// #include "td/mtproto/AuthKey.h" -#include "td/mtproto/KDF.h" #include "td/mtproto/Transport.h" +#include "td/utils/base64.h" +#include "td/utils/common.h" +#include "td/utils/crypto.h" +#include "td/utils/logging.h" +#include "td/utils/ScopeGuard.h" +#include "td/utils/SharedSlice.h" +#include "td/utils/Slice.h" +#include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/UInt.h" + +#include +#include +#include + class Handshake { public: struct KeyPair { @@ -23,17 +30,18 @@ class Handshake { }; static td::Result generate_key_pair() { - EVP_PKEY *pkey = NULL; - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(NID_X25519, NULL); - SCOPE_EXIT { - EVP_PKEY_CTX_free(pctx); - }; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(NID_X25519, nullptr); if (pctx == nullptr) { return td::Status::Error("Can't create EXP_PKEY_CTX"); } + SCOPE_EXIT { + EVP_PKEY_CTX_free(pctx); + }; if (EVP_PKEY_keygen_init(pctx) <= 0) { return td::Status::Error("Can't init keygen"); } + + EVP_PKEY *pkey = nullptr; if (EVP_PKEY_keygen(pctx, &pkey) <= 0) { return td::Status::Error("Can't generate key"); } @@ -138,7 +146,7 @@ class Handshake { } char *data_ptr = nullptr; auto data_size = BIO_get_mem_data(mem_bio, &data_ptr); - return std::string(data_ptr, data_size); + return td::SecureString(data_ptr, data_size); } }; @@ -152,7 +160,7 @@ struct HandshakeTest { namespace td { -void KDF2(Slice auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt128 *aes_iv) { +static void KDF3(Slice auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt128 *aes_iv) { uint8 buf_raw[36 + 16]; MutableSlice buf(buf_raw, 36 + 16); Slice msg_key_slice = as_slice(msg_key); @@ -183,9 +191,10 @@ void KDF2(Slice auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt1 aes_iv_slice.substr(4).copy_from(sha256_a.substr(8, 8)); aes_iv_slice.substr(12).copy_from(sha256_b.substr(24, 4)); } + } // namespace td -td::SecureString encrypt(td::Slice key, td::Slice data, td::int32 seqno, int X) { +static td::SecureString encrypt(td::Slice key, td::Slice data, td::int32 seqno, int X) { td::SecureString res(data.size() + 4 + 16); res.as_mutable_slice().substr(20).copy_from(data); @@ -201,7 +210,7 @@ td::SecureString encrypt(td::Slice key, td::Slice data, td::int32 seqno, int X) td::UInt128 msg_key = td::mtproto::Transport::calc_message_key2(auth_key, X, payload).second; td::UInt256 aes_key; td::UInt128 aes_iv; - td::KDF2(key, msg_key, X, &aes_key, &aes_iv); + td::KDF3(key, msg_key, X, &aes_key, &aes_iv); td::AesCtrState aes; aes.init(aes_key.as_slice(), aes_iv.as_slice()); aes.encrypt(payload, payload); @@ -209,7 +218,7 @@ td::SecureString encrypt(td::Slice key, td::Slice data, td::int32 seqno, int X) return res; } -HandshakeTest gen_test() { +static HandshakeTest gen_test() { HandshakeTest res; res.alice = Handshake::generate_key_pair().move_as_ok(); @@ -219,7 +228,7 @@ HandshakeTest gen_test() { return res; } -void run_test(const HandshakeTest &test) { +static void run_test(const HandshakeTest &test) { auto alice_secret = Handshake::calc_shared_secret(test.alice.private_key, test.bob.public_key).move_as_ok(); auto bob_secret = Handshake::calc_shared_secret(test.bob.private_key, test.alice.public_key).move_as_ok(); auto key = Handshake::expand_secret(alice_secret); @@ -229,13 +238,14 @@ void run_test(const HandshakeTest &test) { CHECK(key == test.key); } -td::StringBuilder &operator<<(td::StringBuilder &sb, const Handshake::KeyPair &key_pair) { +static td::StringBuilder &operator<<(td::StringBuilder &sb, const Handshake::KeyPair &key_pair) { sb << "\tpublic_key (base64url) = " << td::base64url_encode(key_pair.public_key) << "\n"; sb << "\tprivate_key (base64url) = " << td::base64url_encode(key_pair.private_key) << "\n"; sb << "\tprivate_key (pem) = \n" << Handshake::privateKeyToPem(key_pair.private_key).ok() << "\n"; return sb; } -td::StringBuilder &operator<<(td::StringBuilder &sb, const HandshakeTest &test) { + +static td::StringBuilder &operator<<(td::StringBuilder &sb, const HandshakeTest &test) { sb << "Alice\n" << test.alice; sb << "Bob\n" << test.bob; sb << "SharedSecret\n\t" << td::base64url_encode(test.shared_secret) << "\n"; @@ -247,7 +257,7 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const HandshakeTest &test) return sb; } -HandshakeTest pregenerated_test() { +static HandshakeTest pregenerated_test() { HandshakeTest test; test.alice.public_key = td::base64url_decode_secure("QlCME5fXLyyQQWeYnBiGAZbmzuD4ayOuADCFgmioOBY").move_as_ok(); test.alice.private_key = td::base64url_decode_secure("8NZGWKfRCJfiks74RG9_xHmYydarLiRsoq8VcJGPglg").move_as_ok(); From c733f8ed3470152cc91a70a53bb2361aa15e579d Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 25 Feb 2021 15:56:26 +0300 Subject: [PATCH 209/232] Minor improvements. --- td/telegram/ConfigManager.cpp | 4 +++- td/telegram/ContactsManager.cpp | 11 +++++++---- td/telegram/ContactsManager.h | 1 + td/telegram/DialogInviteLink.cpp | 1 - td/telegram/MessageContent.cpp | 2 +- td/telegram/MessageTtlSetting.h | 5 ++--- td/telegram/SuggestedAction.cpp | 2 ++ td/telegram/UpdatesManager.cpp | 2 ++ 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index cdfee4540..09bdd1e81 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -57,6 +57,7 @@ #include "td/utils/tl_parsers.h" #include "td/utils/UInt.h" +#include #include #include #include @@ -997,7 +998,8 @@ void ConfigManager::get_external_link(string &&link, Promise &&promise) auto url = r_url.move_as_ok(); url.protocol_ = HttpUrl::Protocol::Https; - Slice path = Slice(url.query_).truncate(url.query_.find_first_of("?#")); + Slice path = url.query_; + path.truncate(url.query_.find_first_of("?#")); Slice parameters_hash = Slice(url.query_).substr(path.size()); Slice parameters = parameters_hash; parameters.truncate(parameters.find('#')); diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 7845108cd..3755a1718 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -34,7 +34,6 @@ #include "td/telegram/PasswordManager.h" #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" -#include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatActor.h" #include "td/telegram/SecretChatsManager.h" #include "td/telegram/ServerMessageId.h" @@ -60,6 +59,7 @@ #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Random.h" +#include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include "td/utils/Time.h" #include "td/utils/tl_helpers.h" @@ -1778,7 +1778,7 @@ class GetExportedChatInviteQuery : public Td::ResultHandler { } if (result_ptr.ok()->get_id() != telegram_api::messages_exportedChatInvite::ID) { - LOG(ERROR) << "Receive result for GetExportedChatInviteQuery: " << to_string(result_ptr.ok()); + LOG(ERROR) << "Receive wrong result for GetExportedChatInviteQuery: " << to_string(result_ptr.ok()); return on_error(id, Status::Error(500, "Receive unexpected response")); } @@ -10048,7 +10048,7 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u register_user_photo(u, user_id, user_full->photo); } - if (user->bot_info_ != nullptr && on_update_bot_info(std::move(user->bot_info_), false)) { + if (on_update_bot_info(std::move(user->bot_info_), false)) { user_full->need_send_update = true; } update_user_full(user_full, user_id); @@ -10143,7 +10143,10 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim } bool ContactsManager::on_update_bot_info(tl_object_ptr &&new_bot_info, bool send_update) { - CHECK(new_bot_info != nullptr); + if (new_bot_info == nullptr) { + return false; + } + UserId user_id(new_bot_info->user_id_); if (!user_id.is_valid()) { LOG(ERROR) << "Receive invalid " << user_id; diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 3f411463a..b071e6623 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -43,6 +43,7 @@ #include "td/utils/Hints.h" #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" +#include "td/utils/Time.h" #include #include diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index 58bd468df..7da4b9e33 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -7,7 +7,6 @@ #include "td/telegram/DialogInviteLink.h" #include "td/telegram/ContactsManager.h" -#include "td/telegram/Global.h" #include "td/utils/logging.h" #include "td/utils/misc.h" diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 49fbf5655..5114b30bc 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -4629,7 +4629,7 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(action); if (set_messages_ttl->period_ < 0) { - LOG(ERROR) << "Receive wrong ttl = " << set_messages_ttl->period_; + LOG(ERROR) << "Receive wrong TTL = " << set_messages_ttl->period_; break; } return td::make_unique(set_messages_ttl->period_); diff --git a/td/telegram/MessageTtlSetting.h b/td/telegram/MessageTtlSetting.h index 5532eaa3c..7a7c9c690 100644 --- a/td/telegram/MessageTtlSetting.h +++ b/td/telegram/MessageTtlSetting.h @@ -6,13 +6,12 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/utils/common.h" #include "td/utils/StringBuilder.h" #include "td/utils/tl_helpers.h" +#include + namespace td { class MessageTtlSetting { diff --git a/td/telegram/SuggestedAction.cpp b/td/telegram/SuggestedAction.cpp index f04df3fa9..cae588b1f 100644 --- a/td/telegram/SuggestedAction.cpp +++ b/td/telegram/SuggestedAction.cpp @@ -10,6 +10,8 @@ #include "td/telegram/Global.h" #include "td/telegram/Td.h" +#include "td/actor/actor.h" + #include "td/utils/algorithm.h" #include diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 7bf193459..b6f9ba463 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -20,6 +20,7 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/DialogAction.h" #include "td/telegram/DialogId.h" +#include "td/telegram/DialogInviteLink.h" #include "td/telegram/FolderId.h" #include "td/telegram/Global.h" #include "td/telegram/GroupCallManager.h" @@ -57,6 +58,7 @@ #include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" +#include "td/utils/Time.h" #include #include From 18fc612b3b442023d4d8a82cbd79796b6988f744 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 25 Feb 2021 16:02:07 +0300 Subject: [PATCH 210/232] Rename administrator_user_id to creator_user_id. --- td/generate/scheme/td_api.tl | 12 +++++------ td/generate/scheme/td_api.tlo | Bin 198164 -> 198140 bytes td/telegram/ContactsManager.cpp | 33 +++++++++++++++---------------- td/telegram/ContactsManager.h | 5 ++--- td/telegram/DialogInviteLink.cpp | 14 ++++++------- td/telegram/DialogInviteLink.h | 12 +++++------ td/telegram/Td.cpp | 4 ++-- td/telegram/cli.cpp | 16 +++++++-------- 8 files changed, 47 insertions(+), 49 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 6e1e571d7..9a2bc5444 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -547,7 +547,7 @@ supergroupMembersFilterMention query:string message_thread_id:int53 = Supergroup supergroupMembersFilterBots = SupergroupMembersFilter; -//@description Contains a chat invite link @invite_link Chat invite link @administrator_user_id User identifier of an administrator created the link +//@description Contains a chat invite link @invite_link Chat invite link @creator_user_id User identifier of an administrator created the link //@date Point in time (Unix timestamp) when the link was created //@edit_date Point in time (Unix timestamp) when the link was last edited; 0 if never or unknown //@expire_date Point in time (Unix timestamp) when the link will expire; 0 if never @@ -555,7 +555,7 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@member_count Number of chat members, which joined the chat using the link //@is_primary True, if the link is primary. Primary invite link can't have expire date or usage limit. There is exactly one primary invite link for each administrator with can_invite_users right at a given time //@is_revoked True, if the link was revoked -chatInviteLink invite_link:string administrator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_primary:Bool is_revoked:Bool = ChatInviteLink; +chatInviteLink invite_link:string creator_user_id:int32 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_primary:Bool is_revoked:Bool = ChatInviteLink; //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; @@ -4492,12 +4492,12 @@ getChatInviteLinkCounts chat_id:int53 = ChatInviteLinkCounts; //@description Returns invite links for a chat created by specified administrator. Requires administrator privileges and can_invite_users right in the chat to get own links and owner privileges to get other links //@chat_id Chat identifier -//@administrator_user_id User identifier of a chat administrator. Must be an identifier of the current user for non-owner +//@creator_user_id User identifier of a chat administrator. Must be an identifier of the current user for non-owner //@is_revoked Pass true if revoked links needs to be returned instead of active or expired //@offset_date Creation date of an invite link starting after which to return invite links; use 0 to get results from the beginning //@offset_invite_link Invite link starting after which to return invite links; use empty string to get results from the beginning //@limit Maximum number of invite links to return -getChatInviteLinks chat_id:int53 administrator_user_id:int32 is_revoked:Bool offset_date:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; +getChatInviteLinks chat_id:int53 creator_user_id:int32 is_revoked:Bool offset_date:int32 offset_invite_link:string limit:int32 = ChatInviteLinks; //@description Returns chat members joined a chat by an invite link. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links @chat_id Chat identifier @invite_link Invite link for which to return chat members //@offset_member A chat member from which to return next chat members; use null to get results from the beginning @limit Maximum number of chat members to return @@ -4514,8 +4514,8 @@ deleteRevokedChatInviteLink chat_id:int53 invite_link:string = Ok; //@description Deletes all revoked chat invite links created by a given chat administrator. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links //@chat_id Chat identifier -//@administrator_user_id User identifier of a chat administrator, which links will be deleted. Must be an identifier of the current user for non-owner -deleteAllRevokedChatInviteLinks chat_id:int53 administrator_user_id:int32 = Ok; +//@creator_user_id User identifier of a chat administrator, which links will be deleted. Must be an identifier of the current user for non-owner +deleteAllRevokedChatInviteLinks chat_id:int53 creator_user_id:int32 = Ok; //@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked; must have URL "t.me", "telegram.me", or "telegram.dog" and query beginning with "/joinchat/" or "/+" checkChatInviteLink invite_link:string = ChatInviteLinkInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index f5d348e2bb8d905b5c7370cfa60bcfc222ae9d04..e594e6fd1ec13960deb8190e51ee02130f2aecda 100644 GIT binary patch delta 338 zcmbQz!}F(`XTuG4mdy=DA2;7&mv92JraR;_DQuSTDv^gsOvv2B31&?WC{}?m9%Lv? zo>A-qnSsvRu29Nk!^kq>?}91Q6UvxeKu(>0VFDw|WPv)F$rplIr{6uvq`Y0GoGHK= RVnjm_lL5rsCA*jkyaAm~jky2- delta 390 zcmey<%`>HkXTuG4mfzDpwrsw`F5v`bO*hDAQrIlvRU!|On2@=L6U>?%P^XVt5%>WXce4)mGS2QstH#0A@xTGkt zB!9YWGo$i!1%F1C=>oxwveW-QXH=e?Q+ohx;AVq59~LmHxuI!$Llff!FEDSq&sj$4 z?fPdKZN0(#?FAA{Q%oRI4&h9?)6d2-`9owne3@RbvLqHN{F+`8%oKoWx%~7E)=U!H zLYOKbew@Cblqq1cz!|pbR=!LS>$WSzF-;MJ82{l7BhdI#CizKb91t4>N||gJSvGSw me4HLo#^i!&gYx7DwJh5Ylrc?l2J4u%g~contacts_manager_->get_my_id()) { + if (invite_link.get_creator_user_id() != td->contacts_manager_->get_my_id()) { return on_error(id, Status::Error(500, "Receive invalid invite link creator")); } if (invite_link.is_permanent()) { @@ -1810,7 +1810,7 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, int32 offset_date, + void send(DialogId dialog_id, UserId creator_user_id, bool is_revoked, int32 offset_date, const string &offset_invite_link, int32 limit) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); @@ -1818,7 +1818,7 @@ class GetExportedChatInvitesQuery : public Td::ResultHandler { return on_error(0, Status::Error(400, "Can't access the chat")); } - auto input_user = td->contacts_manager_->get_input_user(administrator_user_id); + auto input_user = td->contacts_manager_->get_input_user(creator_user_id); CHECK(input_user != nullptr); int32 flags = 0; @@ -2034,7 +2034,7 @@ class RevokeChatInviteLinkQuery : public Td::ResultHandler { if (!invite_link.is_valid() || !new_invite_link.is_valid()) { return on_error(id, Status::Error(500, "Receive invalid invite link")); } - if (new_invite_link.get_administrator_user_id() == td->contacts_manager_->get_my_id() && + if (new_invite_link.get_creator_user_id() == td->contacts_manager_->get_my_id() && new_invite_link.is_permanent()) { td->contacts_manager_->on_get_permanent_dialog_invite_link(dialog_id_, new_invite_link); } @@ -2097,14 +2097,14 @@ class DeleteRevokedExportedChatInvitesQuery : public Td::ResultHandler { explicit DeleteRevokedExportedChatInvitesQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, UserId administrator_user_id) { + void send(DialogId dialog_id, UserId creator_user_id) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - auto input_user = td->contacts_manager_->get_input_user(administrator_user_id); + auto input_user = td->contacts_manager_->get_input_user(creator_user_id); CHECK(input_user != nullptr); send_query(G()->net_query_creator().create( @@ -7179,12 +7179,12 @@ void ContactsManager::get_dialog_invite_link_counts( td_->create_handler(std::move(promise))->send(dialog_id); } -void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, +void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, bool is_revoked, int32 offset_date, const string &offset_invite_link, int32 limit, Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, administrator_user_id != get_my_id())); + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, creator_user_id != get_my_id())); - if (!have_input_user(administrator_user_id)) { + if (!have_input_user(creator_user_id)) { return promise.set_error(Status::Error(400, "Administrator user not found")); } @@ -7193,7 +7193,7 @@ void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId adminis } td_->create_handler(std::move(promise)) - ->send(dialog_id, administrator_user_id, is_revoked, offset_date, offset_invite_link, limit); + ->send(dialog_id, creator_user_id, is_revoked, offset_date, offset_invite_link, limit); } void ContactsManager::get_dialog_invite_link_users( @@ -7230,16 +7230,15 @@ void ContactsManager::delete_revoked_dialog_invite_link(DialogId dialog_id, cons td_->create_handler(std::move(promise))->send(dialog_id, invite_link); } -void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, +void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, Promise &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, administrator_user_id != get_my_id())); + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, creator_user_id != get_my_id())); - if (!have_input_user(administrator_user_id)) { + if (!have_input_user(creator_user_id)) { return promise.set_error(Status::Error(400, "Administrator user not found")); } - td_->create_handler(std::move(promise)) - ->send(dialog_id, administrator_user_id); + td_->create_handler(std::move(promise))->send(dialog_id, creator_user_id); } void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise &&promise) const { @@ -9363,7 +9362,7 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val dependencies.user_ids.insert(participant.user_id); dependencies.user_ids.insert(participant.inviter_user_id); } - dependencies.user_ids.insert(chat_full->invite_link.get_administrator_user_id()); + dependencies.user_ids.insert(chat_full->invite_link.get_creator_user_id()); resolve_dependencies_force(td_, dependencies, "chat_full"); for (auto &participant : chat_full->participants) { @@ -9459,7 +9458,7 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s add_dialog_and_dependencies(dependencies, DialogId(channel_full->linked_channel_id)); dependencies.chat_ids.insert(channel_full->migrated_from_chat_id); dependencies.user_ids.insert(channel_full->bot_user_ids.begin(), channel_full->bot_user_ids.end()); - dependencies.user_ids.insert(channel_full->invite_link.get_administrator_user_id()); + dependencies.user_ids.insert(channel_full->invite_link.get_creator_user_id()); resolve_dependencies_force(td_, dependencies, "channel_full"); for (auto &user_id : channel_full->bot_user_ids) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index b071e6623..041fae8c8 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -404,7 +404,7 @@ class ContactsManager : public Actor { void get_dialog_invite_link_counts(DialogId dialog_id, Promise> &&promise); - void get_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, bool is_revoked, int32 offset_date, + void get_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, bool is_revoked, int32 offset_date, const string &offset_invite_link, int32 limit, Promise> &&promise); @@ -417,8 +417,7 @@ class ContactsManager : public Actor { void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); - void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId administrator_user_id, - Promise &&promise); + void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, Promise &&promise); void check_dialog_invite_link(const string &invite_link, Promise &&promise) const; diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index 7da4b9e33..565425b72 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -26,10 +26,10 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrlink_); LOG_IF(ERROR, !is_valid_invite_link(invite_link_)) << "Unsupported invite link " << invite_link_; - administrator_user_id_ = UserId(exported_invite->admin_id_); - if (!administrator_user_id_.is_valid()) { - LOG(ERROR) << "Receive invalid " << administrator_user_id_ << " as creator of a link " << invite_link_; - administrator_user_id_ = UserId(); + creator_user_id_ = UserId(exported_invite->admin_id_); + if (!creator_user_id_.is_valid()) { + LOG(ERROR) << "Receive invalid " << creator_user_id_ << " as creator of a link " << invite_link_; + creator_user_id_ = UserId(); } date_ = exported_invite->date_; if (date_ < 1000000000) { @@ -109,12 +109,12 @@ td_api::object_ptr DialogInviteLink::get_chat_invite_lin } return td_api::make_object( - invite_link_, contacts_manager->get_user_id_object(administrator_user_id_, "get_chat_invite_link_object"), date_, + invite_link_, contacts_manager->get_user_id_object(creator_user_id_, "get_chat_invite_link_object"), date_, edit_date_, expire_date_, usage_limit_, usage_count_, is_permanent_, is_revoked_); } bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { - return lhs.invite_link_ == rhs.invite_link_ && lhs.administrator_user_id_ == rhs.administrator_user_id_ && + return lhs.invite_link_ == rhs.invite_link_ && lhs.creator_user_id_ == rhs.creator_user_id_ && lhs.date_ == rhs.date_ && lhs.edit_date_ == rhs.edit_date_ && lhs.expire_date_ == rhs.expire_date_ && lhs.usage_limit_ == rhs.usage_limit_ && lhs.usage_count_ == rhs.usage_count_ && lhs.is_permanent_ == rhs.is_permanent_ && lhs.is_revoked_ == rhs.is_revoked_; @@ -125,7 +125,7 @@ bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { } StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link) { - return string_builder << "ChatInviteLink[" << invite_link.invite_link_ << " by " << invite_link.administrator_user_id_ + return string_builder << "ChatInviteLink[" << invite_link.invite_link_ << " by " << invite_link.creator_user_id_ << " created at " << invite_link.date_ << " edited at " << invite_link.edit_date_ << " expiring at " << invite_link.expire_date_ << " used by " << invite_link.usage_count_ << " with usage limit " << invite_link.usage_limit_ << "]"; diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index e7bde6044..b09fedb75 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -21,7 +21,7 @@ class ContactsManager; class DialogInviteLink { string invite_link_; - UserId administrator_user_id_; + UserId creator_user_id_; int32 date_ = 0; int32 edit_date_ = 0; int32 expire_date_ = 0; @@ -48,7 +48,7 @@ class DialogInviteLink { td_api::object_ptr get_chat_invite_link_object(const ContactsManager *contacts_manager) const; bool is_valid() const { - return !invite_link_.empty() && administrator_user_id_.is_valid() && date_ > 0; + return !invite_link_.empty() && creator_user_id_.is_valid() && date_ > 0; } bool is_permanent() const { @@ -59,8 +59,8 @@ class DialogInviteLink { return invite_link_; } - UserId get_administrator_user_id() const { - return administrator_user_id_; + UserId get_creator_user_id() const { + return creator_user_id_; } template @@ -79,7 +79,7 @@ class DialogInviteLink { STORE_FLAG(has_edit_date); END_STORE_FLAGS(); store(invite_link_, storer); - store(administrator_user_id_, storer); + store(creator_user_id_, storer); store(date_, storer); if (has_expire_date) { store(expire_date_, storer); @@ -111,7 +111,7 @@ class DialogInviteLink { PARSE_FLAG(has_edit_date); END_PARSE_FLAGS(); parse(invite_link_, parser); - parse(administrator_user_id_, parser); + parse(creator_user_id_, parser); parse(date_, parser); if (has_expire_date) { parse(expire_date_, parser); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index fed8aa6f2..18bdf9e38 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6348,7 +6348,7 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.offset_invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.administrator_user_id_), + contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.creator_user_id_), request.is_revoked_, request.offset_date_, request.offset_invite_link_, request.limit_, std::move(promise)); } @@ -6380,7 +6380,7 @@ void Td::on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &re CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), - UserId(request.administrator_user_id_), std::move(promise)); + UserId(request.creator_user_id_), std::move(promise)); } void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 09822f77e..e5ca054b2 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2751,14 +2751,14 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); } else if (op == "gcils" || op == "gcilr") { string chat_id; - string administrator_user_id; + string creator_user_id; int32 offset_date; string offset_invite_link; string limit; - get_args(args, chat_id, administrator_user_id, offset_date, offset_invite_link, limit); - send_request(td_api::make_object(as_chat_id(chat_id), - as_user_id(administrator_user_id), op == "gcilr", - offset_date, offset_invite_link, as_limit(limit))); + get_args(args, chat_id, creator_user_id, offset_date, offset_invite_link, limit); + send_request(td_api::make_object(as_chat_id(chat_id), as_user_id(creator_user_id), + op == "gcilr", offset_date, offset_invite_link, + as_limit(limit))); } else if (op == "gcilm") { string chat_id; string invite_link; @@ -2776,10 +2776,10 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(chat_id), invite_link)); } else if (op == "darcil") { string chat_id; - string administrator_user_id; - get_args(args, chat_id, administrator_user_id); + string creator_user_id; + get_args(args, chat_id, creator_user_id); send_request(td_api::make_object(as_chat_id(chat_id), - as_user_id(administrator_user_id))); + as_user_id(creator_user_id))); } else if (op == "ccil") { send_request(td_api::make_object(args)); } else if (op == "jcbil") { From b348e4e1bcc8b8182990ef1e73ff7afc5a207f9d Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 25 Feb 2021 16:09:55 +0300 Subject: [PATCH 211/232] Update version to 1.7.2. --- CMakeLists.txt | 2 +- README.md | 2 +- example/cpp/CMakeLists.txt | 2 +- example/uwp/extension.vsixmanifest | 2 +- td/telegram/Td.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3faec5b54..801f39b71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) -project(TDLib VERSION 1.7.1 LANGUAGES CXX C) +project(TDLib VERSION 1.7.2 LANGUAGES CXX C) if (NOT DEFINED CMAKE_MODULE_PATH) set(CMAKE_MODULE_PATH "") diff --git a/README.md b/README.md index 0acb25138..6f8e88024 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ target_link_libraries(YourTarget PRIVATE Td::TdStatic) Or you could install `TDLib` and then reference it in your CMakeLists.txt like this: ``` -find_package(Td 1.7.1 REQUIRED) +find_package(Td 1.7.2 REQUIRED) target_link_libraries(YourTarget PRIVATE Td::TdStatic) ``` See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt). diff --git a/example/cpp/CMakeLists.txt b/example/cpp/CMakeLists.txt index ebf3d1120..642bbb07a 100644 --- a/example/cpp/CMakeLists.txt +++ b/example/cpp/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(TdExample VERSION 1.0 LANGUAGES CXX) -find_package(Td 1.7.1 REQUIRED) +find_package(Td 1.7.2 REQUIRED) add_executable(tdjson_example tdjson_example.cpp) target_link_libraries(tdjson_example PRIVATE Td::TdJson) diff --git a/example/uwp/extension.vsixmanifest b/example/uwp/extension.vsixmanifest index 435f183ef..5d7989ac2 100644 --- a/example/uwp/extension.vsixmanifest +++ b/example/uwp/extension.vsixmanifest @@ -1,6 +1,6 @@ - + TDLib for Universal Windows Platform TDLib is a library for building Telegram clients https://core.telegram.org/tdlib diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 16ed25973..cd6272155 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -235,7 +235,7 @@ class Td final : public NetQueryCallback { static td_api::object_ptr static_request(td_api::object_ptr function); private: - static constexpr const char *TDLIB_VERSION = "1.7.1"; + static constexpr const char *TDLIB_VERSION = "1.7.2"; static constexpr int64 ONLINE_ALARM_ID = 0; static constexpr int64 PING_SERVER_ALARM_ID = -1; static constexpr int32 PING_SERVER_TIMEOUT = 300; From 3b3801abbec641ac66a050760d198dfc7bca92be Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 25 Feb 2021 21:09:13 +0300 Subject: [PATCH 212/232] Update layer 124. --- td/generate/scheme/telegram_api.tl | 4 ++-- td/generate/scheme/telegram_api.tlo | Bin 233316 -> 233388 bytes td/telegram/UpdatesManager.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 762ff1d9e..d37b95efc 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -357,8 +357,8 @@ updateChat#1330a196 chat_id:int = Update; updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector version:int = Update; updateGroupCall#a45eb99b chat_id:int call:GroupCall = Update; updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update; -updateChatParticipant#439b2b39 flags:# chat_id:int date:int user_id:int prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update; -updateChannelParticipant#961adf2 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update; +updateChatParticipant#f3b3781f flags:# chat_id:int date:int actor_id:int user_id:int prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update; +updateChannelParticipant#7fecb1ec flags:# channel_id:int date:int actor_id:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update; updateBotStopped#7f9488a user_id:int date:int stopped:Bool qts:int = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index f9bb1d3b8a9c98a42f80d4fdad8825228ff6b206..4c7daf5e995851a3309bb7fea5168b1d2f45eb4a 100644 GIT binary patch delta 2592 zcmaJ@ZD>>8f37)9+fpc9V17G9(j((Ket&AylK`guN}FNjfkBW3qiwcnT0o^Lk7GATAr7Q@5XX$yd-ag zlM9s_FT4|4si+vZ4BLfw$7Ld(ybP5Rv6p1NUT_InORr>r-M!!yGW81CxvxM#$j%$G zJewk3(OJ%8H5%7y+!%ckig0}bL=iC3N8Uz4L;=u~pbFF`V3$DbOi<#I3D_%SJFb#% za38C{wyQ8I6v1l*p#8+`7`sL&_I|0zmc__{3B7BX3H@h4rZLzr!+ag6z>$9F7l5$= zDGqrQ2lt*(v3--GJyrAO>lC%)I$fpgMnNJLZ>5f&~O%_di59c^J0xEkAF-Hao0C?+_K>*CD$6e70)ufKxGO@C`p!U~&*V7+#~O z*|PHVtcJVpLX&`x-zE5Y=el7z3|*07uoI7e>B9aVSW=1=-h_Cv4#4&OYP77J(sFdY;(y>v|F+K`z zp|oWP#GRq#5XcntjDllT%1ao0*Vr>v8L(m|@8K9LSal|0=N|_*T92_}+!BCdjE&Ji zrpG{ESgYVT!~_Z71WiZN1WiZx1QE-WDRN@8NwV`yl3l~(QaL)Gx64dIos2fMlt*}q z(h#4bcHcHlTzu0YXD&HSnby8AcGjyi@QHvwJtF~lbXKew%Pj1Xp=ZhI=qxk~nf*S= z-1o`ucwQF04<2D>nUe#+)%WBUubU&TkvXUpX4VIkas2^leGe#v(Yc#(^DvW3d%#dE z-kkP_1@%?1Vg}05|B#k;>>+{rK2+*OaQ`DpRQM6`PCZ&GhW409?s-he9VDZ5hOQ@s zTlGX1o@W=Nv0%$Wz&u=SG`DVLALLlH9%rIo40l;9FEH_LhYo_y9fvIFecO0+_r7lv zyko$`C~+;5N418k6)pSR6};|ICJixIt5o26h5aHBQVP?>7b(iI&cq@@rWP^zF3mr| zHAQUxX;lyoD&-h0VsgWlnVFXJ< z26rxJ`?&U%6}MYhgY9XQ^x5wvwgR0i*)HK5S;bn=V*JS+yNqMim$}b#Cs;x*Y+oA-ba}(fD$ZTT H*);wiKtCGE delta 2588 zcmaJ@ZAe>J818#+bkT)+Z50Ad*E`HGteLxR!Ys5jLx&v*vt8K=bC59^vic*Gbd%yh zsFZf|hxAzA(q)}L1}oID6lC^?jC31gKQb1r)>yU1RjYQf#_zA%?k4BFS6k5g>)!Wy zzs~c1oZI&O&e5wogV8)oo%{Oe*}=Sy!8~ZYp8CSZf54Y|Yl(m+MA~8ILhYEM@lcml zh-xF?5^r^I}@ z1H~etU=QUT>w$84f?kq&dqEen=3W^bJA0u*$kaQe=eYxZA-ieF%xu!|XclwvzpM6f zt;&O;S0D}dMnUk8t$k!}a3Q$I(kPK%9fc#pWqXu3i$&ptkR9xowio?62Kpf(#7%d} z{GetOC485B*asw#DvE8AN%EQor0~C7};06qhovk+^CK~lQ8&UgcyxY zfsRcURwsale}a`9<#gTOR4|KKFm$WQzF)DRBTA|X(?~5=G%Oya&a4=v zM4L(!nTt9GPCNDT4&ymhlJ2%1I(I86!}2lEwniJqq`!{f7+67^q4NRd5jzWS@Pgm= z;dge}gPsTA;+;2ayy9Jz$Cs@b7$?O)#)*#&0;?y8K#UFu)$K;xv@uSRY48Y*oCJCD zBxpnU6Pp@I!J}QQ3=^iFW9FHOGSL0#n*|Rxeyn6-fgdt4JOd8lK0YJs#?2Zv%|cj! z-nmWt4shrjmXE`e@r zsC|W8dREBdvMCF#K&kLzT_sZ04|O4`S*5I7SD{+SY->~n_Zp$zH40%g@@sr*dCx|Z z4;t9k?P`C#sq6|CENKbC%5E*O`omc{JoS*MZptrLBLbvnc7`gXyzCzSB< zCln$`GTMBoKPA7FPpKWWGNTxWh9R#7SShdnEbUSu`!vPJrOQmTkzp@uSON0(X!*jJ z>c%pr@)IBB2=>Cj#E5Y%ky`Ig!=sJsC#~pZtWqe2m{dajJtOX3h5asoQH9Y@gaq=j z#==?!s-`hH9~;+lRT|s4ZWRQ-l8>P@COgoX&a{**5B0gx_LlK0(vVKSB7t;DH8fv{ zU+q$|l5NLr4LRt3fjM%MdpfzdlTYtUnUx3je_N7^H~JtqeaDW}5)TcqA|AWoxcG~P z^){xV+sfK5~kMTV?LXL>o+JuoBb4i4z&@yf~silP=~!CTmQ|essUc(gmt# zlV0!)dmGoY*|-4c^Zkyx-R!S(Lg@XH)pmrgFt6Oy# &&up } case telegram_api::updateChatParticipant::ID: { auto update = move_tl_object_as(update_ptr); - td_->contacts_manager_->on_update_chat_participant(ChatId(update->chat_id_), UserId(update->user_id_), + td_->contacts_manager_->on_update_chat_participant(ChatId(update->chat_id_), UserId(update->actor_id_), update->date_, DialogInviteLink(std::move(update->invite_)), std::move(update->prev_participant_), std::move(update->new_participant_)); @@ -1987,7 +1987,7 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up } case telegram_api::updateChannelParticipant::ID: { auto update = move_tl_object_as(update_ptr); - td_->contacts_manager_->on_update_channel_participant(ChannelId(update->channel_id_), UserId(update->user_id_), + td_->contacts_manager_->on_update_channel_participant(ChannelId(update->channel_id_), UserId(update->actor_id_), update->date_, DialogInviteLink(std::move(update->invite_)), std::move(update->prev_participant_), std::move(update->new_participant_)); From 813b1411489b022b37d8e7ca1aeb5e3f7bd6bbc9 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Mar 2021 02:33:38 +0300 Subject: [PATCH 213/232] Fix channelParticipantSelf handling. --- td/telegram/ContactsManager.cpp | 18 ++++-------- td/telegram/ContactsManager.h | 3 -- td/telegram/DialogParticipant.cpp | 5 ++-- td/telegram/DialogParticipant.h | 3 +- td/telegram/MessagesManager.cpp | 46 +++++++++++++++---------------- 5 files changed, 32 insertions(+), 43 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 5ea9519d1..0cc211cd6 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -2931,7 +2931,7 @@ class GetChannelParticipantQuery : public Td::ResultHandler { LOG(INFO) << "Receive result for GetChannelParticipantQuery: " << to_string(participant); td->contacts_manager_->on_get_users(std::move(participant->users_), "GetChannelParticipantQuery"); - auto result = td->contacts_manager_->get_dialog_participant(channel_id_, std::move(participant->participant_)); + DialogParticipant result(std::move(participant->participant_)); if (!result.is_valid()) { LOG(ERROR) << "Receive invalid " << result; return promise_.set_error(Status::Error(500, "Receive invalid chat member")); @@ -3034,8 +3034,7 @@ class GetChannelAdministratorsQuery : public Td::ResultHandler { vector administrators; administrators.reserve(participants->participants_.size()); for (auto &participant : participants->participants_) { - DialogParticipant dialog_participant = - td->contacts_manager_->get_dialog_participant(channel_id_, std::move(participant)); + DialogParticipant dialog_participant(std::move(participant)); if (!dialog_participant.is_valid() || !dialog_participant.status.is_administrator()) { LOG(ERROR) << "Receive " << dialog_participant << " as an administrator of " << channel_id_; continue; @@ -11358,11 +11357,6 @@ const DialogParticipant *ContactsManager::get_chat_participant(const ChatFull *c return nullptr; } -DialogParticipant ContactsManager::get_dialog_participant( - ChannelId channel_id, tl_object_ptr &&participant_ptr) const { - return DialogParticipant(std::move(participant_ptr), get_channel_status(channel_id)); -} - tl_object_ptr ContactsManager::get_chat_member_object( const DialogParticipant &dialog_participant) const { UserId participant_user_id = dialog_participant.user_id; @@ -11464,7 +11458,7 @@ void ContactsManager::on_get_channel_participants( vector result; for (auto &participant_ptr : participants) { auto debug_participant = to_string(participant_ptr); - result.push_back(get_dialog_participant(channel_id, std::move(participant_ptr))); + result.emplace_back(std::move(participant_ptr)); const auto &participant = result.back(); if (!participant.is_valid() || (filter.is_bots() && !is_user_bot(participant.user_id)) || (filter.is_administrators() && !participant.status.is_administrator()) || @@ -13125,14 +13119,14 @@ void ContactsManager::on_update_channel_participant(ChannelId channel_id, UserId DialogParticipant old_dialog_participant; DialogParticipant new_dialog_participant; if (old_participant != nullptr) { - old_dialog_participant = get_dialog_participant(channel_id, std::move(old_participant)); + old_dialog_participant = DialogParticipant(std::move(old_participant)); if (new_participant == nullptr) { new_dialog_participant = DialogParticipant::left(old_dialog_participant.user_id); } else { - new_dialog_participant = get_dialog_participant(channel_id, std::move(new_participant)); + new_dialog_participant = DialogParticipant(std::move(new_participant)); } } else { - new_dialog_participant = get_dialog_participant(channel_id, std::move(new_participant)); + new_dialog_participant = DialogParticipant(std::move(new_participant)); old_dialog_participant = DialogParticipant::left(new_dialog_participant.user_id); } if (old_dialog_participant.user_id != new_dialog_participant.user_id || !old_dialog_participant.is_valid() || diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 041fae8c8..8a34325e4 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -521,9 +521,6 @@ class ContactsManager : public Actor { void ban_dialog_participant(DialogId dialog_id, UserId user_id, int32 banned_until_date, bool revoke_messages, Promise &&promise); - DialogParticipant get_dialog_participant(ChannelId channel_id, - tl_object_ptr &&participant_ptr) const; - DialogParticipant get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, Promise &&promise); diff --git a/td/telegram/DialogParticipant.cpp b/td/telegram/DialogParticipant.cpp index 8ba950082..339ea3a37 100644 --- a/td/telegram/DialogParticipant.cpp +++ b/td/telegram/DialogParticipant.cpp @@ -681,8 +681,7 @@ DialogParticipant::DialogParticipant(tl_object_ptr &&participant_ptr, - DialogParticipantStatus my_status) { +DialogParticipant::DialogParticipant(tl_object_ptr &&participant_ptr) { CHECK(participant_ptr != nullptr); switch (participant_ptr->get_id()) { case telegram_api::channelParticipant::ID: { @@ -693,7 +692,7 @@ DialogParticipant::DialogParticipant(tl_object_ptr(participant_ptr); *this = {UserId(participant->user_id_), UserId(participant->inviter_id_), participant->date_, - std::move(my_status)}; + DialogParticipantStatus::Member()}; break; } case telegram_api::channelParticipantCreator::ID: { diff --git a/td/telegram/DialogParticipant.h b/td/telegram/DialogParticipant.h index 9606ab33b..fb022a308 100644 --- a/td/telegram/DialogParticipant.h +++ b/td/telegram/DialogParticipant.h @@ -395,8 +395,7 @@ struct DialogParticipant { DialogParticipant(tl_object_ptr &&participant_ptr, int32 chat_creation_date, bool is_creator); - DialogParticipant(tl_object_ptr &&participant_ptr, - DialogParticipantStatus my_status); + explicit DialogParticipant(tl_object_ptr &&participant_ptr); static DialogParticipant left(UserId user_id) { return {user_id, UserId(), 0, DialogParticipantStatus::Left()}; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index e35613e95..75a4a3876 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -31223,48 +31223,48 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob return make_tl_object(); case telegram_api::channelAdminLogEventActionParticipantInvite::ID: { auto action = move_tl_object_as(action_ptr); - auto member = td_->contacts_manager_->get_dialog_participant(channel_id, std::move(action->participant_)); - if (!member.is_valid()) { - LOG(ERROR) << "Wrong invite: " << member; + DialogParticipant dialog_participant(std::move(action->participant_)); + if (!dialog_participant.is_valid()) { + LOG(ERROR) << "Wrong invite: " << dialog_participant; return nullptr; } return make_tl_object( - td_->contacts_manager_->get_user_id_object(member.user_id, "chatEventMemberInvited"), - member.status.get_chat_member_status_object()); + td_->contacts_manager_->get_user_id_object(dialog_participant.user_id, "chatEventMemberInvited"), + dialog_participant.status.get_chat_member_status_object()); } case telegram_api::channelAdminLogEventActionParticipantToggleBan::ID: { auto action = move_tl_object_as(action_ptr); - auto old_member = - td_->contacts_manager_->get_dialog_participant(channel_id, std::move(action->prev_participant_)); - auto new_member = td_->contacts_manager_->get_dialog_participant(channel_id, std::move(action->new_participant_)); - if (old_member.user_id != new_member.user_id) { - LOG(ERROR) << old_member.user_id << " VS " << new_member.user_id; + DialogParticipant old_dialog_participant(std::move(action->prev_participant_)); + DialogParticipant new_dialog_participant(std::move(action->new_participant_)); + if (old_dialog_participant.user_id != new_dialog_participant.user_id) { + LOG(ERROR) << old_dialog_participant.user_id << " VS " << new_dialog_participant.user_id; return nullptr; } - if (!old_member.is_valid() || !new_member.is_valid()) { - LOG(ERROR) << "Wrong restrict: " << old_member << " -> " << new_member; + if (!old_dialog_participant.is_valid() || !new_dialog_participant.is_valid()) { + LOG(ERROR) << "Wrong restrict: " << old_dialog_participant << " -> " << new_dialog_participant; return nullptr; } return make_tl_object( - td_->contacts_manager_->get_user_id_object(old_member.user_id, "chatEventMemberRestricted"), - old_member.status.get_chat_member_status_object(), new_member.status.get_chat_member_status_object()); + td_->contacts_manager_->get_user_id_object(old_dialog_participant.user_id, "chatEventMemberRestricted"), + old_dialog_participant.status.get_chat_member_status_object(), + new_dialog_participant.status.get_chat_member_status_object()); } case telegram_api::channelAdminLogEventActionParticipantToggleAdmin::ID: { auto action = move_tl_object_as(action_ptr); - auto old_member = - td_->contacts_manager_->get_dialog_participant(channel_id, std::move(action->prev_participant_)); - auto new_member = td_->contacts_manager_->get_dialog_participant(channel_id, std::move(action->new_participant_)); - if (old_member.user_id != new_member.user_id) { - LOG(ERROR) << old_member.user_id << " VS " << new_member.user_id; + DialogParticipant old_dialog_participant(std::move(action->prev_participant_)); + DialogParticipant new_dialog_participant(std::move(action->new_participant_)); + if (old_dialog_participant.user_id != new_dialog_participant.user_id) { + LOG(ERROR) << old_dialog_participant.user_id << " VS " << new_dialog_participant.user_id; return nullptr; } - if (!old_member.is_valid() || !new_member.is_valid()) { - LOG(ERROR) << "Wrong edit administrator: " << old_member << " -> " << new_member; + if (!old_dialog_participant.is_valid() || !new_dialog_participant.is_valid()) { + LOG(ERROR) << "Wrong edit administrator: " << old_dialog_participant << " -> " << new_dialog_participant; return nullptr; } return make_tl_object( - td_->contacts_manager_->get_user_id_object(old_member.user_id, "chatEventMemberPromoted"), - old_member.status.get_chat_member_status_object(), new_member.status.get_chat_member_status_object()); + td_->contacts_manager_->get_user_id_object(old_dialog_participant.user_id, "chatEventMemberPromoted"), + old_dialog_participant.status.get_chat_member_status_object(), + new_dialog_participant.status.get_chat_member_status_object()); } case telegram_api::channelAdminLogEventActionChangeTitle::ID: { auto action = move_tl_object_as(action_ptr); From de0a2a03e55df5fe95a29861f4323a3c0cdd1394 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Mar 2021 02:57:14 +0300 Subject: [PATCH 214/232] Return better error for empty invite links. --- td/telegram/ContactsManager.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 0cc211cd6..30705821a 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -7156,9 +7156,9 @@ void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string & Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - // if (!DialogInviteLink::is_valid_invite_link(invite_link)) { - // return promise.set_error(Status::Error(400, "Wrong invite link")); - // } + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } td_->create_handler(std::move(promise)) ->send(dialog_id, invite_link, expire_date, usage_limit); @@ -7168,6 +7168,10 @@ void ContactsManager::get_dialog_invite_link(DialogId dialog_id, const string &i Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, false)); + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); } @@ -7204,6 +7208,10 @@ void ContactsManager::get_dialog_invite_link_users( return promise.set_error(Status::Error(400, "Parameter limit must be positive")); } + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + UserId offset_user_id; int32 offset_date = 0; if (offset_member != nullptr) { @@ -7219,6 +7227,10 @@ void ContactsManager::revoke_dialog_invite_link(DialogId dialog_id, const string Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); } @@ -7226,6 +7238,10 @@ void ContactsManager::delete_revoked_dialog_invite_link(DialogId dialog_id, cons Promise &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); } From 2bc3d7424b454dd9fb3696a5896b7bc6c74b0213 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Mar 2021 04:19:50 +0300 Subject: [PATCH 215/232] Improve logging of unsupported SVG paths. --- td/telegram/StickersManager.cpp | 24 +++++++++++++----------- td/telegram/StickersManager.h | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index b8344bb87..7aee8865b 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1337,7 +1337,8 @@ tl_object_ptr StickersManager::get_mask_point_object(int32 po } } -vector> StickersManager::get_sticker_minithumbnail(CSlice path) { +vector> StickersManager::get_sticker_minithumbnail( + CSlice path, StickerSetId sticker_set_id) { if (path.empty()) { return {}; } @@ -1431,7 +1432,7 @@ vector> StickersManager::get_sticke while (!is_closed) { skip_commas(); if (path[pos] == '\0') { - LOG(ERROR) << "Receive unclosed path " << path; + LOG(ERROR) << "Receive unclosed path " << path << " in a sticker from " << sticker_set_id; return {}; } if (is_alpha(path[pos])) { @@ -1518,7 +1519,8 @@ vector> StickersManager::get_sticke is_closed = true; break; default: - LOG(ERROR) << "Receive invalid command " << command << " at pos " << pos << " in " << path; + LOG(ERROR) << "Receive invalid command " << command << " at pos " << pos << " in a sticker from " + << sticker_set_id << ": " << path; return {}; } } @@ -1600,8 +1602,8 @@ tl_object_ptr StickersManager::get_sticker_object(FileId file_i auto thumbnail_object = get_thumbnail_object(td_->file_manager_.get(), thumbnail, thumbnail_format); return make_tl_object(sticker->set_id.get(), sticker->dimensions.width, sticker->dimensions.height, sticker->alt, sticker->is_animated, sticker->is_mask, std::move(mask_position), - get_sticker_minithumbnail(sticker->minithumbnail), std::move(thumbnail_object), - td_->file_manager_->get_file_object(file_id)); + get_sticker_minithumbnail(sticker->minithumbnail, sticker->set_id), + std::move(thumbnail_object), td_->file_manager_->get_file_object(file_id)); } tl_object_ptr StickersManager::get_stickers_object(const vector &sticker_ids) const { @@ -1705,9 +1707,9 @@ tl_object_ptr StickersManager::get_sticker_set_object(Sticke sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); return make_tl_object( sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail), - get_sticker_minithumbnail(sticker_set->minithumbnail), sticker_set->is_installed && !sticker_set->is_archived, - sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, - sticker_set->is_viewed, std::move(stickers), std::move(emojis)); + get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id), + sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, + sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, std::move(stickers), std::move(emojis)); } tl_object_ptr StickersManager::get_sticker_sets_object(int32 total_count, @@ -1751,9 +1753,9 @@ tl_object_ptr StickersManager::get_sticker_set_info_obje sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); return make_tl_object( sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail), - get_sticker_minithumbnail(sticker_set->minithumbnail), sticker_set->is_installed && !sticker_set->is_archived, - sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, - sticker_set->is_viewed, + get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id), + sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, + sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, sticker_set->was_loaded ? narrow_cast(sticker_set->sticker_ids.size()) : sticker_set->sticker_count, std::move(stickers)); } diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index daadf6aa2..ed158732b 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -394,7 +394,8 @@ class StickersManager : public Actor { class UploadStickerFileCallback; - static vector> get_sticker_minithumbnail(CSlice path); + static vector> get_sticker_minithumbnail(CSlice path, + StickerSetId sticker_set_id); static tl_object_ptr get_mask_point_object(int32 point); From 1d9b817c9fdec56f6e259f1998cca5a216d9b622 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Mar 2021 04:24:05 +0300 Subject: [PATCH 216/232] Remove updateGroupCall logging for bots. --- td/telegram/GroupCallManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index b76634c5b..71b331239 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -2258,7 +2258,6 @@ void GroupCallManager::discard_group_call(GroupCallId group_call_id, Promise group_call_ptr, DialogId dialog_id) { if (td_->auth_manager_->is_bot()) { - LOG(ERROR) << "Receive " << to_string(group_call_ptr); return; } if (dialog_id != DialogId() && !dialog_id.is_valid()) { From 8430ba6d199f170b994dce498c5c6ad0041aa41f Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Mar 2021 23:29:55 +0300 Subject: [PATCH 217/232] Remove expect_jpeg_minithumbnail parameter from get_photo_size. --- td/telegram/DocumentsManager.cpp | 6 +++--- td/telegram/Photo.cpp | 12 ++++++------ td/telegram/Photo.h | 2 +- td/telegram/StickersManager.cpp | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index 5eb5003df..c5c12acdc 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -265,9 +265,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo if (document_type != Document::Type::VoiceNote) { for (auto &thumb : document->thumbs_) { - auto photo_size = get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash, - file_reference, DcId::create(dc_id), owner_dialog_id, std::move(thumb), - thumbnail_format, document_type != Document::Type::Sticker); + auto photo_size = + get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash, file_reference, + DcId::create(dc_id), owner_dialog_id, std::move(thumb), thumbnail_format); if (photo_size.get_offset() == 0) { if (!thumbnail.file_id.is_valid()) { thumbnail = std::move(photo_size.get<0>()); diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index 923f0f9a9..4c48ef839 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -359,7 +359,7 @@ PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice Variant get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash, std::string file_reference, DcId dc_id, DialogId owner_dialog_id, tl_object_ptr &&size_ptr, - PhotoFormat format, bool expect_jpeg_minithumbnail) { + PhotoFormat format) { CHECK(size_ptr != nullptr); tl_object_ptr location; @@ -394,8 +394,8 @@ Variant get_photo_size(FileManager *file_manager, PhotoSizeSo } case telegram_api::photoStrippedSize::ID: { auto size = move_tl_object_as(size_ptr); - if (!expect_jpeg_minithumbnail) { - LOG(ERROR) << "Receive unexpected JPEG minithumbnail"; + if (format != PhotoFormat::Jpeg) { + LOG(ERROR) << "Receive unexpected JPEG minithumbnail in photo of format " << format; return std::move(res); } return size->bytes_.as_slice().str(); @@ -420,8 +420,8 @@ Variant get_photo_size(FileManager *file_manager, PhotoSizeSo } case telegram_api::photoPathSize::ID: { auto size = move_tl_object_as(size_ptr); - if (expect_jpeg_minithumbnail) { - LOG(ERROR) << "Receive unexpected SVG minithumbnail"; + if (format != PhotoFormat::Tgs && format != PhotoFormat::Webp) { + LOG(ERROR) << "Receive unexpected SVG minithumbnail in photo of format " << format; return std::move(res); } return size->bytes_.as_slice().str(); @@ -717,7 +717,7 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr && for (auto &size_ptr : photo->sizes_) { auto photo_size = get_photo_size(file_manager, {FileType::Photo, 0}, photo->id_, photo->access_hash_, photo->file_reference_.as_slice().str(), dc_id, owner_dialog_id, - std::move(size_ptr), PhotoFormat::Jpeg, true); + std::move(size_ptr), PhotoFormat::Jpeg); if (photo_size.get_offset() == 0) { PhotoSize &size = photo_size.get<0>(); if (size.type == 0 || size.type == 't' || size.type == 'i' || size.type == 'u' || size.type == 'v') { diff --git a/td/telegram/Photo.h b/td/telegram/Photo.h index 933ff30b1..e8ddfed1e 100644 --- a/td/telegram/Photo.h +++ b/td/telegram/Photo.h @@ -113,7 +113,7 @@ PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice Variant get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash, string file_reference, DcId dc_id, DialogId owner_dialog_id, tl_object_ptr &&size_ptr, - PhotoFormat format, bool expect_jpeg_minithumbnail); + PhotoFormat format); AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash, string file_reference, DcId dc_id, DialogId owner_dialog_id, tl_object_ptr &&size); diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 7aee8865b..4b36e7033 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1888,7 +1888,7 @@ std::pair StickersManager::on_get_sticker_document( auto photo_size = get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, document_id, document->access_hash_, document->file_reference_.as_slice().str(), dc_id, DialogId(), std::move(thumb), - has_webp_thumbnail(sticker) ? PhotoFormat::Webp : PhotoFormat::Jpeg, false); + has_webp_thumbnail(sticker) ? PhotoFormat::Webp : PhotoFormat::Jpeg); if (photo_size.get_offset() == 0) { if (!thumbnail.file_id.is_valid()) { thumbnail = std::move(photo_size.get<0>()); @@ -2420,7 +2420,7 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptrthumbs_) { auto photo_size = get_photo_size(td_->file_manager_.get(), {set_id.get(), s->access_hash}, 0, 0, "", DcId::create(set->thumb_dc_id_), DialogId(), std::move(thumb), - is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp, false); + is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); if (photo_size.get_offset() == 0) { if (!thumbnail.file_id.is_valid()) { thumbnail = std::move(photo_size.get<0>()); From a36f0200846b626153bdfd332d36db1603f62ac1 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Mar 2021 23:48:48 +0300 Subject: [PATCH 218/232] Improve StickersManager::has_webp_thumbnail. --- td/telegram/DocumentsManager.cpp | 6 +++--- td/telegram/StickersManager.cpp | 23 +++++++++++++++++------ td/telegram/StickersManager.h | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index c5c12acdc..c396d1f2c 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -167,9 +167,6 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo default_extension = Slice("webp"); owner_dialog_id = DialogId(); file_name.clear(); - if (td_->stickers_manager_->has_webp_thumbnail(sticker) && remote_document.secret_file == nullptr) { - thumbnail_format = PhotoFormat::Webp; - } } else if (video != nullptr || default_document_type == Document::Type::Video || default_document_type == Document::Type::VideoNote) { bool is_video_note = default_document_type == Document::Type::VideoNote; @@ -252,6 +249,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo mime_type = std::move(document->mime_type_); file_reference = document->file_reference_.as_slice().str(); + if (document_type == Document::Type::Sticker && StickersManager::has_webp_thumbnail(document->thumbs_)) { + thumbnail_format = PhotoFormat::Webp; + } fix_animated_sticker_type(); if (owner_dialog_id.get_type() == DialogType::SecretChat) { diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 4b36e7033..2bb94dc10 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1830,8 +1830,19 @@ FileId StickersManager::on_get_sticker(unique_ptr new_sticker, bool rep return file_id; } -bool StickersManager::has_webp_thumbnail(const tl_object_ptr &sticker) { - // server tries to always replace user-provided thumbnail with server-side webp thumbnail +bool StickersManager::has_webp_thumbnail(const vector> &thumbnails) { + // server tries to always replace user-provided thumbnail with server-side WEBP thumbnail + // but there can be some old sticker documents or some big stickers + for (auto &size : thumbnails) { + switch (size->get_id()) { + case telegram_api::photoStrippedSize::ID: + case telegram_api::photoSizeProgressive::ID: + // WEBP thumbnail can't have stripped size or be progressive + return false; + default: + break; + } + } return true; } @@ -1884,11 +1895,11 @@ std::pair StickersManager::on_get_sticker_document( PhotoSize thumbnail; string minithumbnail; + auto thumbnail_format = has_webp_thumbnail(document->thumbs_) ? PhotoFormat::Webp : PhotoFormat::Jpeg; for (auto &thumb : document->thumbs_) { - auto photo_size = - get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, document_id, document->access_hash_, - document->file_reference_.as_slice().str(), dc_id, DialogId(), std::move(thumb), - has_webp_thumbnail(sticker) ? PhotoFormat::Webp : PhotoFormat::Jpeg); + auto photo_size = get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, document_id, + document->access_hash_, document->file_reference_.as_slice().str(), dc_id, + DialogId(), std::move(thumb), thumbnail_format); if (photo_size.get_offset() == 0) { if (!thumbnail.file_id.is_valid()) { thumbnail = std::move(photo_size.get<0>()); diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index ed158732b..e67c83be1 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -90,7 +90,7 @@ class StickersManager : public Actor { vector get_installed_sticker_sets(bool is_masks, Promise &&promise); - bool has_webp_thumbnail(const tl_object_ptr &sticker); + static bool has_webp_thumbnail(const vector> &thumbnails); StickerSetId get_sticker_set_id(const tl_object_ptr &set_ptr); From 1f01a405211d58180aff44b5ba46395c0f8c7b99 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Mar 2021 01:20:45 +0300 Subject: [PATCH 219/232] Bring bots offline if there are no updates for 5 minutes. --- td/telegram/Td.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 18bdf9e38..ff7f403ca 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3031,6 +3031,7 @@ void Td::on_alarm_timeout(int64 alarm_id) { updates_manager_->ping_server(); alarm_timeout_.set_timeout_in(PING_SERVER_ALARM_ID, PING_SERVER_TIMEOUT + Random::fast(0, PING_SERVER_TIMEOUT / 5)); + send_closure(G()->state_manager(), &StateManager::on_online, false); } return; } @@ -3537,6 +3538,7 @@ void Td::on_result(NetQueryPtr query) { if (auth_manager_->is_bot() && auth_manager_->is_authorized()) { alarm_timeout_.set_timeout_in(PING_SERVER_ALARM_ID, PING_SERVER_TIMEOUT + Random::fast(0, PING_SERVER_TIMEOUT / 5)); + send_closure(G()->state_manager(), &StateManager::on_online, true); } } return; From dd80b0ef2507bbbdd45b3a1c7c7c65ed9ecab075 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Mar 2021 04:06:02 +0300 Subject: [PATCH 220/232] Add Td::set_is_bot_online function. --- td/telegram/AuthManager.cpp | 2 +- td/telegram/Td.cpp | 15 ++++++++++++--- td/telegram/Td.h | 3 +++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/td/telegram/AuthManager.cpp b/td/telegram/AuthManager.cpp index f73da39e5..301105f15 100644 --- a/td/telegram/AuthManager.cpp +++ b/td/telegram/AuthManager.cpp @@ -729,7 +729,7 @@ void AuthManager::on_get_authorization(tl_object_ptrschedule_get_promo_data(0); G()->td_db()->get_binlog_pmc()->set("fetched_marks_as_unread", "1"); } else { - send_closure(G()->state_manager(), &StateManager::on_online, true); + td->set_is_bot_online(true); } send_closure(G()->config_manager(), &ConfigManager::request_config); if (query_id_ != 0) { diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index ff7f403ca..74a743674 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3031,7 +3031,7 @@ void Td::on_alarm_timeout(int64 alarm_id) { updates_manager_->ping_server(); alarm_timeout_.set_timeout_in(PING_SERVER_ALARM_ID, PING_SERVER_TIMEOUT + Random::fast(0, PING_SERVER_TIMEOUT / 5)); - send_closure(G()->state_manager(), &StateManager::on_online, false); + set_is_bot_online(false); } return; } @@ -3190,6 +3190,15 @@ bool Td::is_online() const { return is_online_; } +void Td::set_is_bot_online(bool is_bot_online) { + if (is_bot_online == is_bot_online_) { + return; + } + + is_bot_online_ = is_bot_online; + send_closure(G()->state_manager(), &StateManager::on_online, is_bot_online_); +} + bool Td::is_authentication_request(int32 id) { switch (id) { case td_api::setTdlibParameters::ID: @@ -3538,7 +3547,7 @@ void Td::on_result(NetQueryPtr query) { if (auth_manager_->is_bot() && auth_manager_->is_authorized()) { alarm_timeout_.set_timeout_in(PING_SERVER_ALARM_ID, PING_SERVER_TIMEOUT + Random::fast(0, PING_SERVER_TIMEOUT / 5)); - send_closure(G()->state_manager(), &StateManager::on_online, true); + set_is_bot_online(true); } } return; @@ -4177,7 +4186,7 @@ Status Td::init(DbKey key) { on_online_updated(true, true); } if (auth_manager_->is_bot()) { - send_closure(G()->state_manager(), &StateManager::on_online, true); + set_is_bot_online(true); } // Send binlog events to managers diff --git a/td/telegram/Td.h b/td/telegram/Td.h index cd6272155..829ece5f5 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -124,6 +124,8 @@ class Td final : public NetQueryCallback { bool is_online() const; + void set_is_bot_online(bool is_bot_online); + template ActorId create_net_actor(ArgsT &&... args) { auto slot_id = request_actors_.create(ActorOwn<>(), RequestActorIdType); @@ -287,6 +289,7 @@ class Td final : public NetQueryCallback { Container> request_actors_; bool is_online_ = false; + bool is_bot_online_ = false; NetQueryRef update_status_query_; int64 alarm_id_ = 1; From f06ea1bab368f443fc971c8bf029ba94ecdf7029 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Mar 2021 04:40:10 +0300 Subject: [PATCH 221/232] Improve sticker thumbnail format detection. --- td/telegram/StickersManager.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 2bb94dc10..6282fcde1 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1593,10 +1593,15 @@ tl_object_ptr StickersManager::get_sticker_object(FileId file_i const PhotoSize &thumbnail = sticker->m_thumbnail.file_id.is_valid() ? sticker->m_thumbnail : sticker->s_thumbnail; auto thumbnail_format = PhotoFormat::Webp; if (!sticker->set_id.is_valid()) { - auto file_view = td_->file_manager_->get_file_view(sticker->file_id); - if (file_view.is_encrypted()) { + auto sticker_file_view = td_->file_manager_->get_file_view(sticker->file_id); + if (sticker_file_view.is_encrypted()) { // uploaded to secret chats stickers have JPEG thumbnail instead of server-generated WEBP thumbnail_format = PhotoFormat::Jpeg; + } else { + auto thumbnail_file_view = td_->file_manager_->get_file_view(thumbnail.file_id); + if (ends_with(thumbnail_file_view.suggested_path(), ".jpg")) { + thumbnail_format = PhotoFormat::Jpeg; + } } } auto thumbnail_object = get_thumbnail_object(td_->file_manager_.get(), thumbnail, thumbnail_format); From fe6fe481f6f386acc0677fda8db1aebbf3dec0dc Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Mar 2021 04:48:02 +0300 Subject: [PATCH 222/232] Add document ID to SVG path warnings. --- td/telegram/StickersManager.cpp | 25 +++++++++++++++---------- td/telegram/StickersManager.h | 3 ++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 6282fcde1..c3c94f689 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1338,7 +1338,7 @@ tl_object_ptr StickersManager::get_mask_point_object(int32 po } vector> StickersManager::get_sticker_minithumbnail( - CSlice path, StickerSetId sticker_set_id) { + CSlice path, StickerSetId sticker_set_id, int64 document_id) { if (path.empty()) { return {}; } @@ -1432,7 +1432,7 @@ vector> StickersManager::get_sticke while (!is_closed) { skip_commas(); if (path[pos] == '\0') { - LOG(ERROR) << "Receive unclosed path " << path << " in a sticker from " << sticker_set_id; + LOG(ERROR) << "Receive unclosed path " << path << " in a sticker " << document_id << " from " << sticker_set_id; return {}; } if (is_alpha(path[pos])) { @@ -1519,8 +1519,8 @@ vector> StickersManager::get_sticke is_closed = true; break; default: - LOG(ERROR) << "Receive invalid command " << command << " at pos " << pos << " in a sticker from " - << sticker_set_id << ": " << path; + LOG(ERROR) << "Receive invalid command " << command << " at pos " << pos << " in a sticker " << document_id + << " from " << sticker_set_id << ": " << path; return {}; } } @@ -1592,12 +1592,16 @@ tl_object_ptr StickersManager::get_sticker_object(FileId file_i const PhotoSize &thumbnail = sticker->m_thumbnail.file_id.is_valid() ? sticker->m_thumbnail : sticker->s_thumbnail; auto thumbnail_format = PhotoFormat::Webp; + int64 document_id = -1; if (!sticker->set_id.is_valid()) { auto sticker_file_view = td_->file_manager_->get_file_view(sticker->file_id); if (sticker_file_view.is_encrypted()) { // uploaded to secret chats stickers have JPEG thumbnail instead of server-generated WEBP thumbnail_format = PhotoFormat::Jpeg; } else { + if (sticker_file_view.has_remote_location() && sticker_file_view.remote_location().is_document()) { + document_id = sticker_file_view.remote_location().get_id(); + } auto thumbnail_file_view = td_->file_manager_->get_file_view(thumbnail.file_id); if (ends_with(thumbnail_file_view.suggested_path(), ".jpg")) { thumbnail_format = PhotoFormat::Jpeg; @@ -1605,10 +1609,11 @@ tl_object_ptr StickersManager::get_sticker_object(FileId file_i } } auto thumbnail_object = get_thumbnail_object(td_->file_manager_.get(), thumbnail, thumbnail_format); - return make_tl_object(sticker->set_id.get(), sticker->dimensions.width, sticker->dimensions.height, - sticker->alt, sticker->is_animated, sticker->is_mask, std::move(mask_position), - get_sticker_minithumbnail(sticker->minithumbnail, sticker->set_id), - std::move(thumbnail_object), td_->file_manager_->get_file_object(file_id)); + return make_tl_object( + sticker->set_id.get(), sticker->dimensions.width, sticker->dimensions.height, sticker->alt, sticker->is_animated, + sticker->is_mask, std::move(mask_position), + get_sticker_minithumbnail(sticker->minithumbnail, sticker->set_id, document_id), std::move(thumbnail_object), + td_->file_manager_->get_file_object(file_id)); } tl_object_ptr StickersManager::get_stickers_object(const vector &sticker_ids) const { @@ -1712,7 +1717,7 @@ tl_object_ptr StickersManager::get_sticker_set_object(Sticke sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); return make_tl_object( sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail), - get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id), + get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2), sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, std::move(stickers), std::move(emojis)); } @@ -1758,7 +1763,7 @@ tl_object_ptr StickersManager::get_sticker_set_info_obje sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); return make_tl_object( sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail), - get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id), + get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3), sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, sticker_set->was_loaded ? narrow_cast(sticker_set->sticker_ids.size()) : sticker_set->sticker_count, diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index e67c83be1..b955eff57 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -395,7 +395,8 @@ class StickersManager : public Actor { class UploadStickerFileCallback; static vector> get_sticker_minithumbnail(CSlice path, - StickerSetId sticker_set_id); + StickerSetId sticker_set_id, + int64 document_id); static tl_object_ptr get_mask_point_object(int32 point); From 720a64962e6fd5a1785c2ac228cc86daf3239c39 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 2 Mar 2021 04:50:14 +0300 Subject: [PATCH 223/232] Improve sticker thumbnail format detection. --- td/telegram/StickersManager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index c3c94f689..b841472ff 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1602,9 +1602,12 @@ tl_object_ptr StickersManager::get_sticker_object(FileId file_i if (sticker_file_view.has_remote_location() && sticker_file_view.remote_location().is_document()) { document_id = sticker_file_view.remote_location().get_id(); } - auto thumbnail_file_view = td_->file_manager_->get_file_view(thumbnail.file_id); - if (ends_with(thumbnail_file_view.suggested_path(), ".jpg")) { - thumbnail_format = PhotoFormat::Jpeg; + + if (thumbnail.file_id.is_valid()) { + auto thumbnail_file_view = td_->file_manager_->get_file_view(thumbnail.file_id); + if (ends_with(thumbnail_file_view.suggested_path(), ".jpg")) { + thumbnail_format = PhotoFormat::Jpeg; + } } } } From b1da98faebfbcd88b9e566f559cebed6d3f47432 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Mar 2021 15:10:00 +0300 Subject: [PATCH 224/232] Fix possible integer overflow. --- td/telegram/MessagesManager.cpp | 6 +++--- td/telegram/UpdatesManager.cpp | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 75a4a3876..fbeec71ff 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -7425,7 +7425,7 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p return; } - if (old_pts + pts_count != new_pts) { + if (old_pts != new_pts - pts_count) { LOG(INFO) << "Found a gap in the " << dialog_id << " with pts = " << old_pts << ". new_pts = " << new_pts << ", pts_count = " << pts_count << " in update from " << source; @@ -35653,7 +35653,7 @@ void MessagesManager::on_get_channel_difference( } auto new_pts = dialog->pts_; - if (request_pts + request_limit > new_pts) { + if (request_pts > new_pts - request_limit) { LOG(ERROR) << "Receive channelDifferenceTooLong as result of getChannelDifference with pts = " << request_pts << " and limit = " << request_limit << " in " << dialog_id << ", but pts has changed from " << d->pts << " to " << new_pts << ". Difference: " << oneline(to_string(difference)); @@ -35748,7 +35748,7 @@ void MessagesManager::after_get_channel_difference(DialogId dialog_id, bool succ promise.set_value(Unit()); } if (d->postponed_channel_updates.size() != old_size || running_get_channel_difference(dialog_id)) { - if (success && update_pts < d->pts + 10000 && update_pts_count == 1) { + if (success && update_pts - 10000 < d->pts && update_pts_count == 1) { // if getChannelDifference was successful and update pts is near channel pts, // we hope that the update eventually can be applied LOG(INFO) << "Can't apply postponed channel updates"; diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 8a7d6c2fe..4a56771e6 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -357,7 +357,7 @@ Promise<> UpdatesManager::set_pts(int32 pts, const char *source) { } result = add_pts(pts); - if (last_get_difference_pts_ + FORCED_GET_DIFFERENCE_PTS_DIFF < get_pts()) { + if (last_get_difference_pts_ < get_pts() - FORCED_GET_DIFFERENCE_PTS_DIFF) { last_get_difference_pts_ = get_pts(); schedule_get_difference("set_pts"); } @@ -1330,9 +1330,9 @@ void UpdatesManager::on_get_difference(tl_object_ptrintermediate_state_); if (get_pts() != std::numeric_limits::max() && state->date_ == get_date() && (state->pts_ == get_pts() || - (min_postponed_update_pts_ != 0 && state->pts_ >= min_postponed_update_pts_ + 1000)) && + (min_postponed_update_pts_ != 0 && state->pts_ - 1000 >= min_postponed_update_pts_)) && (state->qts_ == get_qts() || - (min_postponed_update_qts_ != 0 && state->qts_ >= min_postponed_update_qts_ + 1000))) { + (min_postponed_update_qts_ != 0 && state->qts_ - 1000 >= min_postponed_update_qts_))) { on_get_updates_state(std::move(state), "get difference final slice"); VLOG(get_difference) << "Trying to switch back from getDifference to update processing"; break; @@ -1676,7 +1676,7 @@ void UpdatesManager::add_pending_qts_update(tl_object_ptr CHECK(!running_get_difference_); - if (qts > old_qts + 1 && old_qts > 0) { + if (qts - 1 > old_qts && old_qts > 0) { LOG(INFO) << "Postpone update with qts = " << qts; if (pending_qts_updates_.empty()) { set_qts_gap_timeout(MAX_UNFILLED_GAP_TIME); @@ -1862,7 +1862,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr } } - if (new_pts <= old_pts || (old_pts >= 1 && new_pts > old_pts + 500000000)) { + if (new_pts <= old_pts || (old_pts >= 1 && new_pts - 500000000 > old_pts)) { td_->messages_manager_->skip_old_pending_pts_update(std::move(update), new_pts, old_pts, pts_count, source); return promise.set_value(Unit()); } @@ -1876,7 +1876,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr return; } - if (old_pts + pts_count > new_pts) { + if (old_pts > new_pts - pts_count) { LOG(WARNING) << "Have old_pts (= " << old_pts << ") + pts_count (= " << pts_count << ") > new_pts (= " << new_pts << "). Logged in " << G()->shared_config().get_option_integer("authorization_date") << ". Update from " << source << " = " << oneline(to_string(update)); @@ -1890,7 +1890,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr accumulated_pts_ = new_pts; } - if (old_pts + accumulated_pts_count_ > accumulated_pts_) { + if (old_pts > accumulated_pts_ - accumulated_pts_count_) { LOG(WARNING) << "Have old_pts (= " << old_pts << ") + accumulated_pts_count (= " << accumulated_pts_count_ << ") > accumulated_pts (= " << accumulated_pts_ << "). new_pts = " << new_pts << ", pts_count = " << pts_count << ". Logged in " @@ -1903,7 +1903,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr LOG_IF(INFO, pts_count == 0 && update->get_id() != dummyUpdate::ID) << "Skip useless update " << to_string(update); - if (pending_pts_updates_.empty() && old_pts + accumulated_pts_count_ == accumulated_pts_ && + if (pending_pts_updates_.empty() && old_pts == accumulated_pts_ - accumulated_pts_count_ && !pts_gap_timeout_.has_timeout()) { if (pts_count > 0) { td_->messages_manager_->process_pts_update(std::move(update)); @@ -1919,13 +1919,13 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr pending_pts_updates_.emplace(new_pts, PendingPtsUpdate(std::move(update), new_pts, pts_count, std::move(promise))); - if (old_pts + accumulated_pts_count_ < accumulated_pts_) { + if (old_pts < accumulated_pts_ - accumulated_pts_count_) { set_pts_gap_timeout(MAX_UNFILLED_GAP_TIME); last_pts_gap_time_ = Time::now(); return; } - CHECK(old_pts + accumulated_pts_count_ == accumulated_pts_); + CHECK(old_pts == accumulated_pts_ - accumulated_pts_count_); process_pending_pts_updates(); } @@ -1956,7 +1956,7 @@ void UpdatesManager::process_seq_updates(int32 seq_end, int32 date, void UpdatesManager::process_qts_update(tl_object_ptr &&update_ptr, int32 qts, Promise &&promise) { LOG(DEBUG) << "Process " << to_string(update_ptr); - if (last_get_difference_qts_ + FORCED_GET_DIFFERENCE_PTS_DIFF < qts) { + if (last_get_difference_qts_ < qts - FORCED_GET_DIFFERENCE_PTS_DIFF) { if (last_get_difference_qts_ != 0) { schedule_get_difference("process_qts_update"); } From 0c409cb79e045aadbb9aaceafe34b6438ffd415f Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Mar 2021 16:33:17 +0300 Subject: [PATCH 225/232] Ignore minithumbnails for JPEG sticker thumbnails. --- td/telegram/DocumentsManager.cpp | 3 +++ td/telegram/StickersManager.cpp | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index c396d1f2c..028b77ae4 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -435,6 +435,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo std::move(mime_type), !is_web); break; case Document::Type::Sticker: + if (thumbnail_format == PhotoFormat::Jpeg) { + minithumbnail = string(); + } td_->stickers_manager_->create_sticker(file_id, std::move(minithumbnail), std::move(thumbnail), dimensions, std::move(sticker), is_animated_sticker, load_data_multipromise_ptr); break; diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index b841472ff..f2455084b 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1919,7 +1919,9 @@ std::pair StickersManager::on_get_sticker_document( } break; } else { - minithumbnail = std::move(photo_size.get<1>()); + if (thumbnail_format == PhotoFormat::Webp) { + minithumbnail = std::move(photo_size.get<1>()); + } } } From b88f5ba6f30aa7aadbaf54c5fa01b65aba8bc1bd Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 3 Mar 2021 19:30:33 +0300 Subject: [PATCH 226/232] Fix banChatMember for bots. --- td/telegram/Td.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 74a743674..201cad534 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6272,7 +6272,6 @@ void Td::on_request(uint64 id, td_api::setChatMemberStatus &request) { } void Td::on_request(uint64 id, const td_api::banChatMember &request) { - CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); contacts_manager_->ban_dialog_participant(DialogId(request.chat_id_), UserId(request.user_id_), request.banned_until_date_, request.revoke_messages_, std::move(promise)); From 08cdb75c0fc07daad93b219ad14cc1344c1bf022 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Mar 2021 01:40:56 +0300 Subject: [PATCH 227/232] Add tl-parser. --- td/generate/tl-parser/CMakeLists.txt | 19 + td/generate/tl-parser/LICENSE | 339 +++ td/generate/tl-parser/crc32.c | 345 +++ td/generate/tl-parser/crc32.h | 37 + td/generate/tl-parser/portable_endian.h | 124 + td/generate/tl-parser/tl-parser-tree.h | 178 ++ td/generate/tl-parser/tl-parser.c | 3078 +++++++++++++++++++++++ td/generate/tl-parser/tl-parser.h | 223 ++ td/generate/tl-parser/tl-tl.h | 55 + td/generate/tl-parser/tlc.c | 165 ++ td/generate/tl-parser/wgetopt.c | 1274 ++++++++++ td/generate/tl-parser/wgetopt.h | 193 ++ 12 files changed, 6030 insertions(+) create mode 100644 td/generate/tl-parser/CMakeLists.txt create mode 100644 td/generate/tl-parser/LICENSE create mode 100644 td/generate/tl-parser/crc32.c create mode 100644 td/generate/tl-parser/crc32.h create mode 100644 td/generate/tl-parser/portable_endian.h create mode 100644 td/generate/tl-parser/tl-parser-tree.h create mode 100644 td/generate/tl-parser/tl-parser.c create mode 100644 td/generate/tl-parser/tl-parser.h create mode 100644 td/generate/tl-parser/tl-tl.h create mode 100644 td/generate/tl-parser/tlc.c create mode 100644 td/generate/tl-parser/wgetopt.c create mode 100644 td/generate/tl-parser/wgetopt.h diff --git a/td/generate/tl-parser/CMakeLists.txt b/td/generate/tl-parser/CMakeLists.txt new file mode 100644 index 000000000..05c3ca016 --- /dev/null +++ b/td/generate/tl-parser/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.0 FATAL_ERROR) + +project(tl-parser LANGUAGES C) + +set(SOURCES crc32.h crc32.c tlc.c tl-parser.c tl-parser.h tl-parser-tree.h tl-tl.h portable_endian.h) + +if (WIN32) + add_definitions("-D_CRT_SECURE_NO_WARNINGS") + list(APPEND SOURCES wgetopt.c wgetopt.h) + if (MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8 /wd4101 /wd4244") + endif() +endif() + +add_executable(${PROJECT_NAME} ${SOURCES}) + +if (NOT WIN32) + target_link_libraries(${PROJECT_NAME} m) +endif() diff --git a/td/generate/tl-parser/LICENSE b/td/generate/tl-parser/LICENSE new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/td/generate/tl-parser/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/td/generate/tl-parser/crc32.c b/td/generate/tl-parser/crc32.c new file mode 100644 index 000000000..b7e02181b --- /dev/null +++ b/td/generate/tl-parser/crc32.c @@ -0,0 +1,345 @@ +/* + This file is part of VK/KittenPHP-DB-Engine Library. + + VK/KittenPHP-DB-Engine Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + VK/KittenPHP-DB-Engine Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with VK/KittenPHP-DB-Engine Library. If not, see . + + Copyright 2009-2012 Vkontakte Ltd + 2009-2012 Nikolai Durov + 2009-2012 Andrei Lopatin + 2012 Anton Maydell +*/ + +#include +#include +#include + +#include "crc32.h" + +unsigned int crc32_table[256] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +unsigned int crc32_table2[256] = +{ + 0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, + 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7, + 0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, + 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf, + 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, + 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, + 0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, + 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e, + 0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, + 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, + 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, + 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d, + 0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, + 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034, + 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, + 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, + 0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, + 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2, + 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, + 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, + 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, + 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93, + 0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, + 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b, + 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, + 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, + 0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, + 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768, + 0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, + 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, + 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, + 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539, + 0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, + 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c, + 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, + 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, + 0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, + 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd, + 0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, + 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, + 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, + 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, + 0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, + 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026, + 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, + 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, + 0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, + 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277, + 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, + 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, + 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, + 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81, + 0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, + 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8, + 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, + 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, + 0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, + 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b, + 0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, + 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, + 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, + 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a, + 0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, + 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72, +}; + +unsigned int crc32_table1[256] = +{ + 0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, + 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685, + 0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, + 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d, + 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, + 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, + 0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, + 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d, + 0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, + 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, + 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, + 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd, + 0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, + 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315, + 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, + 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, + 0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, + 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45, + 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, + 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, + 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, + 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835, + 0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, + 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d, + 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, + 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, + 0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, + 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d, + 0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, + 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, + 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, + 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, + 0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, + 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05, + 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, + 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, + 0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, + 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75, + 0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, + 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, + 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, + 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5, + 0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, + 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d, + 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, + 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, + 0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, + 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d, + 0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, + 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, + 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, + 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d, + 0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, + 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5, + 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, + 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, + 0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, + 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625, + 0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, + 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, + 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, + 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555, + 0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, + 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed, +}; + +unsigned int crc32_table0[256] = { + 0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, + 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9, + 0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, + 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056, + 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, + 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, + 0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, + 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9, + 0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, + 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, + 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, + 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68, + 0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, + 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018, + 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, + 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, + 0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, + 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084, + 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, + 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, + 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, + 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b, + 0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, + 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4, + 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, + 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, + 0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, + 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755, + 0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, + 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, + 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, + 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca, + 0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, + 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82, + 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, + 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, + 0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, + 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d, + 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, + 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, + 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, + 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc, + 0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, + 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953, + 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, + 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, + 0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, + 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc, + 0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, + 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, + 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, + 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50, + 0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, + 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120, + 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, + 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, + 0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, + 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981, + 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, + 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, + 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, + 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, + 0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, + 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1, +}; + +static unsigned int crc32_partial (const void *data, int len, unsigned crc) { + const int *p = (const int *) data; + int x; +#define DO_ONE(v) crc ^= v; crc = crc32_table0[crc & 0xff] ^ crc32_table1[(crc & 0xff00) >> 8] ^ crc32_table2[(crc & 0xff0000) >> 16] ^ crc32_table[crc >> 24]; +#define DO_FOUR(p) DO_ONE((p)[0]); DO_ONE((p)[1]); DO_ONE((p)[2]); DO_ONE((p)[3]); + + for (x = (len >> 5); x > 0; x--) { + DO_FOUR (p); + DO_FOUR (p + 4); + p += 8; + } + if (len & 16) { + DO_FOUR (p); + p += 4; + } + if (len & 8) { + DO_ONE (p[0]); + DO_ONE (p[1]); + p += 2; + } + if (len & 4) { + DO_ONE (*p++); + } + /* + for (x = (len >> 2) & 7; x > 0; x--) { + DO_ONE (*p++); + } + */ +#undef DO_ONE +#undef DO_FOUR + const char *q = (const char *) p; + if (len & 2) { + crc = crc32_table[(crc ^ q[0]) & 0xff] ^ (crc >> 8); + crc = crc32_table[(crc ^ q[1]) & 0xff] ^ (crc >> 8); + q += 2; + } + if (len & 1) { + crc = crc32_table[(crc ^ *q++) & 0xff] ^ (crc >> 8); + } + return crc; +} + +unsigned int compute_crc32 (const void *data, int len) { + return crc32_partial (data, len, -1) ^ -1; +} + diff --git a/td/generate/tl-parser/crc32.h b/td/generate/tl-parser/crc32.h new file mode 100644 index 000000000..b461784b3 --- /dev/null +++ b/td/generate/tl-parser/crc32.h @@ -0,0 +1,37 @@ +/* + This file is part of VK/KittenPHP-DB-Engine Library. + + VK/KittenPHP-DB-Engine Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + VK/KittenPHP-DB-Engine Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with VK/KittenPHP-DB-Engine Library. If not, see . + + Copyright 2009-2012 Vkontakte Ltd + 2009-2012 Nikolai Durov + 2009-2012 Andrei Lopatin + 2012 Anton Maydell +*/ + +#ifndef __CRC32_H__ +#define __CRC32_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned int compute_crc32 (const void *data, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/td/generate/tl-parser/portable_endian.h b/td/generate/tl-parser/portable_endian.h new file mode 100644 index 000000000..9f6d2f41c --- /dev/null +++ b/td/generate/tl-parser/portable_endian.h @@ -0,0 +1,124 @@ +// "License": Public Domain +// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. +// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to +// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it +// an example on how to get the endian conversion functions on different platforms. + +/* Originally cloned from https://gist.github.com/PkmX/63dd23f28ba885be53a5 + * Commit was: 1eca2ab34f2301b9641aa73d1016b951fff3fc39 + * Re-published at https://github.com/BenWiederhake/portable-endian.h to provide a means to submit patches and report issues. */ + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# include +#ifdef __MINGW32__ +# include +#endif + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) htonll(x) +# define htole64(x) (x) +# define be64toh(x) ntohll(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#else + +# error platform not supported + +#endif + +#endif diff --git a/td/generate/tl-parser/tl-parser-tree.h b/td/generate/tl-parser/tl-parser-tree.h new file mode 100644 index 000000000..fba7c6752 --- /dev/null +++ b/td/generate/tl-parser/tl-parser-tree.h @@ -0,0 +1,178 @@ +/* + This file is part of tgl-library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Copyright Vitaly Valtman 2013-2014 +*/ +#ifndef __TREE_H__ +#define __TREE_H__ +#include + +#include +#include + +#pragma pack(push,4) +#define DEFINE_TREE(X_NAME, X_TYPE, X_CMP, X_UNSET) \ +struct tree_ ## X_NAME { \ + struct tree_ ## X_NAME *left, *right;\ + X_TYPE x;\ + int y;\ +};\ +\ +static struct tree_ ## X_NAME *new_tree_node_ ## X_NAME (X_TYPE x, int y) {\ + struct tree_ ## X_NAME *T = malloc (sizeof (*T));\ + T->x = x;\ + T->y = y;\ + T->left = T->right = 0;\ + return T;\ +}\ +\ +static void delete_tree_node_ ## X_NAME (struct tree_ ## X_NAME *T) {\ + free (T);\ +}\ +\ +static void tree_split_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x, struct tree_ ## X_NAME **L, struct tree_ ## X_NAME **R) {\ + if (!T) {\ + *L = *R = 0;\ + } else {\ + int c = X_CMP (x, T->x);\ + if (c < 0) {\ + tree_split_ ## X_NAME (T->left, x, L, &T->left);\ + *R = T;\ + } else {\ + tree_split_ ## X_NAME (T->right, x, &T->right, R);\ + *L = T;\ + }\ + }\ +}\ +\ +static struct tree_ ## X_NAME *tree_insert_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x, int y) __attribute__ ((warn_unused_result,unused));\ +static struct tree_ ## X_NAME *tree_insert_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x, int y) {\ + if (!T) {\ + return new_tree_node_ ## X_NAME (x, y);\ + } else {\ + if (y > T->y) {\ + struct tree_ ## X_NAME *N = new_tree_node_ ## X_NAME (x, y);\ + tree_split_ ## X_NAME (T, x, &N->left, &N->right);\ + return N;\ + } else {\ + int c = X_CMP (x, T->x);\ + assert (c);\ + if (c < 0) { \ + T->left = tree_insert_ ## X_NAME (T->left, x, y);\ + } else { \ + T->right = tree_insert_ ## X_NAME (T->right, x, y);\ + } \ + return T; \ + }\ + }\ +}\ +\ +static struct tree_ ## X_NAME *tree_merge_ ## X_NAME (struct tree_ ## X_NAME *L, struct tree_ ## X_NAME *R) {\ + if (!L || !R) {\ + return L ? L : R;\ + } else {\ + if (L->y > R->y) {\ + L->right = tree_merge_ ## X_NAME (L->right, R);\ + return L;\ + } else {\ + R->left = tree_merge_ ## X_NAME (L, R->left);\ + return R;\ + }\ + }\ +}\ +\ +static struct tree_ ## X_NAME *tree_delete_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) __attribute__ ((warn_unused_result,unused));\ +static struct tree_ ## X_NAME *tree_delete_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) {\ + assert (T);\ + int c = X_CMP (x, T->x);\ + if (!c) {\ + struct tree_ ## X_NAME *N = tree_merge_ ## X_NAME (T->left, T->right);\ + delete_tree_node_ ## X_NAME (T);\ + return N;\ + } else {\ + if (c < 0) { \ + T->left = tree_delete_ ## X_NAME (T->left, x); \ + } else { \ + T->right = tree_delete_ ## X_NAME (T->right, x); \ + } \ + return T; \ + }\ +}\ +\ +static X_TYPE tree_get_min_ ## X_NAME (struct tree_ ## X_NAME *t) __attribute__ ((unused));\ +static X_TYPE tree_get_min_ ## X_NAME (struct tree_ ## X_NAME *T) {\ + if (!T) { return X_UNSET; } \ + while (T->left) { T = T->left; }\ + return T->x; \ +} \ +\ +static X_TYPE tree_lookup_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) __attribute__ ((unused));\ +static X_TYPE tree_lookup_ ## X_NAME (struct tree_ ## X_NAME *T, X_TYPE x) {\ + int c;\ + while (T && (c = X_CMP (x, T->x))) {\ + T = (c < 0 ? T->left : T->right);\ + }\ + return T ? T->x : X_UNSET;\ +}\ +\ +static void tree_act_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE)) __attribute__ ((unused));\ +static void tree_act_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE)) {\ + if (!T) { return; } \ + tree_act_ ## X_NAME (T->left, act); \ + act (T->x); \ + tree_act_ ## X_NAME (T->right, act); \ +}\ +\ +static void tree_act_ex_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE, void *), void *extra) __attribute__ ((unused));\ +static void tree_act_ex_ ## X_NAME (struct tree_ ## X_NAME *T, void (*act)(X_TYPE, void *), void *extra) {\ + if (!T) { return; } \ + tree_act_ex_ ## X_NAME (T->left, act, extra); \ + act (T->x, extra); \ + tree_act_ex_ ## X_NAME (T->right, act, extra); \ +}\ +\ +static int tree_count_ ## X_NAME (struct tree_ ## X_NAME *T) __attribute__ ((unused));\ +static int tree_count_ ## X_NAME (struct tree_ ## X_NAME *T) { \ + if (!T) { return 0; }\ + return 1 + tree_count_ ## X_NAME (T->left) + tree_count_ ## X_NAME (T->right); \ +}\ +static void tree_check_ ## X_NAME (struct tree_ ## X_NAME *T) __attribute__ ((unused));\ +static void tree_check_ ## X_NAME (struct tree_ ## X_NAME *T) { \ + if (!T) { return; }\ + if (T->left) { \ + assert (T->left->y <= T->y);\ + assert (X_CMP (T->left->x, T->x) < 0); \ + }\ + if (T->right) { \ + assert (T->right->y <= T->y);\ + assert (X_CMP (T->right->x, T->x) > 0); \ + }\ + tree_check_ ## X_NAME (T->left); \ + tree_check_ ## X_NAME (T->right); \ +}\ +static struct tree_ ## X_NAME *tree_clear_ ## X_NAME (struct tree_ ## X_NAME *T) __attribute__ ((unused));\ +static struct tree_ ## X_NAME *tree_clear_ ## X_NAME (struct tree_ ## X_NAME *T) { \ + if (!T) { return 0; }\ + tree_clear_ ## X_NAME (T->left); \ + tree_clear_ ## X_NAME (T->right); \ + delete_tree_node_ ## X_NAME (T); \ + return 0; \ +} \ + +#define int_cmp(a,b) ((a) - (b)) +#pragma pack(pop) +#endif diff --git a/td/generate/tl-parser/tl-parser.c b/td/generate/tl-parser/tl-parser.c new file mode 100644 index 000000000..66276ba01 --- /dev/null +++ b/td/generate/tl-parser/tl-parser.c @@ -0,0 +1,3078 @@ +/* + This file is part of tl-parser + + tl-parser is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + tl-parser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this tl-parser. If not, see . + + Copyright Vitaly Valtman 2014 + + It is derivative work of VK/KittenPHP-DB-Engine (https://github.com/vk-com/kphp-kdb/) + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman + +*/ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "portable_endian.h" +#include "tl-parser-tree.h" +#include "tl-parser.h" +#include "crc32.h" +#include "tl-tl.h" + +extern int verbosity; +extern int schema_version; +extern int output_expressions; + + +int total_types_num; +int total_constructors_num; +int total_functions_num; + + +/*char *tstrdup (const char *s) { + assert (s); + char *r = talloc (strlen (s) + 1); + memcpy (r, s, strlen (s) + 1); + return r; +}*/ + +#define talloc(a) malloc(a) +#define tfree(a,b) free (a) +#define talloc0(a) calloc(a,1) +#define tstrdup(a) strdup(a) + +typedef char error_int_must_be_4_byte[(sizeof (int) == 4) ? 1 : -1]; +typedef char error_long_long_must_be_8_byte[(sizeof (long long) == 8) ? 1 : -1]; + +char curch; +struct parse parse; + +struct tree *tree; + +struct tree *tree_alloc (void) { + struct tree *T = talloc (sizeof (*T)); + assert (T); + memset (T, 0, sizeof (*T)); + return T; +} + +void tree_add_child (struct tree *P, struct tree *C) { + if (P->nc == P->size) { + void **t = talloc (sizeof (void *) * (++P->size)); + memcpy (t, P->c, sizeof (void *) * (P->size - 1)); + if (P->c) { + tfree (P->c, sizeof (void *) * (P->size - 1)); + } + P->c = (void *)t; + assert (P->c); + } + P->c[P->nc ++] = C; +} + +void tree_delete (struct tree *T) { + assert (T); + int i; + for (i = 0; i < T->nc; i++) { + assert (T->c[i]); + tree_delete (T->c[i]); + } + if (T->c) { + tfree (T->c, sizeof (void *) * T->nc); + } + tfree (T, sizeof (*T)); +} + +void tree_del_child (struct tree *P) { + assert (P->nc); + tree_delete (P->c[--P->nc]); +} + + +char nextch (void) { + if (parse.pos < parse.len - 1) { + curch = parse.text[++parse.pos]; + } else { + curch = 0; + } + if (curch == 10) { + parse.line ++; + parse.line_pos = 0; + } else { + if (curch) { + parse.line_pos ++; + } + } + return curch; +} + + +struct parse save_parse (void) { + return parse; +} + +void load_parse (struct parse _parse) { + parse = _parse; + curch = parse.pos > parse.len ? 0: parse.text[parse.pos] ; +} + +int is_whitespace (char c) { + return (c <= 32); +} + +int is_uletter (char c) { + return (c >= 'A' && c <= 'Z'); +} + +int is_lletter (char c) { + return (c >= 'a' && c <= 'z'); +} + +int is_letter (char c) { + return is_uletter (c) || is_lletter (c); +} + +int is_digit (char c) { + return (c >= '0' && c <= '9'); +} + +int is_hexdigit (char c) { + return is_digit (c) || (c >= 'a' && c <= 'f'); +} + +int is_ident_char (char c) { + return is_digit (c) || is_letter (c) || c == '_'; +} + +int last_error_pos; +int last_error_line; +int last_error_line_pos; +char *last_error; + +void parse_error (const char *e) { + if (parse.pos > last_error_pos) { + last_error_pos = parse.pos; + last_error_line = parse.line; + last_error_line_pos = parse.line_pos; + if (last_error) { + tfree (last_error, strlen (last_error) + 1); + } + last_error = tstrdup (e); + } +} + +void tl_print_parse_error (void) { + fprintf (stderr, "Error near line %d pos %d: `%s`\n", last_error_line + 1, last_error_line_pos + 1, last_error); +} + +char *parse_lex (void) { + while (1) { + while (curch && is_whitespace (curch)) { nextch (); } + if (curch == '/' && nextch () == '/') { + while (nextch () != 10); + nextch (); + } else { + break; + } + } + if (!curch) { + parse.lex.len = 0; + parse.lex.type = lex_eof; + return (parse.lex.ptr = 0); + } + char *p = parse.text + parse.pos; + parse.lex.flags = 0; + switch (curch) { + case '-': + if (nextch () != '-' || nextch () != '-') { + parse_error ("Can not parse triple minus"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } else { + parse.lex.len = 3; + parse.lex.type = lex_triple_minus; + nextch (); + return (parse.lex.ptr = p); + } + case ':': + case ';': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '=': + case '#': + case '?': + case '%': + case '<': + case '>': + case '+': + case ',': + case '*': + case '_': + case '!': + case '.': + nextch (); + parse.lex.len = 1; + parse.lex.type = lex_char; + return (parse.lex.ptr = p); + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + parse.lex.flags = 0; + if (is_uletter (curch)) { + while (is_ident_char (nextch ())); + parse.lex.len = parse.text + parse.pos - p; + parse.lex.ptr = p; + if (parse.lex.len == 5 && !memcmp (parse.lex.ptr, "Final", 5)) { + parse.lex.type = lex_final; + } else if (parse.lex.len == 3 && !memcmp (parse.lex.ptr, "New", 3)) { + parse.lex.type = lex_new; + } else if (parse.lex.len == 5 && !memcmp (parse.lex.ptr, "Empty", 5)) { + parse.lex.type = lex_empty; + } else { + parse.lex.type = lex_uc_ident; + } + return (parse.lex.ptr = p); + } + while (is_ident_char (nextch ())); + if (curch == '.' && !is_letter (parse.text[parse.pos + 1])) { + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_lc_ident; + return (parse.lex.ptr = p); + } + while (curch == '.') { + parse.lex.flags |= 1; + nextch (); + if (is_uletter (curch)) { + while (is_ident_char (nextch ())); + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_uc_ident; + return (parse.lex.ptr = p); + } + if (is_lletter (curch)) { + while (is_ident_char (nextch ())); + } else { + parse_error ("Expected letter"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } + } + if (curch == '#') { + parse.lex.flags |= 2; + int i; + int ok = 1; + for (i = 0; i < 8; i++) { + if (!is_hexdigit (nextch())) { + if (curch == ' ' && i >= 5) { + ok = 2; + break; + } else { + parse_error ("Hex digit expected"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } + } + } + if (ok == 1) { + nextch (); + } + } + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_lc_ident; + return (parse.lex.ptr = p); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + while (is_digit (nextch ())); + parse.lex.len = parse.text + parse.pos - p; + parse.lex.type = lex_num; + return (parse.lex.ptr = p); + default: + parse_error ("Unknown lexem"); + parse.lex.type = lex_error; + return (parse.lex.ptr = (void *)-1); + } + +} + +int expect (char *s) { + if (!parse.lex.ptr || parse.lex.ptr == (void *)-1 || parse.lex.type == lex_error || parse.lex.type == lex_none || parse.lex.len != (int)strlen (s) || memcmp (s, parse.lex.ptr, parse.lex.len)) { + static char buf[1000]; + sprintf (buf, "Expected %s", s); + parse_error (buf); + return -1; + } else { + parse_lex (); + } + return 1; +} + +struct parse *tl_init_parse_file (const char *fname) { + FILE *f = fopen (fname, "rb"); + if (f == NULL) { + fprintf (stderr, "Failed to open the input file.\n"); + return NULL; + } + if (fseek (f, 0, SEEK_END) != 0) { + fprintf (stderr, "Can't seek to the end of the input file.\n"); + return NULL; + } + long size = ftell (f); + if (size <= 0 || size > INT_MAX) { + fprintf (stderr, "Size is %ld. Too small or too big.\n", size); + return NULL; + } + fseek (f, 0, SEEK_SET); + + static struct parse save; + save.text = talloc ((size_t)size); + save.len = fread (save.text, 1, (size_t)size, f); + assert (save.len == size); + fclose (f); + save.pos = 0; + save.line = 0; + save.line_pos = 0; + save.lex.ptr = save.text; + save.lex.len = 0; + save.lex.type = lex_none; + return &save; +} + +#define PARSE_INIT(_type) struct parse save = save_parse (); struct tree *T = tree_alloc (); T->type = (_type); T->lex_line = parse.line; T->lex_line_pos = parse.line_pos; struct tree *S __attribute__ ((unused)); +#define PARSE_FAIL load_parse (save); tree_delete (T); return 0; +#define PARSE_OK return T; +#define PARSE_TRY_PES(x) if (!(S = x ())) { PARSE_FAIL; } { tree_add_child (T, S); } +#define PARSE_TRY_OPT(x) if ((S = x ())) { tree_add_child (T, S); PARSE_OK } +#define PARSE_TRY(x) S = x (); +#define PARSE_ADD(_type) S = tree_alloc (); S->type = _type; tree_add_child (T, S); +#define EXPECT(s) if (expect (s) < 0) { PARSE_FAIL; } +#define LEX_CHAR(c) (parse.lex.type == lex_char && *parse.lex.ptr == c) +struct tree *parse_args (void); +struct tree *parse_expr (void); + +struct tree *parse_boxed_type_ident (void) { + PARSE_INIT (type_boxed_type_ident); + if (parse.lex.type != lex_uc_ident) { + parse_error ("Can not parse boxed type"); + PARSE_FAIL; + } else { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } +} + +struct tree *parse_full_combinator_id (void) { + PARSE_INIT (type_full_combinator_id); + if (parse.lex.type == lex_lc_ident || LEX_CHAR('_')) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse full combinator id"); + PARSE_FAIL; + } +} + +struct tree *parse_combinator_id (void) { + PARSE_INIT (type_combinator_id); + if (parse.lex.type == lex_lc_ident && !(parse.lex.flags & 2)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse combinator id"); + PARSE_FAIL; + } +} + +struct tree *parse_var_ident (void) { + PARSE_INIT (type_var_ident); + if ((parse.lex.type == lex_lc_ident || parse.lex.type == lex_uc_ident) && !(parse.lex.flags & 3)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse var ident"); + PARSE_FAIL; + } +} + +struct tree *parse_var_ident_opt (void) { + PARSE_INIT (type_var_ident_opt); + if ((parse.lex.type == lex_lc_ident || parse.lex.type == lex_uc_ident)&& !(parse.lex.flags & 3)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else if (LEX_CHAR ('_')) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse var ident opt"); + PARSE_FAIL; + } +} + +struct tree *parse_nat_const (void) { + PARSE_INIT (type_nat_const); + if (parse.lex.type == lex_num) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse nat const"); + PARSE_FAIL; + } +} + +struct tree *parse_type_ident (void) { + PARSE_INIT (type_type_ident); + if (parse.lex.type == lex_uc_ident && !(parse.lex.flags & 2)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else if (parse.lex.type == lex_lc_ident && !(parse.lex.flags & 2)) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else if (LEX_CHAR ('#')) { + T->text = parse.lex.ptr; + T->len = parse.lex.len; + T->flags = parse.lex.flags; + parse_lex (); + PARSE_OK; + } else { + parse_error ("Can not parse type ident"); + PARSE_FAIL; + } +} + +struct tree *parse_term (void) { + PARSE_INIT (type_term); + while (LEX_CHAR ('%')) { + EXPECT ("%") + PARSE_ADD (type_percent); + } + if (LEX_CHAR ('(')) { + EXPECT ("("); + PARSE_TRY_PES (parse_expr); + EXPECT (")"); + PARSE_OK; + } + PARSE_TRY (parse_type_ident); + if (S) { + tree_add_child (T, S); + if (LEX_CHAR ('<')) { + EXPECT ("<"); + while (1) { + PARSE_TRY_PES (parse_expr); + if (LEX_CHAR ('>')) { break; } + EXPECT (","); + } + EXPECT (">"); + } + PARSE_OK; + } + PARSE_TRY_OPT (parse_type_ident); + PARSE_TRY_OPT (parse_var_ident); + PARSE_TRY_OPT (parse_nat_const); + PARSE_FAIL; +} + +struct tree *parse_nat_term (void) { + PARSE_INIT (type_nat_term); + PARSE_TRY_PES (parse_term); + PARSE_OK; +} + +struct tree *parse_subexpr (void) { + PARSE_INIT (type_subexpr); + int was_term = 0; + int cc = 0; + + while (1) { + PARSE_TRY (parse_nat_const); + if (S) { + tree_add_child (T, S); + } else if (!was_term) { + was_term = 1; + PARSE_TRY (parse_term); + if (S) { + tree_add_child (T, S); + } else { + break; + } + } + cc ++; + if (!LEX_CHAR ('+')) { + break; + } + EXPECT ("+"); + } + if (!cc) { + PARSE_FAIL; + } else { + PARSE_OK; + } +} + +struct tree *parse_expr (void) { + PARSE_INIT (type_expr); + int cc = 0; + while (1) { + PARSE_TRY (parse_subexpr); + if (S) { + tree_add_child (T, S); + cc ++; + } else { + if (cc < 1) { PARSE_FAIL; } + else { PARSE_OK; } + } + } +} + + + +struct tree *parse_final_empty (void) { + PARSE_INIT (type_final_empty); + EXPECT ("Empty"); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_final_new (void) { + PARSE_INIT (type_final_new); + EXPECT ("New"); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_final_final (void) { + PARSE_INIT (type_final_final); + EXPECT ("Final"); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_partial_comb_app_decl (void) { + PARSE_INIT (type_partial_comb_app_decl); + PARSE_TRY_PES (parse_combinator_id); + while (1) { + PARSE_TRY_PES (parse_subexpr); + if (LEX_CHAR (';')) { break; } + } + PARSE_OK; +} + +struct tree *parse_partial_type_app_decl (void) { + PARSE_INIT (type_partial_type_app_decl); + PARSE_TRY_PES (parse_boxed_type_ident); + if (LEX_CHAR ('<')) { + EXPECT ("<"); + while (1) { + PARSE_TRY_PES (parse_expr); + if (LEX_CHAR ('>')) { break; } + EXPECT (","); + } + EXPECT (">"); + PARSE_OK; + } else { + while (1) { + PARSE_TRY_PES (parse_subexpr); + if (LEX_CHAR (';')) { break; } + } + PARSE_OK; + } +} + + + + +struct tree *parse_multiplicity (void) { + PARSE_INIT (type_multiplicity); + PARSE_TRY_PES (parse_nat_term); + PARSE_OK; +} + + +struct tree *parse_type_term (void) { + PARSE_INIT (type_type_term); + PARSE_TRY_PES (parse_term); + PARSE_OK; +} + +struct tree *parse_optional_arg_def (void) { + PARSE_INIT (type_optional_arg_def); + PARSE_TRY_PES (parse_var_ident); + EXPECT ("."); + PARSE_TRY_PES (parse_nat_const); + EXPECT ("?"); + PARSE_OK; +} + +struct tree *parse_args4 (void) { + PARSE_INIT (type_args4); + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + if (LEX_CHAR ('!')) { + PARSE_ADD (type_exclam); + EXPECT ("!"); + } + PARSE_TRY_PES (parse_type_term); + PARSE_OK; +} + +struct tree *parse_args3 (void) { + PARSE_INIT (type_args3); + PARSE_TRY_PES (parse_var_ident_opt); + EXPECT (":"); + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + if (LEX_CHAR ('!')) { + PARSE_ADD (type_exclam); + EXPECT ("!"); + } + PARSE_TRY_PES (parse_type_term); + PARSE_OK; +} + +struct tree *parse_args2 (void) { + PARSE_INIT (type_args2); + PARSE_TRY (parse_var_ident_opt); + if (S && LEX_CHAR (':')) { + tree_add_child (T, S); + EXPECT (":"); + } else { + load_parse (save); + } + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + struct parse save2 = save_parse (); + PARSE_TRY (parse_multiplicity); + if (S && LEX_CHAR ('*')) { + tree_add_child (T, S); + EXPECT ("*"); + } else { + load_parse (save2); + } + EXPECT ("["); + while (1) { + if (LEX_CHAR (']')) { break; } + PARSE_TRY_PES (parse_args); + } + EXPECT ("]"); + PARSE_OK; +} + +struct tree *parse_args1 (void) { + PARSE_INIT (type_args1); + EXPECT ("("); + while (1) { + PARSE_TRY_PES (parse_var_ident_opt); + if (LEX_CHAR(':')) { break; } + } + EXPECT (":"); + struct parse so = save_parse (); + PARSE_TRY (parse_optional_arg_def); + if (S) { + tree_add_child (T, S); + } else { + load_parse (so); + } + if (LEX_CHAR ('!')) { + PARSE_ADD (type_exclam); + EXPECT ("!"); + } + PARSE_TRY_PES (parse_type_term); + EXPECT (")"); + PARSE_OK; +} + +struct tree *parse_args (void) { + PARSE_INIT (type_args); + PARSE_TRY_OPT (parse_args1); + PARSE_TRY_OPT (parse_args2); + PARSE_TRY_OPT (parse_args3); + PARSE_TRY_OPT (parse_args4); + PARSE_FAIL; +} + +struct tree *parse_opt_args (void) { + PARSE_INIT (type_opt_args); + while (1) { + PARSE_TRY_PES (parse_var_ident); + if (parse.lex.type == lex_char && *parse.lex.ptr == ':') { break;} + } + EXPECT (":"); + PARSE_TRY_PES (parse_type_term); + PARSE_OK; +} + +struct tree *parse_final_decl (void) { + PARSE_INIT (type_final_decl); + PARSE_TRY_OPT (parse_final_new); + PARSE_TRY_OPT (parse_final_final); + PARSE_TRY_OPT (parse_final_empty); + PARSE_FAIL; +} + +struct tree *parse_partial_app_decl (void) { + PARSE_INIT (type_partial_app_decl); + PARSE_TRY_OPT (parse_partial_type_app_decl); + PARSE_TRY_OPT (parse_partial_comb_app_decl); + PARSE_FAIL; +} + +struct tree *parse_result_type (void) { + PARSE_INIT (type_result_type); + PARSE_TRY_PES (parse_boxed_type_ident); + if (LEX_CHAR ('<')) { + EXPECT ("<"); + while (1) { + PARSE_TRY_PES (parse_expr); + if (LEX_CHAR ('>')) { break; } + EXPECT (","); + } + EXPECT (">"); + PARSE_OK; + } else { + while (1) { + if (LEX_CHAR (';')) { PARSE_OK; } + PARSE_TRY_PES (parse_subexpr); + } + } +} + +struct tree *parse_combinator_decl (void) { + PARSE_INIT (type_combinator_decl); + PARSE_TRY_PES (parse_full_combinator_id) + while (1) { + if (LEX_CHAR ('{')) { + parse_lex (); + PARSE_TRY_PES (parse_opt_args); + EXPECT ("}"); + } else { + break; + } + } + while (1) { + if (LEX_CHAR ('=')) { break; } + PARSE_TRY_PES (parse_args); + } + EXPECT ("="); + PARSE_ADD (type_equals); + + PARSE_TRY_PES (parse_result_type); + PARSE_OK; +} + +struct tree *parse_builtin_combinator_decl (void) { + PARSE_INIT (type_builtin_combinator_decl); + PARSE_TRY_PES (parse_full_combinator_id) + EXPECT ("?"); + EXPECT ("="); + PARSE_TRY_PES (parse_boxed_type_ident); + PARSE_OK; +} + +struct tree *parse_declaration (void) { + PARSE_INIT (type_declaration); + PARSE_TRY_OPT (parse_combinator_decl); + PARSE_TRY_OPT (parse_partial_app_decl); + PARSE_TRY_OPT (parse_final_decl); + PARSE_TRY_OPT (parse_builtin_combinator_decl); + PARSE_FAIL; +} + +struct tree *parse_constr_declarations (void) { + PARSE_INIT (type_constr_declarations); + if (parse.lex.type == lex_triple_minus || parse.lex.type == lex_eof) { PARSE_OK; } + while (1) { + PARSE_TRY_PES (parse_declaration); + EXPECT (";"); + if (parse.lex.type == lex_eof || parse.lex.type == lex_triple_minus) { PARSE_OK; } + } +} + +struct tree *parse_fun_declarations (void) { + PARSE_INIT (type_fun_declarations); + if (parse.lex.type == lex_triple_minus || parse.lex.type == lex_eof) { PARSE_OK; } + while (1) { + PARSE_TRY_PES (parse_declaration); + EXPECT (";"); + if (parse.lex.type == lex_eof || parse.lex.type == lex_triple_minus) { PARSE_OK; } + } +} + +struct tree *parse_program (void) { + PARSE_INIT (type_tl_program); + while (1) { + PARSE_TRY_PES (parse_constr_declarations); + if (parse.lex.type == lex_eof) { PARSE_OK; } + if (parse.lex.type == lex_error || expect ("---") < 0 || expect ("functions") < 0 || expect ("---") < 0) { PARSE_FAIL; } + + PARSE_TRY_PES (parse_fun_declarations); + if (parse.lex.type == lex_eof) { PARSE_OK; } + if (parse.lex.type == lex_error || expect ("---") < 0 || expect ("types") < 0 || expect ("---") < 0) { PARSE_FAIL; } + } +} + +struct tree *tl_parse_lex (struct parse *_parse) { + assert (_parse); + load_parse (*_parse); + if (parse.lex.type == lex_none) { + parse_lex (); + } + if (parse.lex.type == lex_error) { + return 0; + } + return parse_program (); +} + +int mystrcmp2 (const char *b, int len, const char *a) { + int c = strncmp (b, a, len); + return c ? a[len] ? -1 : 0 : c; +} + +char *mystrdup (const char *a, int len) { + char *z = talloc (len + 1); + memcpy (z, a, len); + z[len] = 0; + return z; +} + +struct tl_program *tl_program_cur; +#define TL_TRY_PES(x) if (!(x)) { return 0; } + +#define tl_type_cmp(a,b) (strcmp (a->id, b->id)) +DEFINE_TREE (tl_type,struct tl_type *,tl_type_cmp,0) +struct tree_tl_type *tl_type_tree; + +DEFINE_TREE (tl_constructor,struct tl_constructor *,tl_type_cmp,0) +struct tree_tl_constructor *tl_constructor_tree; +struct tree_tl_constructor *tl_function_tree; + +DEFINE_TREE (tl_var,struct tl_var *,tl_type_cmp,0) + +struct tl_var_value { + struct tl_combinator_tree *ptr; + struct tl_combinator_tree *val; + int num_val; +}; + +#define tl_var_value_cmp(a,b) (((char *)a.ptr) - ((char *)b.ptr)) +struct tl_var_value empty; +DEFINE_TREE (var_value, struct tl_var_value, tl_var_value_cmp, empty) +//tree_tl_var_t *tl_var_tree; + +DEFINE_TREE (tl_field,char *,strcmp, 0) +//tree_tl_field_t *tl_field_tree; +#define TL_FAIL return 0; +#define TL_INIT(x) struct tl_combinator_tree *x = 0; +#define TL_TRY(f,x) { struct tl_combinator_tree *_t = f; if (!_t) { TL_FAIL;} x = tl_union (x, _t); if (!x) { TL_FAIL; }} +#define TL_ERROR(...) fprintf (stderr, __VA_ARGS__); +#define TL_WARNING(...) fprintf (stderr, __VA_ARGS__); + +void tl_set_var_value (struct tree_var_value **T, struct tl_combinator_tree *var, struct tl_combinator_tree *value) { + struct tl_var_value t = {.ptr = var, .val = value, .num_val = 0}; + if (tree_lookup_var_value (*T, t).ptr) { + *T = tree_delete_var_value (*T, t); + } + *T = tree_insert_var_value (*T, t, lrand48 ()); +} + +void tl_set_var_value_num (struct tree_var_value **T, struct tl_combinator_tree *var, struct tl_combinator_tree *value, long long num_value) { + struct tl_var_value t = {.ptr = var, .val = value, .num_val = num_value}; + if (tree_lookup_var_value (*T, t).ptr) { + *T = tree_delete_var_value (*T, t); + } + *T = tree_insert_var_value (*T, t, lrand48 ()); +} + +struct tl_combinator_tree *tl_get_var_value (struct tree_var_value **T, struct tl_combinator_tree *var) { + struct tl_var_value t = {.ptr = var, .val = 0, .num_val = 0}; + struct tl_var_value r = tree_lookup_var_value (*T, t); + return r.ptr ? r.val : 0; +} + +int tl_get_var_value_num (struct tree_var_value **T, struct tl_combinator_tree *var) { + struct tl_var_value t = {.ptr = var, .val = 0}; + struct tl_var_value r = tree_lookup_var_value (*T, t); + return r.ptr ? r.num_val : 0; +} + +int namespace_level; + +struct tree_tl_var *vars[10]; +struct tree_tl_field *fields[10]; +struct tl_var *last_num_var[10]; + +int tl_is_type_name (const char *id, int len) { + if (len == 1 && *id == '#') { return 1;} + int ok = id[0] >= 'A' && id[0] <= 'Z'; + int i; + for (i = 0; i < len - 1; i++) if (id[i] == '.') { + ok = id[i + 1] >= 'A' && id[i + 1] <= 'Z'; + } + return ok; +} + +int tl_add_field (char *id) { + assert (namespace_level < 10); + assert (namespace_level >= 0); + if (tree_lookup_tl_field (fields[namespace_level], id)) { + return 0; + } + fields[namespace_level] = tree_insert_tl_field (fields[namespace_level], id, lrand48 ()); + return 1; +} + +void tl_clear_fields (void) { +// tree_act_tl_field (fields[namespace_level], (void *)free); + fields[namespace_level] = tree_clear_tl_field (fields[namespace_level]); +} + +struct tl_var *tl_add_var (char *id, struct tl_combinator_tree *ptr, int type) { + struct tl_var *v = talloc (sizeof (*v)); + v->id = tstrdup (id); + v->type = type; + v->ptr = ptr; + v->flags = 0; + if (tree_lookup_tl_var (vars[namespace_level], v)) { + return 0; + } + vars[namespace_level] = tree_insert_tl_var (vars[namespace_level], v, lrand48 ()); + if (type) { + last_num_var[namespace_level] = v; + } + return v; +} + +void tl_del_var (struct tl_var *v) { +// free (v->id); + tfree (v, sizeof (*v)); +} + +void tl_clear_vars (void) { + tree_act_tl_var (vars[namespace_level], tl_del_var); + vars[namespace_level] = tree_clear_tl_var (vars[namespace_level]); + last_num_var[namespace_level] = 0; +} + +struct tl_var *tl_get_last_num_var (void) { + return last_num_var[namespace_level]; +} + +struct tl_var *tl_get_var (char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_var v = {.id = id}; + int i; + for (i = namespace_level; i >= 0; i--) { + struct tl_var *w = tree_lookup_tl_var (vars[i], &v); + if (w) { + tfree (id, len + 1); + return w; + } + } + tfree (id, len + 1); + return 0; +} + +void namespace_push (void) { + namespace_level ++; + assert (namespace_level < 10); + tl_clear_vars (); + tl_clear_fields (); +} + +void namespace_pop (void) { + namespace_level --; + assert (namespace_level >= 0); +} + +struct tl_type *tl_get_type (const char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_type _t = {.id = id}; + struct tl_type *r = tree_lookup_tl_type (tl_type_tree, &_t); + tfree (id, len + 1); + return r; +} + +struct tl_type *tl_add_type (const char *_id, int len, int params_num, long long params_types) { + char *id = talloc (len + 1); + memcpy (id, _id, len); + id[len] = 0; + struct tl_type _t = {.id = id}; + struct tl_type *_r = 0; + if ((_r = tree_lookup_tl_type (tl_type_tree, &_t))) { + tfree (id, len + 1); + if (params_num >= 0 && (_r->params_num != params_num || _r->params_types != params_types)) { + TL_ERROR ("Wrong params_num or types for type %s\n", _r->id); + return 0; + } + return _r; + } + struct tl_type *t = talloc (sizeof (*t)); + t->id = id; + t->print_id = tstrdup (t->id); + int i; + for (i = 0; i < len; i++) if (t->print_id[i] == '.' || t->print_id[i] == '#' || t->print_id[i] == ' ') { + t->print_id[i] = '$'; + } + t->name = 0; + t->constructors_num = 0; + t->constructors = 0; + t->flags = 0; + t->real_id = 0; + if (params_num >= 0) { + assert (params_num <= 64); + t->params_num = params_num; + t->params_types = params_types; + } else { + t->flags |= 4; + t->params_num = -1; + } + tl_type_tree = tree_insert_tl_type (tl_type_tree, t, lrand48 ()); + total_types_num ++; + return t; +} + +void tl_add_type_param (struct tl_type *t, int x) { + assert (t->flags & 4); + assert (t->params_num <= 64); + if (x) { + t->params_types |= (1ull << (t->params_num ++)); + } else { + t->params_num ++; + } +} + +int tl_type_set_params (struct tl_type *t, int x, long long y) { + if (t->flags & 4) { + t->params_num = x; + t->params_types = y; + t->flags &= ~4; + } else { + if (t->params_num != x || t->params_types != y) { + fprintf (stderr, "Wrong num of params (type %s)\n", t->id); + return 0; + } + } + return 1; +} + +void tl_type_finalize (struct tl_type *t) { + t->flags &= ~4; +} + +struct tl_constructor *tl_get_constructor (const char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_constructor _t = {.id = id}; + struct tl_constructor *r = tree_lookup_tl_constructor (tl_constructor_tree, &_t); + tfree (id, len + 1); + return r; +} + +struct tl_constructor *tl_add_constructor (struct tl_type *a, const char *_id, int len, int force_magic) { + assert (a); + if (a->flags & 1) { + TL_ERROR ("New constructor for type `%s` after final statement\n", a->id); + return 0; + } + int x = 0; + while (x < len && (_id[x] != '#' || force_magic)) { x++; } + char *id = talloc (x + 1); + memcpy (id, _id, x); + id[x] = 0; + + unsigned magic = 0; + if (x < len) { + assert (len - x >= 6 && len - x <= 9); + int i; + for (i = 1; i < len - x; i++) { + magic = (magic << 4) + (_id[x + i] <= '9' ? _id[x + i] - '0' : _id[x + i] - 'a' + 10); + } + assert (magic && magic != (unsigned)-1); + } + + len = x; + if (*id != '_') { + struct tl_constructor _t = {.id = id}; + if (tree_lookup_tl_constructor (tl_constructor_tree, &_t)) { + TL_ERROR ("Duplicate constructor id `%s`\n", id); + tfree (id, len + 1); + return 0; + } + } else { + assert (len == 1); + } + + struct tl_constructor *t = talloc (sizeof (*t)); + t->type = a; + t->name = magic; + t->id = id; + t->print_id = tstrdup (id); + t->real_id = 0; + + int i; + for (i = 0; i < len; i++) if (t->print_id[i] == '.' || t->print_id[i] == '#' || t->print_id[i] == ' ') { + t->print_id[i] = '$'; + } + + t->left = t->right = 0; + a->constructors = realloc (a->constructors, sizeof (void *) * (a->constructors_num + 1)); + assert (a->constructors); + a->constructors[a->constructors_num ++] = t; + if (*id != '_') { + tl_constructor_tree = tree_insert_tl_constructor (tl_constructor_tree, t, lrand48 ()); + } else { + a->flags |= FLAG_DEFAULT_CONSTRUCTOR; + } + total_constructors_num ++; + return t; +} + +struct tl_constructor *tl_get_function (const char *_id, int len) { + char *id = mystrdup (_id, len); + struct tl_constructor _t = {.id = id}; + struct tl_constructor *r = tree_lookup_tl_constructor (tl_function_tree, &_t); + tfree (id, len + 1); + return r; +} + +struct tl_constructor *tl_add_function (struct tl_type *a, const char *_id, int len, int force_magic) { +// assert (a); + int x = 0; + while (x < len && ((_id[x] != '#') || force_magic)) { x++; } + char *id = talloc (x + 1); + memcpy (id, _id, x); + id[x] = 0; + + unsigned magic = 0; + if (x < len) { + assert (len - x >= 6 && len - x <= 9); + int i; + for (i = 1; i < len - x; i++) { + magic = (magic << 4) + (_id[x + i] <= '9' ? _id[x + i] - '0' : _id[x + i] - 'a' + 10); + } + assert (magic && magic != (unsigned)-1); + } + + len = x; + + struct tl_constructor _t = {.id = id}; + if (tree_lookup_tl_constructor (tl_function_tree, &_t)) { + TL_ERROR ("Duplicate function id `%s`\n", id); + tfree (id, len + 1); + return 0; + } + + struct tl_constructor *t = talloc (sizeof (*t)); + t->type = a; + t->name = magic; + t->id = id; + t->print_id = tstrdup (id); + t->real_id = 0; + + int i; + for (i = 0; i < len; i++) if (t->print_id[i] == '.' || t->print_id[i] == '#' || t->print_id[i] == ' ') { + t->print_id[i] = '$'; + } + + t->left = t->right = 0; + tl_function_tree = tree_insert_tl_constructor (tl_function_tree, t, lrand48 ()); + total_functions_num ++; + return t; +} + +static char buf[(1 << 20)]; +int buf_pos; + +struct tl_combinator_tree *alloc_ctree_node (void) { + struct tl_combinator_tree *T = talloc (sizeof (*T)); + assert (T); + memset (T, 0, sizeof (*T)); + return T; +} + +struct tl_combinator_tree *tl_tree_dup (struct tl_combinator_tree *T) { + if (!T) { return 0; } + struct tl_combinator_tree *S = talloc (sizeof (*S)); + memcpy (S, T, sizeof (*S)); + S->left = tl_tree_dup (T->left); + S->right = tl_tree_dup (T->right); + return S; +} + +struct tl_type *tl_tree_get_type (struct tl_combinator_tree *T) { + assert (T->type == type_type); + if (T->act == act_array) { return 0;} + while (T->left) { + T = T->left; + if (T->act == act_array) { return 0;} + assert (T->type == type_type); + } + assert (T->act == act_type || T->act == act_var || T->act == act_array); + return T->act == act_type ? T->data : 0; +} + +void tl_tree_set_len (struct tl_combinator_tree *T) { + TL_INIT (H); + H = T; + while (H->left) { + H->left->type_len = H->type_len + 1; + H = H->left; + } + assert (H->type == type_type); + struct tl_type *t = H->data; + assert (t); + assert (H->type_len == t->params_num); +} + +void tl_buf_reset (void) { + buf_pos = 0; +} + +void tl_buf_add_string (char *s, int len) { + if (len < 0) { len = strlen (s); } + buf[buf_pos ++] = ' '; + memcpy (buf + buf_pos, s, len); buf_pos += len; + buf[buf_pos] = 0; +} + +void tl_buf_add_string_nospace (char *s, int len) { + if (len < 0) { len = strlen (s); } +// if (buf_pos) { buf[buf_pos ++] = ' '; } + memcpy (buf + buf_pos, s, len); buf_pos += len; + buf[buf_pos] = 0; +} + +void tl_buf_add_string_q (char *s, int len, int x) { + if (x) { + tl_buf_add_string (s, len); + } else { + tl_buf_add_string_nospace (s, len); + } +} + + +void tl_buf_add_tree (struct tl_combinator_tree *T, int x) { + if (!T) { return; } + assert (T != (void *)-1l && T != (void *)-2l); + switch (T->act) { + case act_question_mark: + tl_buf_add_string_q ("?", -1, x); + return; + case act_type: + if ((T->flags & 1) && !(T->flags & 4)) { + tl_buf_add_string_q ("%", -1, x); + x = 0; + } + if (T->flags & 2) { + tl_buf_add_string_q ((char *)T->data, -1, x); + } else { + struct tl_type *t = T->data; + if (T->flags & 4) { + assert (t->constructors_num == 1); + tl_buf_add_string_q (t->constructors[0]->real_id ? t->constructors[0]->real_id : t->constructors[0]->id, -1, x); + } else { + tl_buf_add_string_q (t->real_id ? t->real_id : t->id, -1, x); + } + } + return; + case act_field: + if (T->data) { + tl_buf_add_string_q ((char *)T->data, -1, x); + x = 0; + tl_buf_add_string_q (":", -1, 0); + } + tl_buf_add_tree (T->left, x); + tl_buf_add_tree (T->right, 1); + return; + case act_union: + tl_buf_add_tree (T->left, x); + tl_buf_add_tree (T->right, 1); + return; + case act_var: + { + if (T->data == (void *)-1l) { return; } + struct tl_combinator_tree *v = T->data; + tl_buf_add_string_q ((char *)v->data, -1, x); + if (T->type == type_num && T->type_flags) { + static char _buf[30]; + sprintf (_buf, "+%lld", T->type_flags); + tl_buf_add_string_q (_buf, -1, 0); + } + } + return; + case act_arg: + tl_buf_add_tree (T->left, x); + tl_buf_add_tree (T->right, 1); + return; + case act_array: + if (T->left && !(T->left->flags & 128)) { + tl_buf_add_tree (T->left, x); + x = 0; + tl_buf_add_string_q ("*", -1, x); + } + tl_buf_add_string_q ("[", -1, x); + tl_buf_add_tree (T->right, 1); + tl_buf_add_string_q ("]", -1, 1); + return; + case act_plus: + tl_buf_add_tree (T->left, x); + tl_buf_add_string_q ("+", -1, 0); + tl_buf_add_tree (T->right, 0); + return; + case act_nat_const: + { + static char _buf[30]; + snprintf (_buf, 29, "%lld", T->type_flags); + tl_buf_add_string_q (_buf, -1, x); + return; + } + case act_opt_field: + { + struct tl_combinator_tree *v = T->left->data; + tl_buf_add_string_q ((char *)v->data, -1, x); + tl_buf_add_string_q (".", -1, 0); + static char _buf[30]; + sprintf (_buf, "%lld", T->left->type_flags); + tl_buf_add_string_q (_buf, -1, 0); + tl_buf_add_string_q ("?", -1, 0); + tl_buf_add_tree (T->right, 0); + return; + } + + default: + fprintf (stderr, "%s %s\n", TL_ACT (T->act), TL_TYPE (T->type)); + assert (0); + return; + } +} + +int tl_count_combinator_name (struct tl_constructor *c) { + assert (c); + tl_buf_reset (); + tl_buf_add_string_nospace (c->real_id ? c->real_id : c->id, -1); + tl_buf_add_tree (c->left, 1); + tl_buf_add_string ("=", -1); + tl_buf_add_tree (c->right, 1); + //fprintf (stderr, "%.*s\n", buf_pos, buf); + if (!c->name) { + c->name = compute_crc32 (buf, buf_pos); + } + return c->name; +} + +int tl_print_combinator (struct tl_constructor *c) { + tl_buf_reset (); + tl_buf_add_string_nospace (c->real_id ? c->real_id : c->id, -1); + static char _buf[10]; + sprintf (_buf, "#%08x", c->name); + tl_buf_add_string_nospace (_buf, -1); + tl_buf_add_tree (c->left, 1); + tl_buf_add_string ("=", -1); + tl_buf_add_tree (c->right, 1); + if (output_expressions >= 1) { + fprintf (stderr, "%.*s\n", buf_pos, buf); + } +/* if (!c->name) { + c->name = compute_crc32 (buf, buf_pos); + }*/ + return c->name; +} + +int _tl_finish_subtree (struct tl_combinator_tree *R, int x, long long y) { + assert (R->type == type_type); + assert (R->type_len < 0); + assert (R->act == act_arg || R->act == act_type); + R->type_len = x; + R->type_flags = y; + if (R->act == act_type) { + struct tl_type *t = R->data; + assert (t); + return tl_type_set_params (t, x, y); + } + assert ((R->right->type == type_type && R->right->type_len == 0) || R->right->type == type_num || R->right->type == type_num_value); + return _tl_finish_subtree (R->left, x + 1, y * 2 + (R->right->type == type_num || R->right->type == type_num_value)); +} + +int tl_finish_subtree (struct tl_combinator_tree *R) { + assert (R); + if (R->type != type_type) { + return 1; + } + if (R->type_len >= 0) { + if (R->type_len > 0) { + TL_ERROR ("Not enough params\n"); + return 0; + } + return 1; + } + return _tl_finish_subtree (R, 0, 0); +} + +struct tl_combinator_tree *tl_union (struct tl_combinator_tree *L, struct tl_combinator_tree *R) { + if (!L) { return R; } + if (!R) { return L; } + TL_INIT (v); + v = alloc_ctree_node (); + v->left = L; + v->right = R; + switch (L->type) { + case type_num: + if (R->type != type_num_value) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + tfree (v, sizeof (*v)); + L->type_flags += R->type_flags; + return L; + case type_num_value: + if (R->type != type_num_value && R->type != type_num) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + tfree (v, sizeof (*v)); + R->type_flags += L->type_flags; + return R; + case type_list_item: + case type_list: + if (R->type != type_list_item) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + v->type = type_list; + v->act = act_union; + return v; + case type_type: + if (L->type_len == 0) { + TL_ERROR ("Arguments number exceeds type arity\n"); + return 0; + } + if (R->type != type_num && R->type != type_type && R->type != type_num_value) { + TL_ERROR ("Union: type mistmatch\n"); + return 0; + } + if (R->type_len < 0) { + if (!tl_finish_subtree (R)) { + return 0; + } + } + if (R->type_len > 0) { + TL_ERROR ("Argument type must have full number of arguments\n"); + return 0; + } + if (L->type_len > 0 && ((L->type_flags & 1) != (R->type == type_num || R->type == type_num_value))) { + TL_ERROR ("Argument types mistmatch: L->type_flags = %lld, R->type = %s\n", L->flags, TL_TYPE (R->type)); + return 0; + } + v->type = type_type; + v->act = act_arg; + v->type_len = L->type_len > 0 ? L->type_len - 1 : -1; + v->type_flags = L->type_flags >> 1; + return v; + default: + assert (0); + return 0; + } +} + +struct tl_combinator_tree *tl_parse_any_term (struct tree *T, int s); +struct tl_combinator_tree *tl_parse_term (struct tree *T, int s) { + assert (T->type == type_term); + int i = 0; + while (i < T->nc && T->c[i]->type == type_percent) { i ++; s ++; } + assert (i < T->nc); + TL_INIT (L); + while (i < T->nc) { + TL_TRY (tl_parse_any_term (T->c[i], s), L); + s = 0; + i ++; + } + return L; +} + + +struct tl_combinator_tree *tl_parse_type_term (struct tree *T, int s) { + assert (T->type == type_type_term); + assert (T->nc == 1); + struct tl_combinator_tree *Z = tl_parse_term (T->c[0], s); + if (!Z || Z->type != type_type) { if (Z) { TL_ERROR ("type_term: found type %s\n", TL_TYPE (Z->type)); } TL_FAIL; } + return Z; +} + +struct tl_combinator_tree *tl_parse_nat_term (struct tree *T, int s) { + assert (T->type == type_nat_term); + assert (T->nc == 1); + struct tl_combinator_tree *Z = tl_parse_term (T->c[0], s); + if (!Z || (Z->type != type_num && Z->type != type_num_value)) { if (Z) { TL_ERROR ("nat_term: found type %s\n", TL_TYPE (Z->type)); }TL_FAIL; } + return Z; +} + +struct tl_combinator_tree *tl_parse_subexpr (struct tree *T, int s) { + assert (T->type == type_subexpr); + assert (T->nc >= 1); + int i; + TL_INIT (L); + for (i = 0; i < T->nc; i++) { + TL_TRY (tl_parse_any_term (T->c[i], s), L); + s = 0; + } + return L; +} + +struct tl_combinator_tree *tl_parse_expr (struct tree *T, int s) { + assert (T->type == type_expr); + assert (T->nc >= 1); + int i; + TL_INIT (L); + for (i = 0; i < T->nc; i++) { + TL_TRY (tl_parse_subexpr (T->c[i], s), L); + s = 0; + } + return L; +} + +struct tl_combinator_tree *tl_parse_nat_const (struct tree *T, int s) { + assert (T->type == type_nat_const); + assert (!T->nc); + if (s > 0) { + TL_ERROR ("Nat const can not preceed with %%\n"); + TL_FAIL; + } + assert (T->type == type_nat_const); + assert (!T->nc); + TL_INIT (L); + L = alloc_ctree_node (); + L->act = act_nat_const; + L->type = type_num_value; + int i; + long long x = 0; + for (i = 0; i < T->len; i++) { + x = x * 10 + T->text[i] - '0'; + } + L->type_flags = x; + return L; +} + +struct tl_combinator_tree *tl_parse_ident (struct tree *T, int s) { + assert (T->type == type_type_ident || T->type == type_var_ident || T->type == type_boxed_type_ident); + assert (!T->nc); + struct tl_var *v = tl_get_var (T->text, T->len); + TL_INIT (L); + if (v) { + L = alloc_ctree_node (); + L->act = act_var; + L->type = v->type ? type_num : type_type; + if (L->type == type_num && s) { + TL_ERROR ("Nat var can not preceed with %%\n"); + TL_FAIL; + } else { + if (s) { + L->flags |= 1; + } + } + L->type_len = 0; + L->type_flags = 0; + L->data = v->ptr; + return L; + } + +/* if (!mystrcmp2 (T->text, T->len, "#") || !mystrcmp2 (T->text, T->len, "Type")) { + L = alloc_ctree_node (); + L->act = act_type; + L->flags |= 2; + L->data = tl_get_type (T->text, T->len); + assert (L->data); + L->type = type_type; + L->type_len = 0; + L->type_flags = 0; + return L; + }*/ + + struct tl_constructor *c = tl_get_constructor (T->text, T->len); + if (c) { + assert (c->type); + if (c->type->constructors_num != 1) { + TL_ERROR ("Constructor can be used only if it is the only constructor of the type\n"); + return 0; + } + c->type->flags |= 1; + L = alloc_ctree_node (); + L->act = act_type; + L->flags |= 5; + L->data = c->type; + L->type = type_type; + L->type_len = c->type->params_num; + L->type_flags = c->type->params_types; + return L; + } + int x = tl_is_type_name (T->text, T->len); + if (x) { + struct tl_type *t = tl_add_type (T->text, T->len, -1, 0); + L = alloc_ctree_node (); + if (s) { + L->flags |= 1; + t->flags |= 8; + } + L->act = act_type; + L->data = t; + L->type = type_type; + L->type_len = t->params_num; + L->type_flags = t->params_types; + return L; + } else { + TL_ERROR ("Not a type/var ident `%.*s`\n", T->len, T->text); + return 0; + } +} + +struct tl_combinator_tree *tl_parse_any_term (struct tree *T, int s) { + switch (T->type) { + case type_type_term: + return tl_parse_type_term (T, s); + case type_nat_term: + return tl_parse_nat_term (T, s); + case type_term: + return tl_parse_term (T, s); + case type_expr: + return tl_parse_expr (T, s); + case type_subexpr: + return tl_parse_subexpr (T, s); + case type_nat_const: + return tl_parse_nat_const (T, s); + case type_type_ident: + case type_var_ident: + return tl_parse_ident (T, s); + default: + fprintf (stderr, "type = %d\n", T->type); + assert (0); + return 0; + } +} + +struct tl_combinator_tree *tl_parse_multiplicity (struct tree *T) { + assert (T->type == type_multiplicity); + assert (T->nc == 1); + return tl_parse_nat_term (T->c[0], 0); +} + +struct tl_combinator_tree *tl_parse_opt_args (struct tree *T) { + assert (T); + assert (T->type == type_opt_args); + assert (T->nc >= 2); + TL_INIT (R); + TL_TRY (tl_parse_type_term (T->c[T->nc - 1], 0), R); + assert (R->type == type_type && !R->type_len); + assert (tl_finish_subtree (R)); + struct tl_type *t = tl_tree_get_type (R); + //assert (t); + int tt = -1; + if (t && !strcmp (t->id, "#")) { + tt = 1; + } else if (t && !strcmp (t->id, "Type")) { + tt = 0; + } + if (tt < 0) { + TL_ERROR ("Optargs can be only of type # or Type\n"); + TL_FAIL; + } + + int i; + for (i = 0; i < T->nc - 1; i++) { + if (T->c[i]->type != type_var_ident) { + TL_ERROR ("Variable name expected\n"); + TL_FAIL; + } + if (T->c[i]->len == 1 && *T->c[i]->text == '_') { + TL_ERROR ("Variables can not be unnamed\n"); + TL_FAIL; + } + } + TL_INIT (H); +// for (i = T->nc - 2; i >= (T->nc >= 2 ? 0 : -1); i--) { + for (i = 0; i <= T->nc - 2; i++) { + TL_INIT (S); S = alloc_ctree_node (); + S->left = (i == T->nc - 2) ? R : tl_tree_dup (R) ; S->right = 0; + S->type = type_list_item; + S->type_len = 0; + S->act = act_field; + S->data = i >= 0 ? mystrdup (T->c[i]->text, T->c[i]->len) : 0; + if (tt >= 0) { + assert (S->data); + tl_add_var (S->data, S, tt); + } + S->flags = 33; + H = tl_union (H, S); + } + return H; +} + +struct tl_combinator_tree *tl_parse_args (struct tree *T); +struct tl_combinator_tree *tl_parse_args2 (struct tree *T) { + assert (T); + assert (T->type == type_args2); + assert (T->nc >= 1); + TL_INIT (R); + TL_INIT (L); + int x = 0; + char *field_name = 0; + if (T->c[x]->type == type_var_ident_opt || T->c[x]->type == type_var_ident) { + field_name = mystrdup (T->c[x]->text, T->c[x]->len); + if (!tl_add_field (field_name)) { + TL_ERROR ("Duplicate field name %s\n", field_name); + TL_FAIL; + } + x ++; + } + //fprintf (stderr, "%d %d\n", x, T->nc); + if (T->c[x]->type == type_multiplicity) { + L = tl_parse_multiplicity (T->c[x]); + if (!L) { TL_FAIL;} + x ++; + } else { + struct tl_var *v = tl_get_last_num_var (); + if (!v) { + TL_ERROR ("Expected multiplicity or nat var\n"); + TL_FAIL; + } + L = alloc_ctree_node (); + L->act = act_var; + L->type = type_num; + L->flags |= 128; + L->type_len = 0; + L->type_flags = 0; + L->data = v->ptr; + ((struct tl_combinator_tree *)(v->ptr))->flags |= 256; + } + namespace_push (); + while (x < T->nc) { + TL_TRY (tl_parse_args (T->c[x]), R); + x ++; + } + namespace_pop (); + struct tl_combinator_tree *S = alloc_ctree_node (); + S->type = type_type; + S->type_len = 0; + S->act = act_array; + S->left = L; + S->right = R; + //S->data = field_name; + + struct tl_combinator_tree *H = alloc_ctree_node (); + H->type = type_list_item; + H->act = act_field; + H->left = S; + H->right = 0; + H->data = field_name; + H->type_len = 0; + + return H; +} + +void tl_mark_vars (struct tl_combinator_tree *T); +struct tl_combinator_tree *tl_parse_args134 (struct tree *T) { + assert (T); + assert (T->type == type_args1 || T->type == type_args3 || T->type == type_args4); + assert (T->nc >= 1); + TL_INIT (R); + TL_TRY (tl_parse_type_term (T->c[T->nc - 1], 0), R); + assert (tl_finish_subtree (R)); + assert (R->type == type_type && !R->type_len); + struct tl_type *t = tl_tree_get_type (R); + //assert (t); + int tt = -1; + if (t && !strcmp (t->id, "#")) { + tt = 1; + } else if (t && !strcmp (t->id, "Type")) { + tt = 0; + } + +/* if (tt >= 0 && T->nc == 1) { + TL_ERROR ("Variables can not be unnamed (type %d)\n", tt); + }*/ + int last = T->nc - 2; + int excl = 0; + if (last >= 0 && T->c[last]->type == type_exclam) { + excl ++; + tl_mark_vars (R); + last --; + } + if (last >= 0 && T->c[last]->type == type_optional_arg_def) { + assert (T->c[last]->nc == 2); + TL_INIT (E); E = alloc_ctree_node (); + E->type = type_type; + E->act = act_opt_field; + E->left = tl_parse_ident (T->c[last]->c[0], 0); + int i; + long long x = 0; + for (i = 0; i < T->c[last]->c[1]->len; i++) { + x = x * 10 + T->c[last]->c[1]->text[i] - '0'; + } + E->left->type_flags = x; + E->type_flags = R->type_flags; + E->type_len = R->type_len; + E->right = R; + R = E; + last --; + } + int i; + for (i = 0; i < last; i++) { + if (T->c[i]->type != type_var_ident && T->c[i]->type != type_var_ident_opt) { + TL_ERROR ("Variable name expected\n"); + TL_FAIL; + } +/* if (tt >= 0 && (T->nc == 1 || (T->c[i]->len == 1 && *T->c[i]->text == '_'))) { + TL_ERROR ("Variables can not be unnamed\n"); + TL_FAIL; + }*/ + } + TL_INIT (H); +// for (i = T->nc - 2; i >= (T->nc >= 2 ? 0 : -1); i--) { + for (i = (last >= 0 ? 0 : -1); i <= last; i++) { + TL_INIT (S); S = alloc_ctree_node (); + S->left = (i == last) ? R : tl_tree_dup (R) ; S->right = 0; + S->type = type_list_item; + S->type_len = 0; + S->act = act_field; + S->data = i >= 0 ? mystrdup (T->c[i]->text, T->c[i]->len) : 0; + if (excl) { + S->flags |= FLAG_EXCL; + } + if (S->data && (T->c[i]->len >= 2 || *T->c[i]->text != '_')) { + if (!tl_add_field (S->data)) { + TL_ERROR ("Duplicate field name %s\n", (char *)S->data); + TL_FAIL; + } + } + if (tt >= 0) { + //assert (S->data); + char *name = S->data; + if (!name) { + static char s[20]; + sprintf (s, "%lld", lrand48 () * (1ll << 32) + lrand48 ()); + name = s; + } + struct tl_var *v = tl_add_var (name, S, tt); + if (!v) {TL_FAIL;} + v->flags |= 2; + } + + H = tl_union (H, S); + } + return H; +} + + +struct tl_combinator_tree *tl_parse_args (struct tree *T) { + assert (T->type == type_args); + assert (T->nc == 1); + switch (T->c[0]->type) { + case type_args1: + return tl_parse_args134 (T->c[0]); + case type_args2: + return tl_parse_args2 (T->c[0]); + case type_args3: + return tl_parse_args134 (T->c[0]); + case type_args4: + return tl_parse_args134 (T->c[0]); + default: + assert (0); + return 0; + } +} + +void tl_mark_vars (struct tl_combinator_tree *T) { + if (!T) { return; } + if (T->act == act_var) { + char *id = ((struct tl_combinator_tree *)(T->data))->data; + struct tl_var *v = tl_get_var (id, strlen (id)); + assert (v); + v->flags |= 1; + } + tl_mark_vars (T->left); + tl_mark_vars (T->right); +} + +struct tl_combinator_tree *tl_parse_result_type (struct tree *T) { + assert (T->type == type_result_type); + assert (T->nc >= 1); + assert (T->nc <= 64); + + TL_INIT (L); + + if (tl_get_var (T->c[0]->text, T->c[0]->len)) { + if (T->nc != 1) { + TL_ERROR ("Variable can not take params\n"); + TL_FAIL; + } + L = alloc_ctree_node (); + L->act = act_var; + L->type = type_type; + struct tl_var *v = tl_get_var (T->c[0]->text, T->c[0]->len); + if (v->type) { + TL_ERROR ("Type mistmatch\n"); + TL_FAIL; + } + L->data = v->ptr; +// assert (v->ptr); + } else { + L = alloc_ctree_node (); + L->act = act_type; + L->type = type_type; + struct tl_type *t = tl_add_type (T->c[0]->text, T->c[0]->len, -1, 0); + assert (t); + L->type_len = t->params_num; + L->type_flags = t->params_types; + L->data = t; + + int i; + for (i = 1; i < T->nc; i++) { + TL_TRY (tl_parse_any_term (T->c[i], 0), L); + assert (L->right); + assert (L->right->type == type_num || L->right->type == type_num_value || (L->right->type == type_type && L->right->type_len == 0)); + } + } + + if (!tl_finish_subtree (L)) { + TL_FAIL; + } + + tl_mark_vars (L); + return L; +} + +int __ok; +void tl_var_check_used (struct tl_var *v) { + __ok = __ok && (v->flags & 3); +} + +int tl_parse_combinator_decl (struct tree *T, int fun) { + assert (T->type == type_combinator_decl); + assert (T->nc >= 3); + namespace_level = 0; + tl_clear_vars (); + tl_clear_fields (); + TL_INIT (L); + TL_INIT (R); + + int i = 1; + while (i < T->nc - 2 && T->c[i]->type == type_opt_args) { + TL_TRY (tl_parse_opt_args (T->c[i]), L); + i++; + } + while (i < T->nc - 2 && T->c[i]->type == type_args) { + TL_TRY (tl_parse_args (T->c[i]), L); + i++; + } + assert (i == T->nc - 2 && T->c[i]->type == type_equals); + i ++; + + R = tl_parse_result_type (T->c[i]); + if (!R) { TL_FAIL; } + + struct tl_type *t = tl_tree_get_type (R); + if (!fun && !t) { + TL_ERROR ("Only functions can return variables\n"); + } + assert (t || fun); + + assert (namespace_level == 0); + __ok = 1; + tree_act_tl_var (vars[0], tl_var_check_used); + if (!__ok) { + TL_ERROR ("Not all variables are used in right side\n"); + TL_FAIL; + } + + if (tl_get_constructor (T->c[0]->text, T->c[0]->len) || tl_get_function (T->c[0]->text, T->c[0]->len)) { + TL_ERROR ("Duplicate combinator id %.*s\n", T->c[0]->len, T->c[0]->text); + return 0; + } + struct tl_constructor *c = !fun ? tl_add_constructor (t, T->c[0]->text, T->c[0]->len, 0) : tl_add_function (t, T->c[0]->text, T->c[0]->len, 0); + if (!c) { TL_FAIL; } + c->left = L; + c->right = R; + + if (!c->name) { + tl_count_combinator_name (c); + } + tl_print_combinator (c); + + return 1; +} + +void change_var_ptrs (struct tl_combinator_tree *O, struct tl_combinator_tree *D, struct tree_var_value **V) { + if (!O || !D) { + assert (!O && !D); + return; + } + if (O->act == act_field) { + struct tl_type *t = tl_tree_get_type (O->left); + if (t && (!strcmp (t->id, "#") || !strcmp (t->id, "Type"))) { + tl_set_var_value (V, O, D); + } + } + if (O->act == act_var) { + assert (D->data == O->data); + D->data = tl_get_var_value (V, O->data); + assert (D->data); + } + change_var_ptrs (O->left, D->left, V); + change_var_ptrs (O->right, D->right, V); +} + +struct tl_combinator_tree *change_first_var (struct tl_combinator_tree *O, struct tl_combinator_tree **X, struct tl_combinator_tree *Y) { + if (!O) { return (void *)-2l; }; + if (O->act == act_field && !*X) { + struct tl_type *t = tl_tree_get_type (O->left); + if (t && !strcmp (t->id, "#")) { + if (Y->type != type_num && Y->type != type_num_value) { + TL_ERROR ("change_var: Type mistmatch\n"); + return 0; + } else { + *X = O; + return (void *)-1l; + } + } + if (t && !strcmp (t->id, "Type")) { + if (Y->type != type_type || Y->type_len != 0) { + TL_ERROR ("change_var: Type mistmatch\n"); + return 0; + } else { + *X = O; + return (void *)-1l; + } + } + } + if (O->act == act_var) { + if (O->data == *X) { + struct tl_combinator_tree *R = tl_tree_dup (Y); + if (O->type == type_num || O->type == type_num_value) { R->type_flags += O->type_flags; } + return R; + } + } + struct tl_combinator_tree *t; + t = change_first_var (O->left, X, Y); + if (!t) { return 0;} + if (t == (void *)-1l) { + t = change_first_var (O->right, X, Y); + if (!t) { return 0;} + if (t == (void *)-1l) { return (void *)-1l; } + if (t != (void *)-2l) { return t;} + return (void *)-1l; + } + if (t != (void *)-2l) { + O->left = t; + } + t = change_first_var (O->right, X, Y); + if (!t) { return 0;} + if (t == (void *)-1l) { + return O->left; + } + if (t != (void *)-2l) { + O->right = t; + } + return O; +} + + +int uniformize (struct tl_combinator_tree *L, struct tl_combinator_tree *R, struct tree_var_value **T); +struct tree_var_value **_T; +int __tok; +void check_nat_val (struct tl_var_value v) { + if (!__tok) { return; } + long long x = v.num_val; + struct tl_combinator_tree *L = v.val; + if (L->type == type_type) { return;} + while (1) { + if (L->type == type_num_value) { + if (x + L->type_flags < 0) { + __tok = 0; + return; + } else { + return; + } + } + assert (L->type == type_num); + x += L->type_flags; + x += tl_get_var_value_num (_T, L->data); + L = tl_get_var_value (_T, L->data); + if (!L) { return;} + } +} + +int check_constructors_equal (struct tl_combinator_tree *L, struct tl_combinator_tree *R, struct tree_var_value **T) { + if (!uniformize (L, R, T)) { return 0; } + __tok = 1; + _T = T; + tree_act_var_value (*T, check_nat_val); + return __tok; +} + +struct tl_combinator_tree *reduce_type (struct tl_combinator_tree *A, struct tl_type *t) { + assert (A); + if (A->type_len == t->params_num) { + assert (A->type_flags == t->params_types); + A->act = act_type; + A->type = type_type; + A->left = A->right = 0; + A->data = t; + return A; + } + A->left = reduce_type (A->left, t); + return A; +} + +struct tl_combinator_tree *change_value_var (struct tl_combinator_tree *O, struct tree_var_value **X) { + if (!O) { return (void *)-2l; }; + while (O->act == act_var) { + assert (O->data); + if (!tl_get_var_value (X, O->data)) { + break; + } + if (O->type == type_type) { + O = tl_tree_dup (tl_get_var_value (X, O->data)); + } else { + long long n = tl_get_var_value_num (X, O->data); + struct tl_combinator_tree *T = tl_get_var_value (X, O->data); + O->data = T->data; + O->type = T->type; + O->act = T->act; + O->type_flags = O->type_flags + n + T->type_flags; + } + } + if (O->act == act_field) { + if (tl_get_var_value (X, O)) { return (void *)-1l; } + } + struct tl_combinator_tree *t; + t = change_value_var (O->left, X); + if (!t) { return 0;} + if (t == (void *)-1l) { + t = change_value_var (O->right, X); + if (!t) { return 0;} + if (t == (void *)-1l) { return (void *)-1l; } + if (t != (void *)-2l) { return t;} + return (void *)-1l; + } + if (t != (void *)-2l) { + O->left = t; + } + t = change_value_var (O->right, X); + if (!t) { return 0;} + if (t == (void *)-1l) { + return O->left; + } + if (t != (void *)-2l) { + O->right = t; + } + return O; +} + +int tl_parse_partial_type_app_decl (struct tree *T) { + assert (T->type == type_partial_type_app_decl); + assert (T->nc >= 1); + + assert (T->c[0]->type == type_boxed_type_ident); + struct tl_type *t = tl_get_type (T->c[0]->text, T->c[0]->len); + if (!t) { + TL_ERROR ("Can not make partial app for unknown type\n"); + return 0; + } + + tl_type_finalize (t); + + struct tl_combinator_tree *L = tl_parse_ident (T->c[0], 0); + assert (L); + int i; + tl_buf_reset (); + int cc = T->nc - 1; + for (i = 1; i < T->nc; i++) { + TL_TRY (tl_parse_any_term (T->c[i], 0), L); + tl_buf_add_tree (L->right, 1); + } + + while (L->type_len) { + struct tl_combinator_tree *C = alloc_ctree_node (); + C->act = act_var; + C->type = (L->type_flags & 1) ? type_num : type_type; + C->type_len = 0; + C->type_flags = 0; + C->data = (void *)-1l; + L = tl_union (L, C); + if (!L) { return 0; } + } + + + static char _buf[100000]; + snprintf (_buf, 100000, "%s%.*s", t->id, buf_pos, buf); + struct tl_type *nt = tl_add_type (_buf, strlen (_buf), t->params_num - cc, t->params_types >> cc); + assert (nt); + //snprintf (_buf, 100000, "%s #", t->id); + //nt->real_id = strdup (_buf); + + for (i = 0; i < t->constructors_num; i++) { + struct tl_constructor *c = t->constructors[i]; + struct tree_var_value *V = 0; + TL_INIT (A); + TL_INIT (B); + A = tl_tree_dup (c->left); + B = tl_tree_dup (c->right); + + struct tree_var_value *W = 0; + change_var_ptrs (c->left, A, &W); + change_var_ptrs (c->right, B, &W); + + + if (!check_constructors_equal (B, L, &V)) { continue; } + B = reduce_type (B, nt); + A = change_value_var (A, &V); + if (A == (void *)-1l) { A = 0;} + B = change_value_var (B, &V); + assert (B != (void *)-1l); + snprintf (_buf, 100000, "%s%.*s", c->id, buf_pos, buf); + + struct tl_constructor *r = tl_add_constructor (nt, _buf, strlen (_buf), 1); + snprintf (_buf, 100000, "%s", c->id); + r->real_id = tstrdup (_buf); + + r->left = A; + r->right = B; + if (!r->name) { + tl_count_combinator_name (r); + } + tl_print_combinator (r); + } + + return 1; +} + +int tl_parse_partial_comb_app_decl (struct tree *T, int fun) { + assert (T->type == type_partial_comb_app_decl); + + struct tl_constructor *c = !fun ? tl_get_constructor (T->c[0]->text, T->c[0]->len) : tl_get_function (T->c[0]->text, T->c[0]->len); + if (!c) { + TL_ERROR ("Can not make partial app for undefined combinator\n"); + return 0; + } + + //TL_INIT (K); + //static char buf[1000]; + //int x = sprintf (buf, "%s", c->id); + TL_INIT (L); + TL_INIT (R); + L = tl_tree_dup (c->left); + R = tl_tree_dup (c->right); + + + struct tree_var_value *V = 0; + change_var_ptrs (c->left, L, &V); + change_var_ptrs (c->right, R, &V); + V = tree_clear_var_value (V); + + int i; + tl_buf_reset (); + for (i = 1; i < T->nc; i++) { + TL_INIT (X); + TL_INIT (Z); + X = tl_parse_any_term (T->c[i], 0); + struct tl_combinator_tree *K = 0; + if (!(Z = change_first_var (L, &K, X))) { + TL_FAIL; + } + L = Z; + if (!K) { + TL_ERROR ("Partial app: not enougth variables (i = %d)\n", i); + TL_FAIL; + } + if (!(Z = change_first_var (R, &K, X))) { + TL_FAIL; + } + assert (Z == R); + tl_buf_add_tree (X, 1); + } + + static char _buf[100000]; + snprintf (_buf, 100000, "%s%.*s", c->id, buf_pos, buf); +// fprintf (stderr, "Local id: %s\n", _buf); + + struct tl_constructor *r = !fun ? tl_add_constructor (c->type, _buf, strlen (_buf), 1) : tl_add_function (c->type, _buf, strlen (_buf), 1); + r->left = L; + r->right = R; + snprintf (_buf, 100000, "%s", c->id); + r->real_id = tstrdup (_buf); + if (!r->name) { + tl_count_combinator_name (r); + } + tl_print_combinator (r); + return 1; +} + + +int tl_parse_partial_app_decl (struct tree *T, int fun) { + assert (T->type == type_partial_app_decl); + assert (T->nc == 1); + if (T->c[0]->type == type_partial_comb_app_decl) { + return tl_parse_partial_comb_app_decl (T->c[0], fun); + } else { + if (fun) { + TL_ERROR ("Partial type app in functions block\n"); + TL_FAIL; + } + return tl_parse_partial_type_app_decl (T->c[0]); + } +} + +int tl_parse_final_final (struct tree *T) { + assert (T->type == type_final_final); + assert (T->nc == 1); + struct tl_type *R; + if ((R = tl_get_type (T->c[0]->text, T->c[0]->len))) { + R->flags |= 1; + return 1; + } else { + TL_ERROR ("Final statement for type `%.*s` before declaration\n", T->c[0]->len, T->c[0]->text); + TL_FAIL; + } +} + +int tl_parse_final_new (struct tree *T) { + assert (T->type == type_final_new); + assert (T->nc == 1); + if (tl_get_type (T->c[0]->text, T->c[0]->len)) { + TL_ERROR ("New statement: type `%.*s` already declared\n", T->c[0]->len, T->c[0]->text); + TL_FAIL; + } else { + return 1; + } +} + +int tl_parse_final_empty (struct tree *T) { + assert (T->type == type_final_empty); + assert (T->nc == 1); + if (tl_get_type (T->c[0]->text, T->c[0]->len)) { + TL_ERROR ("New statement: type `%.*s` already declared\n", T->c[0]->len, T->c[0]->text); + TL_FAIL; + } + struct tl_type *t = tl_add_type (T->c[0]->text, T->c[0]->len, 0, 0); + assert (t); + t->flags |= 1 | FLAG_EMPTY; + return 1; +} + +int tl_parse_final_decl (struct tree *T, int fun) { + assert (T->type == type_final_decl); + assert (!fun); + assert (T->nc == 1); + switch (T->c[0]->type) { + case type_final_new: + return tl_parse_final_new (T->c[0]); + case type_final_final: + return tl_parse_final_final (T->c[0]); + case type_final_empty: + return tl_parse_final_empty (T->c[0]); + default: + assert (0); + return 0; + } +} + +int tl_parse_builtin_combinator_decl (struct tree *T, int fun) { + if (fun) { + TL_ERROR ("Builtin type can not be described in function block\n"); + return -1; + } + assert (T->type == type_builtin_combinator_decl); + assert (T->nc == 2); + assert (T->c[0]->type == type_full_combinator_id); + assert (T->c[1]->type == type_boxed_type_ident); + + + if ((!mystrcmp2 (T->c[0]->text, T->c[0]->len, "int") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Int")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "long") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Long")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "double") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Double")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "object") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Object")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "function") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "Function")) || + (!mystrcmp2 (T->c[0]->text, T->c[0]->len, "string") && !mystrcmp2 (T->c[1]->text, T->c[1]->len, "String"))) { + struct tl_type *t = tl_add_type (T->c[1]->text, T->c[1]->len, 0, 0); + if (!t) { + return 0; + } + struct tl_constructor *c = tl_add_constructor (t, T->c[0]->text, T->c[0]->len, 0); + if (!c) { + return 0; + } + + c->left = alloc_ctree_node (); + c->left->act = act_question_mark; + c->left->type = type_list_item; + + c->right = alloc_ctree_node (); + c->right->act = act_type; + c->right->data = t; + c->right->type = type_type; + + if (!c->name) { + tl_count_combinator_name (c); + } + tl_print_combinator (c); + } else { + TL_ERROR ("Unknown builting type `%.*s`\n", T->c[0]->len, T->c[0]->text); + return 0; + } + + return 1; +} + +int tl_parse_declaration (struct tree *T, int fun) { + assert (T->type == type_declaration); + assert (T->nc == 1); + switch (T->c[0]->type) { + case type_combinator_decl: + return tl_parse_combinator_decl (T->c[0], fun); + case type_partial_app_decl: + return tl_parse_partial_app_decl (T->c[0], fun); + case type_final_decl: + return tl_parse_final_decl (T->c[0], fun); + case type_builtin_combinator_decl: + return tl_parse_builtin_combinator_decl (T->c[0], fun); + default: + assert (0); + return 0; + } +} + +int tl_parse_constr_declarations (struct tree *T) { + assert (T->type == type_constr_declarations); + int i; + for (i = 0; i < T->nc; i++) { + TL_TRY_PES (tl_parse_declaration (T->c[i], 0)); + } + return 1; +} + +int tl_parse_fun_declarations (struct tree *T) { + assert (T->type == type_fun_declarations); + int i; + for (i = 0; i < T->nc; i++) { + TL_TRY_PES (tl_parse_declaration (T->c[i], 1)); + } + return 1; +} + +int tl_tree_lookup_value (struct tl_combinator_tree *L, void *var, struct tree_var_value **T) { + if (!L) { + return -1; + } + if (L->act == act_var && L->data == var) { + return 0; + } + if (L->act == act_var) { + struct tl_combinator_tree *E = tl_get_var_value (T, L->data); + if (!E) { return -1;} + else { return tl_tree_lookup_value (E, var, T); } + } + if (tl_tree_lookup_value (L->left, var, T) >= 0) { return 1; } + if (tl_tree_lookup_value (L->right, var, T) >= 0) { return 1; } + return -1; +} + +int tl_tree_lookup_value_nat (struct tl_combinator_tree *L, void *var, long long x, struct tree_var_value **T) { + assert (L); + if (L->type == type_num_value) { return -1; } + assert (L->type == type_num); + assert (L->act == act_var); + if (L->data == var) { + return x == L->type_flags ? 0 : 1; + } else { + if (!tl_get_var_value (T, L->data)) { + return -1; + } + return tl_tree_lookup_value_nat (tl_get_var_value (T, L->data), var, x + tl_get_var_value_num (T, L->data), T); + } + +} + +int uniformize (struct tl_combinator_tree *L, struct tl_combinator_tree *R, struct tree_var_value **T) { + if (!L || !R) { + assert (!L && !R); + return 1; + } + if (R->act == act_var) { + struct tl_combinator_tree *_ = R; R = L; L = _; + } + + if (L->type == type_type) { + if (R->type != type_type || L->type_len != R->type_len || L->type_flags != R->type_flags) { + return 0; + } + if (R->data == (void *)-1l || L->data == (void *)-1l) { return 1;} + if (L->act == act_var) { + int x = tl_tree_lookup_value (R, L->data, T); + if (x > 0) { +// if (tl_tree_lookup_value (R, L->data, T) > 0) { + return 0; + } + if (x == 0) { + return 1; + } + struct tl_combinator_tree *E = tl_get_var_value (T, L->data); + if (!E) { + tl_set_var_value (T, L->data, R); + return 1; + } else { + return uniformize (E, R, T); + } + } else { + if (L->act != R->act || L->data != R->data) { + return 0; + } + return uniformize (L->left, R->left, T) && uniformize (L->right, R->right, T); + } + } else { + assert (L->type == type_num || L->type == type_num_value); + if (R->type != type_num && R->type != type_num_value) { + return 0; + } + assert (R->type == type_num || R->type == type_num_value); + if (R->data == (void *)-1l || L->data == (void *)-1l) { return 1;} + long long x = 0; + struct tl_combinator_tree *K = L; + while (1) { + x += K->type_flags; + if (K->type == type_num_value) { + break; + } + if (!tl_get_var_value (T, K->data)) { + int s = tl_tree_lookup_value_nat (R, K->data, K->type_flags, T); + if (s > 0) { + return 0; + } + if (s == 0) { + return 1; + } + /*tl_set_var_value_num (T, K->data, R, -x); + return 1;*/ + break; + } + x += tl_get_var_value_num (T, K->data); + K = tl_get_var_value (T, K->data); + } + long long y = 0; + struct tl_combinator_tree *M = R; + while (1) { + y += M->type_flags; + if (M->type == type_num_value) { + break; + } + if (!tl_get_var_value (T, M->data)) { + int s = tl_tree_lookup_value_nat (L, M->data, M->type_flags, T); + if (s > 0) { + return 0; + } + if (s == 0) { + return 1; + } + /*tl_set_var_value_num (T, M->data, L, -y); + return 1;*/ + break; + } + y += tl_get_var_value_num (T, M->data); + M = tl_get_var_value (T, M->data); + } + if (K->type == type_num_value && M->type == type_num_value) { + return x == y; + } + if (M->type == type_num_value) { + tl_set_var_value_num (T, K->data, M, -(x - y + M->type_flags)); + return 1; + } else if (K->type == type_num_value) { + tl_set_var_value_num (T, M->data, K, -(y - x + K->type_flags)); + return 1; + } else { + if (x >= y) { + tl_set_var_value_num (T, K->data, M, -(x - y + M->type_flags)); + } else { + tl_set_var_value_num (T, M->data, K, -(y - x + K->type_flags)); + } + return 1; + } + } + return 0; +} + + +void tl_type_check (struct tl_type *t) { + if (!__ok) return; + if (!strcmp (t->id, "#")) { t->name = 0x70659eff; return; } + if (!strcmp (t->id, "Type")) { t->name = 0x2cecf817; return; } + if (t->constructors_num <= 0 && !(t->flags & FLAG_EMPTY)) { + TL_ERROR ("Type %s has no constructors\n", t->id); + __ok = 0; + return; + } + int i, j; + t->name = 0; + for (i = 0; i < t->constructors_num; i++) { + t->name ^= t->constructors[i]->name; + } + for (i = 0; i < t->constructors_num; i++) { + for (j = i + 1; j < t->constructors_num; j++) { + struct tree_var_value *v = 0; + if (check_constructors_equal (t->constructors[i]->right, t->constructors[j]->right, &v)) { + t->flags |= 16; + } + } + } + if ((t->flags & 24) == 24) { + TL_WARNING ("Warning: Type %s has overlapping costructors, but it is used with `%%`\n", t->id); + } + int z = 0; + int sid = 0; + for (i = 0; i < t->constructors_num; i++) if (*t->constructors[i]->id == '_') { + z ++; + sid = i; + } + if (z > 1) { + TL_ERROR ("Type %s has %d default constructors\n", t->id, z); + __ok = 0; + return; + } + if (z == 1 && (t->flags & 8)) { + TL_ERROR ("Type %s has default constructors and used bare\n", t->id); + __ok = 0; + return; + } + if (z) { + struct tl_constructor *c; + c = t->constructors[sid]; + t->constructors[sid] = t->constructors[t->constructors_num - 1]; + t->constructors[t->constructors_num - 1] = c; + } +} + +struct tl_program *tl_parse (struct tree *T) { + assert (T); + assert (T->type == type_tl_program); + int i; + tl_program_cur = talloc (sizeof (*tl_program_cur)); + tl_add_type ("#", 1, 0, 0); + tl_add_type ("Type", 4, 0, 0); + for (i = 0; i < T->nc; i++) { + if (T->c[i]->type == type_constr_declarations) { TL_TRY_PES (tl_parse_constr_declarations (T->c[i])); } + else { TL_TRY_PES (tl_parse_fun_declarations (T->c[i])) } + } + __ok = 1; + tree_act_tl_type (tl_type_tree, tl_type_check); + if (!__ok) { + return 0; + } + return tl_program_cur; +} + +FILE *__f; +int num = 0; + +void wint (int a) { +// printf ("%d ", a); + a = htole32 (a); + assert (fwrite (&a, 1, 4, __f) == 4); +} + +void wdata (const void *x, int len) { + assert (fwrite (x, 1, len, __f) == len); +} + +void wstr (const char *s) { + if (s) { +// printf ("\"%s\" ", s); + int x = strlen (s); + if (x <= 254) { + unsigned char x_c = (unsigned char)x; + assert (fwrite (&x_c, 1, 1, __f) == 1); + } else { + fprintf (stderr, "String is too big...\n"); + assert (0); + } + wdata (s, x); + x ++; // The header, containing the length, which is 1 byte + int t = 0; + if (x & 3) { + // Let's hope it's truly zero on every platform + wdata (&t, 4 - (x & 3)); + } + } else { +// printf (" "); + wint (0); + } +} + +void wll (long long a) { +// printf ("%lld ", a); + a = htole64 (a); + assert (fwrite (&a, 1, 8, __f) == 8); +} + +int count_list_size (struct tl_combinator_tree *T) { + assert (T->type == type_list || T->type == type_list_item); + if (T->type == type_list_item) { + return 1; + } else { + return count_list_size (T->left) + count_list_size (T->right); + } +} + +void write_type_flags (long long flags) { + int new_flags = 0; + if (flags & 1) { + new_flags |= FLAG_BARE; + } + if (flags & FLAG_DEFAULT_CONSTRUCTOR) { + new_flags |= FLAG_DEFAULT_CONSTRUCTOR; + } + wint (new_flags); +} + +void write_field_flags (long long flags) { + int new_flags = 0; + //fprintf (stderr, "%lld\n", flags); + if (flags & 1) { + new_flags |= FLAG_BARE; + } + if (flags & 32) { + new_flags |= FLAG_OPT_VAR; + } + if (flags & FLAG_EXCL) { + new_flags |= FLAG_EXCL; + } + if (flags & FLAG_OPT_FIELD) { + // new_flags |= FLAG_OPT_FIELD; + new_flags |= 2; + } + if (flags & (1 << 21)) { + new_flags |= 4; + } + wint (new_flags); +} + +void write_var_type_flags (long long flags) { + int new_flags = 0; + if (flags & 1) { + new_flags |= FLAG_BARE; + } + if (new_flags & FLAG_BARE) { + TL_ERROR ("Sorry, bare vars are not (yet ?) supported.\n"); + assert (!(new_flags & FLAG_BARE)); + } + wint (new_flags); +} + +void write_tree (struct tl_combinator_tree *T, int extra, struct tree_var_value **v, int *last_var); +void write_args (struct tl_combinator_tree *T, struct tree_var_value **v, int *last_var) { + assert (T->type == type_list || T->type == type_list_item); + if (T->type == type_list) { + assert (T->act == act_union); + assert (T->left); + assert (T->right); + write_args (T->left, v, last_var); + write_args (T->right, v, last_var); + return; + } + wint (TLS_ARG_V2); + assert (T->act == act_field); + assert (T->left); + wstr (T->data && strcmp (T->data, "_") ? T->data : 0); + long long f = T->flags; + if (T->left->act == act_opt_field) { + f |= (1 << 20); + } + if (T->left->act == act_type && T->left->data && (!strcmp (((struct tl_type *)T->left->data)->id, "#") || !strcmp (((struct tl_type *)T->left->data)->id, "Type"))) { + write_field_flags (f | (1 << 21)); + wint (*last_var); + *last_var = (*last_var) + 1; + tl_set_var_value_num (v, T, 0, (*last_var) - 1); + } else { + write_field_flags (f); + } + write_tree (T->left, 0, v, last_var); +} + +void write_array (struct tl_combinator_tree *T, struct tree_var_value **v, int *last_var) { + wint (TLS_ARRAY); + write_tree (T->left, 0, v, last_var); + write_tree (T->right, 0, v, last_var); +} + +void write_type_rec (struct tl_combinator_tree *T, int cc, struct tree_var_value **v, int *last_var) { + if (T->act == act_arg) { + write_type_rec (T->left, cc + 1, v, last_var); + if (T->right->type == type_num_value || T->right->type == type_num) { + wint (TLS_EXPR_NAT); + } else { + wint (TLS_EXPR_TYPE); + } + write_tree (T->right, 0, v, last_var); + } else { + assert (T->act == act_var || T->act == act_type); + if (T->act == act_var) { + assert (!cc); + wint (TLS_TYPE_VAR); + wint (tl_get_var_value_num (v, T->data)); + write_var_type_flags (T->flags); + //wint (T->flags); + } else { + wint (TLS_TYPE_EXPR); + struct tl_type *t = T->data; + wint (t->name); + write_type_flags (T->flags); +// wint (T->flags); + wint (cc); +// fprintf (stderr, "cc = %d\n", cc); + } + } +} + +void write_opt_type (struct tl_combinator_tree *T, struct tree_var_value **v, int *last_var) { + wint (tl_get_var_value_num (v, T->left->data)); + wint (T->left->type_flags); +// write_tree (T->right, 0, v, last_var); + assert (T); + T = T->right; + switch (T->type) { + case type_type: + if (T->act == act_array) { + write_array (T, v, last_var); + } else if (T->act == act_type || T->act == act_var || T->act == act_arg) { + write_type_rec (T, 0, v, last_var); + } else { + assert (0); + } + break; + default: + assert (0); + } +} + +void write_tree (struct tl_combinator_tree *T, int extra, struct tree_var_value **v, int *last_var) { + assert (T); + switch (T->type) { + case type_list_item: + case type_list: + if (extra) { + wint (TLS_COMBINATOR_RIGHT_V2); + } + wint (count_list_size (T)); + write_args (T, v, last_var); + break; + case type_num_value: + wint ((int)TLS_NAT_CONST); + wint (T->type_flags); + break; + case type_num: + wint ((int)TLS_NAT_VAR); + wint (T->type_flags); + wint (tl_get_var_value_num (v, T->data)); + break; + case type_type: + if (T->act == act_array) { + write_array (T, v, last_var); + } else if (T->act == act_type || T->act == act_var || T->act == act_arg) { + write_type_rec (T, 0, v, last_var); + } else { + assert (T->act == act_opt_field); + write_opt_type (T, v, last_var); + } + break; + default: + assert (0); + } +} + +void write_type (struct tl_type *t) { + wint (TLS_TYPE); + wint (t->name); + wstr (t->id); + wint (t->constructors_num); + wint (t->flags); + wint (t->params_num); + wll (t->params_types); +} + +int is_builtin_type (const char *id) { + return !strcmp (id, "int") || !strcmp (id, "long") || !strcmp (id, "double") || !strcmp (id, "string") + || !strcmp(id, "object") || !strcmp(id, "function"); +} + +void write_combinator (struct tl_constructor *c) { + wint (c->name); + wstr (c->id); + wint (c->type ? c->type->name : 0); + struct tree_var_value *T = 0; + int x = 0; + assert (c->right); + if (c->left) { + if (is_builtin_type (c->id)) { + wint (TLS_COMBINATOR_LEFT_BUILTIN); + } else { + wint (TLS_COMBINATOR_LEFT); + // FIXME: What is that? +// wint (count_list_size (c->left)); + write_tree (c->left, 0, &T, &x); + } + } else { + wint (TLS_COMBINATOR_LEFT); + wint (0); + } + wint (TLS_COMBINATOR_RIGHT_V2); + write_tree (c->right, 1, &T, &x); +} + +void write_constructor (struct tl_constructor *c) { + wint (TLS_COMBINATOR); + write_combinator (c); +} + +void write_function (struct tl_constructor *c) { + wint (TLS_COMBINATOR); + write_combinator (c); +} + +void write_type_constructors (struct tl_type *t) { + int i; + for (i = 0; i < t->constructors_num; i++) { + write_constructor (t->constructors[i]); + } +} + +void write_types (FILE *f) { + __f = f; + wint (TLS_SCHEMA_V2); + wint (0); +#ifdef TL_PARSER_NEED_TIME + wint (time (0)); +#else + /* Make the tlo reproducible by default. Rationale: https://wiki.debian.org/ReproducibleBuilds/Howto#Introduction */ + wint (0); +#endif + num = 0; + wint (total_types_num); + tree_act_tl_type (tl_type_tree, write_type); + wint (total_constructors_num); + tree_act_tl_type (tl_type_tree, write_type_constructors); + wint (total_functions_num); + tree_act_tl_constructor (tl_function_tree, write_function); +} diff --git a/td/generate/tl-parser/tl-parser.h b/td/generate/tl-parser/tl-parser.h new file mode 100644 index 000000000..59209d4a5 --- /dev/null +++ b/td/generate/tl-parser/tl-parser.h @@ -0,0 +1,223 @@ +/* + This file is part of tgl-libary/tlc + + Tgl-library/tlc is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Tgl-library/tlc is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this tgl-library/tlc. If not, see . + + Copyright Vitaly Valtman 2014 + + It is derivative work of VK/KittenPHP-DB-Engine (https://github.com/vk-com/kphp-kdb/) + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman + +*/ + +#ifndef __TL_PARSER_NEW_H__ +#define __TL_PARSER_NEW_H__ + +#include + +enum lex_type { + lex_error, + lex_char, + lex_triple_minus, + lex_uc_ident, + lex_lc_ident, + lex_eof, + lex_final, + lex_new, + lex_none, + lex_num, + lex_empty +}; + + +struct curlex { + char *ptr; + int len; + enum lex_type type; + int flags; +}; + +struct parse { + char *text; + int pos; + int len; + int line; + int line_pos; + struct curlex lex; +}; + + +enum tree_type { + type_tl_program, + type_fun_declarations, + type_constr_declarations, + type_declaration, + type_combinator_decl, + type_equals, + type_partial_app_decl, + type_final_decl, + type_full_combinator_id, + type_opt_args, + type_args, + type_args1, + type_args2, + type_args3, + type_args4, + type_boxed_type_ident, + type_subexpr, + type_partial_comb_app_decl, + type_partial_type_app_decl, + type_final_new, + type_final_final, + type_final_empty, +// type_type, + type_var_ident, + type_var_ident_opt, + type_multiplicity, + type_type_term, + type_term, + type_percent, + type_result_type, + type_expr, + type_nat_term, + type_combinator_id, + type_nat_const, + type_type_ident, + type_builtin_combinator_decl, + type_exclam, + type_optional_arg_def +}; + +struct tree { + char *text; + int len; + enum tree_type type; + int lex_line; + int lex_line_pos; + int flags; + int size; + int nc; + struct tree **c; +}; + + +#define TL_ACT(x) (x == act_var ? "act_var" : x == act_field ? "act_field" : x == act_plus ? "act_plus" : x == act_type ? "act_type" : x == act_nat_const ? "act_nat_const" : x == act_array ? "act_array" : x == act_question_mark ? "act_question_mark" : \ + x == act_union ? "act_union" : x == act_arg ? "act_arg" : x == act_opt_field ? "act_opt_field" : "act_unknown") + +#define TL_TYPE(x) (x == type_num ? "type_num" : x == type_type ? "type_type" : x == type_list_item ? "type_list_item" : x == type_list ? "type_list" : x == type_num_value ? "type_num_value" : "type_unknown") +enum combinator_tree_action { + act_var, + act_field, + act_plus, + act_type, + act_nat_const, + act_array, + act_question_mark, + act_union, + act_arg, + act_opt_field +}; + +enum combinator_tree_type { + type_num, + type_num_value, + type_type, + type_list_item, + type_list +}; + +struct tl_combinator_tree { + enum combinator_tree_action act; + struct tl_combinator_tree *left, *right; + char *name; + void *data; + long long flags; + enum combinator_tree_type type; + int type_len; + long long type_flags; +}; + + +struct tl_program { + int types_num; + int functions_num; + int constructors_num; + struct tl_type **types; + struct tl_function **functions; +// struct tl_constuctor **constructors; +}; + +struct tl_type { + char *id; + char *print_id; + char *real_id; + unsigned name; + int flags; + + int params_num; + long long params_types; + + int constructors_num; + struct tl_constructor **constructors; +}; + +struct tl_constructor { + char *id; + char *print_id; + char *real_id; + unsigned name; + struct tl_type *type; + + struct tl_combinator_tree *left; + struct tl_combinator_tree *right; +}; + +struct tl_var { + char *id; + struct tl_combinator_tree *ptr; + int type; + int flags; +}; + +struct parse *tl_init_parse_file (const char *fname); +struct tree *tl_parse_lex (struct parse *P); +void tl_print_parse_error (void); +struct tl_program *tl_parse (struct tree *T); + +void write_types (FILE *f); + +#define FLAG_BARE 1 +#define FLAG_OPT_VAR (1 << 17) +#define FLAG_EXCL (1 << 18) +#define FLAG_OPT_FIELD (1 << 20) +#define FLAG_IS_VAR (1 << 21) +#define FLAG_DEFAULT_CONSTRUCTOR (1 << 25) +#define FLAG_EMPTY (1 << 10) + +#ifdef NDEBUG +#undef assert +#define assert(x) if (!(x)) { fprintf(stderr, "Assertion error!\n"); abort(); } +#endif + +#ifdef _WIN32 +#include "wgetopt.h" + +#define __attribute__(x) + +#define lrand48() rand() +#define strdup _strdup +#endif + +#endif diff --git a/td/generate/tl-parser/tl-tl.h b/td/generate/tl-parser/tl-tl.h new file mode 100644 index 000000000..8bc0a707b --- /dev/null +++ b/td/generate/tl-parser/tl-tl.h @@ -0,0 +1,55 @@ +/* + This file is part of VK/KittenPHP-DB-Engine. + + VK/KittenPHP-DB-Engine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + VK/KittenPHP-DB-Engine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VK/KittenPHP-DB-Engine. If not, see . + + This program is released under the GPL with the additional exemption + that compiling, linking, and/or using OpenSSL is allowed. + You are free to remove this exemption from derived works. + + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman +*/ + +#ifndef __TL_TL_H__ +#define __TL_TL_H__ + +// Current tl-tl schema is V2 +// See https://core.telegram.org/mtproto/TL-tl + +#define TLS_SCHEMA_V2 0x3a2f9be2 +#define TLS_TYPE 0x12eb4386 +#define TLS_COMBINATOR 0x5c0a1ed5 +#define TLS_COMBINATOR_LEFT_BUILTIN 0xcd211f63 +#define TLS_COMBINATOR_LEFT 0x4c12c6d9 +#define TLS_COMBINATOR_RIGHT_V2 0x2c064372 +#define TLS_ARG_V2 0x29dfe61b + +#define TLS_EXPR_TYPE 0xecc9da78 +#define TLS_EXPR_NAT 0xdcb49bd8 + +#define TLS_NAT_CONST 0xdcb49bd8 +#define TLS_NAT_VAR 0x4e8a14f0 +#define TLS_TYPE_VAR 0x0142ceae +#define TLS_ARRAY 0xd9fb20de +#define TLS_TYPE_EXPR 0xc1863d08 + +/* Deprecated (old versions), read-only */ +#define TLS_TREE_NAT_CONST 0xc09f07d7 +#define TLS_TREE_NAT_VAR 0x90ea6f58 +#define TLS_TREE_TYPE_VAR 0x1caa237a +#define TLS_TREE_ARRAY 0x80479360 +#define TLS_TREE_TYPE 0x10f32190 + +#endif diff --git a/td/generate/tl-parser/tlc.c b/td/generate/tl-parser/tlc.c new file mode 100644 index 000000000..faddf9400 --- /dev/null +++ b/td/generate/tl-parser/tlc.c @@ -0,0 +1,165 @@ +/* + This file is part of tl-parser + + tl-parser is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + tl-parser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this tl-parser. If not, see . + + Copyright Vitaly Valtman 2014 + + It is derivative work of VK/KittenPHP-DB-Engine (https://github.com/vk-com/kphp-kdb/) + Copyright 2012-2013 Vkontakte Ltd + 2012-2013 Vitaliy Valtman + +*/ + +#include +#include +#include + +#include "tl-parser.h" + +#ifndef _WIN32 +#include +#endif + +#include +#include +#include + +#include + +#ifdef HAVE_EXECINFO_H +#include +#endif +#include + +int verbosity; +int output_expressions; +void usage (void) { + printf ("usage: tl-parser [-v] [-h] \n" + "\tTL compiler\n" + "\t-v\toutput statistical and debug information into stderr\n" + "\t-E\twhenever is possible output to stdout expressions\n" + "\t-e \texport serialized schema to file\n" + ); + exit (2); +} + +int vkext_write (const char *filename) { + FILE *f = fopen(filename, "wb"); + assert (f != NULL); + write_types (f); + fclose (f); + return 0; +} + +void logprintf (const char *format, ...) __attribute__ ((format (printf, 1, 2))); +void logprintf (const char *format __attribute__ ((unused)), ...) { + va_list ap; + va_start (ap, format); + vfprintf (stderr, format, ap); + va_end (ap); +} + +void hexdump (int *in_ptr, int *in_end) { + int *ptr = in_ptr; + while (ptr < in_end) { printf (" %08x", *(ptr ++)); } + printf ("\n"); +} + +#ifdef HAVE_EXECINFO_H +void print_backtrace (void) { + void *buffer[255]; + const int calls = backtrace (buffer, sizeof (buffer) / sizeof (void *)); + backtrace_symbols_fd (buffer, calls, 1); +} +#else +void print_backtrace (void) { + if (fwrite ("No libexec. Backtrace disabled\n", 32, 1, stderr) < 0) { + // Sad thing + } +} +#endif + +void sig_segv_handler (int signum __attribute__ ((unused))) { + if (fwrite ("SIGSEGV received\n", 18, 1, stderr) < 0) { + // Sad thing + } + print_backtrace (); + exit (EXIT_FAILURE); +} + +void sig_abrt_handler (int signum __attribute__ ((unused))) { + if (fwrite ("SIGABRT received\n", 18, 1, stderr) < 0) { + // Sad thing + } + print_backtrace (); + exit (EXIT_FAILURE); +} + +int main (int argc, char **argv) { + signal (SIGSEGV, sig_segv_handler); + signal (SIGABRT, sig_abrt_handler); + int i; + char *vkext_file = 0; + while ((i = getopt (argc, argv, "Ehve:w:")) != -1) { + switch (i) { + case 'E': + output_expressions++; + break; + case 'h': + usage (); + return 2; + case 'e': + vkext_file = optarg; + break; + case 'v': + verbosity++; + break; + } + } + + if (argc != optind + 1) { + usage (); + } + + + struct parse *P = tl_init_parse_file (argv[optind]); + if (!P) { + return 1; + } + struct tree *T; + if (!(T = tl_parse_lex (P))) { + fprintf (stderr, "Error in parse:\n"); + tl_print_parse_error (); + return 1; + } else { + if (verbosity) { + fprintf (stderr, "Parse ok\n"); + } + if (!tl_parse (T)) { + if (verbosity) { + fprintf (stderr, "Fail\n"); + } + return 1; + } else { + if (verbosity) { + fprintf (stderr, "Ok\n"); + } + } + } + if (vkext_file) { + vkext_write (vkext_file); + } + return 0; +} diff --git a/td/generate/tl-parser/wgetopt.c b/td/generate/tl-parser/wgetopt.c new file mode 100644 index 000000000..4557b9211 --- /dev/null +++ b/td/generate/tl-parser/wgetopt.c @@ -0,0 +1,1274 @@ +/* Getopt for GNU. +NOTE: getopt is now part of the C library, so if you don't know what +"Keep this file name-space clean" means, talk to drepper@gnu.org +before changing it! +Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 +Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the GNU C Library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . +Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems +reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not +actually compiling the library itself. This code is part of the GNU C +Library, but also included in many other GNU distributions. Compiling +and linking in this code is a waste when using the GNU C library +(especially if it is a shared library). Rather than having every GNU +program understand `configure --with-gnu-libc' and omit the object files, +it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include +to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them +contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include +# ifndef _ +# define _(msgid) gettext (msgid) +# endif +# else +# define _(msgid) (msgid) +# endif +# if defined _LIBC && defined USE_IN_LIBIO +# include +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' +but it behaves differently for the user, since it allows the user +to intersperse the options with the other arguments. + +As `getopt' works, it permutes the elements of ARGV so that, +when it is done, all the options precede everything else. Thus +all application programs are extended to handle flexible argument order. + +Setting the environment variable POSIXLY_CORRECT disables permutation. +Then the behavior is completely standard. + +GNU application programs can use a third alternative mode in which +they can distinguish the relative order of options and other arguments. */ + +#include "wgetopt.h" + +/* For communication from `getopt' to the caller. +When `getopt' finds an option that takes an argument, +the argument value is returned here. +Also, when `ordering' is RETURN_IN_ORDER, +each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. +This is used for communication to and from the caller +and for communication between successive calls to `getopt'. + +On entry to `getopt', zero means this is the first call; initialize. + +When `getopt' returns -1, this is the index of the first of the +non-option elements that the caller should itself scan. + +Otherwise, `optind' communicates from one call to the next +how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which +causes problems with re-calling getopt as programs generally don't +know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element +in which the last option character we returned was found. +This allows us to pick up the scan where we left off. + +If this is zero, or a null string, it means resume the scan +by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message +for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. +This must be initialized on some systems to avoid linking in the +system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + +If the caller did not specify anything, +the default is REQUIRE_ORDER if the environment variable +POSIXLY_CORRECT is defined, PERMUTE otherwise. + +REQUIRE_ORDER means don't recognize them as options; +stop option processing when the first non-option is seen. +This is what Unix does. +This mode of operation is selected by either setting the environment +variable POSIXLY_CORRECT, or using `+' as the first character +of the list of option characters. + +PERMUTE is the default. We permute the contents of ARGV as we scan, +so that eventually all the non-options are at the end. This allows options +to be given in any order, even with programs that were not written to +expect this. + +RETURN_IN_ORDER is an option available to programs that were written +to expect options and other ARGV-elements in any order and that care about +the ordering of the two. We describe each non-option ARGV-element +as if it were the argument of an option with character code 1. +Using `-' as the first character of the list of option characters +selects this mode of operation. + +The special argument `--' forces an end of option-scanning regardless +of the value of `ordering'. In the case of RETURN_IN_ORDER, only +`--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries +because there are many ways it can cause trouble. +On some systems, it contains special magic macros that don't work +in GCC. */ +# include +# define my_index strchr +#else + +#define HAVE_STRING_H 1 +# if HAVE_STRING_H +# include +# else +# include +# endif + +/* Avoid depending on library functions or files +whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv(); +#endif + +static char * +my_index(str, chr) +const char *str; +int chr; +{ + while (*str) + { + if (*str == chr) + return (char *)str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. +If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. +That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, +and has done so at least since version 2.4.5. -- rms. */ +extern int strlen(const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have +been skipped. `first_nonopt' is the index in ARGV of the first of them; +`last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Stored original parameters. +XXX This is no good solution. We should rather copy the args so +that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags +indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ +if (nonoption_flags_len > 0) \ +{ \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ +} +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. +One subsequence is elements [first_nonopt,last_nonopt) +which contains all the non-options that have been skipped so far. +The other is elements [last_nonopt,optind), which contains all +the options processed since those non-options were skipped. + +`first_nonopt' and `last_nonopt' are relocated so that they describe +the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange(char **); +#endif + +static void +exchange(argv) +char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc(top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset(__mempcpy(new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS(bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS(bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize(int, char *const *, const char *); +#endif +static const char * +_getopt_initialize(argc, argv, optstring) +int argc; +char *const *argv; +const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL + && argc == __libc_argc && argv == __libc_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen(orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *)malloc(nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset(__mempcpy(__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters +given in OPTSTRING. + +If an element of ARGV starts with '-', and is not exactly "-" or "--", +then it is an option element. The characters of this element +(aside from the initial '-') are option characters. If `getopt' +is called repeatedly, it returns successively each of the option characters +from each of the option elements. + +If `getopt' finds another option character, it returns that character, +updating `optind' and `nextchar' so that the next call to `getopt' can +resume the scan with the following option character or ARGV-element. + +If there are no more option characters, `getopt' returns -1. +Then `optind' is the index in ARGV of the first ARGV-element +that is not an option. (The ARGV-elements have been permuted +so that those that are not options now come last.) + +OPTSTRING is a string containing the legitimate option characters. +If an option character is seen that is not listed in OPTSTRING, +return '?' after printing an error message. If you set `opterr' to +zero, the error message is suppressed but we still return '?'. + +If a char in OPTSTRING is followed by a colon, that means it wants an arg, +so the following text in the same ARGV-element, or the text of the following +ARGV-element, is returned in `optarg'. Two colons mean an option that +wants an optional arg; if there is text in the current ARGV-element, +it is returned in `optarg', otherwise `optarg' is set to zero. + +If OPTSTRING starts with `-' or `+', it requests different methods of +handling the non-option ARGV-elements. +See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + +Long-named options begin with `--' instead of `-'. +Their names may be abbreviated as long as the abbreviation is unique +or is an exact match for some defined option. If they have an +argument, it follows the option name in the same ARGV-element, separated +from the option name by a `=', or else the in next ARGV-element. +When `getopt' finds a long-named option, it returns 0 if that option's +`flag' field is nonzero, the value of the option's `val' field +if the `flag' field is zero. + +The elements of ARGV aren't really const, because we permute them. +But we pretend they're const in the prototype to be compatible +with other systems. + +LONGOPTS is a vector of `struct option' terminated by an +element containing a name which is zero. + +LONGIND returns the index in LONGOPT of the long-named option found. +It is only valid when a long-named option has been found by the most +recent call. + +If LONG_ONLY is nonzero, '-' as well as '--' can introduce +long-named options. */ + +int +_getopt_internal(argc, argv, optstring, longopts, longind, long_only) +int argc; +char *const *argv; +const char *optstring; +const struct option *longopts; +int *longind; +int long_only; +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize(argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char **)argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp(argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char **)argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int)(nameend - nextchar) + == (unsigned int)strlen(p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen(nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (argv[optind - 1][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("\ + %s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#else + fprintf(stderr, _("\ + %s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("\ + %s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], + pfound->name); +#else + fprintf(stderr, _("\ + %s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#endif + } + + nextchar += strlen(nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen(nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index(optstring, *nextchar) == NULL) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (argv[optind][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#else + fprintf(stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#else + fprintf(stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#endif + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index(optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (posixly_correct) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: illegal option -- %c\n"), + argv[0], c); +#else + fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); +#endif + } + else + { +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: invalid option -- %c\n"), + argv[0], c); +#else + fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#endif + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("%s: option requires an argument -- %c\n"), + argv[0], c); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int)(nameend - nextchar) == strlen(p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen(nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("\ + %s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("\ + %s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + + nextchar += strlen(nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("\ + %s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen(nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt(argc, argv, optstring) +int argc; +char *const *argv; +const char *optstring; +{ + return _getopt_internal(argc, argv, optstring, + (const struct option *) 0, + (int *)0, + 0); +} + + + + +int +getopt_long(int argc, char *const *argv, const char *options, +const struct option *long_options, int *opt_index) +{ + return _getopt_internal(argc, argv, options, long_options, opt_index, 0, 0); +} + +int +getopt_long_only(int argc, char *const *argv, const char *options, +const struct option *long_options, int *opt_index) +{ + return _getopt_internal(argc, argv, options, long_options, opt_index, 1, 0); +} + + + + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing +the above definition of `getopt'. */ + +int +main(argc, argv) +int argc; +char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt(argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf("option %c\n", c); + break; + + case 'a': + printf("option a\n"); + break; + + case 'b': + printf("option b\n"); + break; + + case 'c': + printf("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } + + exit(0); +} + +#endif /* TEST */ diff --git a/td/generate/tl-parser/wgetopt.h b/td/generate/tl-parser/wgetopt.h new file mode 100644 index 000000000..6e2fa2718 --- /dev/null +++ b/td/generate/tl-parser/wgetopt.h @@ -0,0 +1,193 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2009,2010 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifndef __THROW +# ifndef __GNUC_PREREQ +# define __GNUC_PREREQ(maj, min) (0) +# endif +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __THROW; + +# if defined __need_getopt && defined __USE_POSIX2 \ + && !defined __USE_POSIX_IMPLICITLY && !defined __USE_GNU +/* The GNU getopt has more functionality than the standard version. The + additional functionality can be disable at runtime. This redirection + helps to also do this at runtime. */ +# ifdef __REDIRECT + extern int __REDIRECT_NTH (getopt, (int ___argc, char *const *___argv, + const char *__shortopts), + __posix_getopt); +# else +extern int __posix_getopt (int ___argc, char *const *___argv, + const char *__shortopts) __THROW; +# define getopt __posix_getopt +# endif +# endif +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ + +#ifndef __need_getopt +extern int getopt_long (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; +extern int getopt_long_only (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; + +#endif + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ From a87af24fb907b4eff269fe87c7d8c8b63a91bfe1 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Mar 2021 01:41:09 +0300 Subject: [PATCH 228/232] Use tl-parser to generate tlo files. --- CMakeLists.txt | 1 - td/generate/CMakeLists.txt | 38 ++++++++++++++++++---------- td/generate/generate_c.cpp | 2 +- td/generate/generate_common.cpp | 2 +- td/generate/generate_json.cpp | 2 +- td/generate/scheme/mtproto_api.tlo | Bin 8636 -> 0 bytes td/generate/scheme/secret_api.tlo | Bin 14668 -> 0 bytes td/generate/scheme/td_api.tlo | Bin 198140 -> 0 bytes td/generate/scheme/telegram_api.tlo | Bin 233388 -> 0 bytes td/generate/scheme/update-tlo.sh | 6 ----- 10 files changed, 27 insertions(+), 24 deletions(-) delete mode 100644 td/generate/scheme/mtproto_api.tlo delete mode 100644 td/generate/scheme/secret_api.tlo delete mode 100644 td/generate/scheme/td_api.tlo delete mode 100644 td/generate/scheme/telegram_api.tlo delete mode 100755 td/generate/scheme/update-tlo.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 801f39b71..b186375f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,7 +197,6 @@ endif() get_directory_property(HAS_PARENT PARENT_DIRECTORY) if (HAS_PARENT) - set(TL_TD_API_TLO ${TL_TD_API_TLO} PARENT_SCOPE) # was used in standalone binding generators set(TL_TD_JSON_AUTO ${TL_TD_JSON_AUTO_SOURCE} PARENT_SCOPE) # used in tdbot set(TD_TEST_SOURCE ${TD_TEST_SOURCE} PARENT_SCOPE) # used to build tests endif() diff --git a/td/generate/CMakeLists.txt b/td/generate/CMakeLists.txt index adbaa2660..f7a93d8f6 100644 --- a/td/generate/CMakeLists.txt +++ b/td/generate/CMakeLists.txt @@ -6,6 +6,7 @@ endif() file(MAKE_DIRECTORY auto/td/telegram) file(MAKE_DIRECTORY auto/td/mtproto) +file(MAKE_DIRECTORY auto/tlo) set(TL_TD_AUTO_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/auto PARENT_SCOPE) @@ -37,9 +38,6 @@ set(TL_TD_JSON_AUTO_SOURCE PARENT_SCOPE ) -set(TL_TD_API_TLO ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tlo) -set(TL_TD_API_TLO ${TL_TD_API_TLO} PARENT_SCOPE) - set(TL_C_AUTO_SOURCE ${TD_AUTO_INCLUDE_DIR}/telegram/td_tdc_api.cpp ${TD_AUTO_INCLUDE_DIR}/telegram/td_tdc_api.h @@ -106,13 +104,25 @@ if (NOT CMAKE_CROSSCOMPILING) set(GENERATE_COMMON_CMD generate_common) endif() + add_subdirectory(tl-parser) + + add_custom_target(tl_generate_tlo + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND tl-parser -e auto/tlo/mtproto_api.tlo scheme/mtproto_api.tl + COMMAND tl-parser -e auto/tlo/secret_api.tlo scheme/secret_api.tl + COMMAND tl-parser -e auto/tlo/td_api.tlo scheme/td_api.tl + COMMAND tl-parser -e auto/tlo/telegram_api.tlo scheme/telegram_api.tl + COMMENT "Generate tlo files" + DEPENDS tl-parser scheme/mtproto_api.tl scheme/secret_api.tl scheme/td_api.tl scheme/telegram_api.tl + ) + add_executable(generate_common ${TL_GENERATE_COMMON_SOURCE}) target_link_libraries(generate_common PRIVATE tdtl) add_custom_target(tl_generate_common WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${GENERATE_COMMON_CMD} - COMMENT "Generate common tl source files" - DEPENDS generate_common scheme/mtproto_api.tlo scheme/telegram_api.tlo scheme/secret_api.tlo ${TL_TD_API_TLO} DoxygenTlDocumentationGenerator.php + COMMENT "Generate common TL source files" + DEPENDS generate_common tl_generate_tlo auto/tlo/mtproto_api.tlo auto/tlo/secret_api.tlo auto/tlo/td_api.tlo auto/tlo/telegram_api.tlo scheme/td_api.tl DoxygenTlDocumentationGenerator.php ) if (TD_ENABLE_JNI) target_compile_definitions(generate_common PRIVATE TD_ENABLE_JNI=1) @@ -126,8 +136,8 @@ if (NOT CMAKE_CROSSCOMPILING) add_custom_target(tl_generate_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND generate_c - COMMENT "Generate C tl source files" - DEPENDS generate_c ${TL_TD_API_TLO} + COMMENT "Generate C TL source files" + DEPENDS generate_c tl_generate_tlo auto/tlo/td_api.tlo scheme/td_api.tl ) add_executable(td_generate_java_api ${TL_GENERATE_JAVA_SOURCE}) @@ -138,30 +148,30 @@ if (NOT CMAKE_CROSSCOMPILING) add_custom_target(tl_generate_json WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND generate_json - COMMENT "Generate JSON tl source files" - DEPENDS generate_json ${TL_TD_API_TLO} + COMMENT "Generate JSON TL source files" + DEPENDS generate_json tl_generate_tlo auto/tlo/td_api.tlo scheme/td_api.tl ) if (TD_ENABLE_JNI) install(TARGETS td_generate_java_api RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") install(FILES JavadocTlDocumentationGenerator.php TlDocumentationGenerator.php DESTINATION "${CMAKE_INSTALL_BINDIR}/td/generate") - install(FILES scheme/td_api.tlo scheme/td_api.tl DESTINATION "${CMAKE_INSTALL_BINDIR}/td/generate/scheme") + install(FILES auto/tlo/td_api.tlo scheme/td_api.tl DESTINATION "${CMAKE_INSTALL_BINDIR}/td/generate/scheme") endif() if (TD_ENABLE_DOTNET) if (PHP_EXECUTABLE) - set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TL_TD_API_TLO} && ${PHP_EXECUTABLE} DotnetTlDocumentationGenerator.php scheme/td_api.tl auto/td/telegram/TdDotNetApi.h) + set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${CMAKE_CURRENT_SOURCE_DIR}/auto/tlo/td_api.tlo && ${PHP_EXECUTABLE} DotnetTlDocumentationGenerator.php scheme/td_api.tl auto/td/telegram/TdDotNetApi.h) else() - set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TL_TD_API_TLO}) + set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${CMAKE_CURRENT_SOURCE_DIR}/auto/tlo/td_api.tlo) endif() add_executable(td_generate_dotnet_api generate_dotnet.cpp tl_writer_dotnet.h) target_link_libraries(td_generate_dotnet_api PRIVATE tdtl) add_custom_target(generate_dotnet_api WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${GENERATE_DOTNET_API_CMD} ${TL_TD_API_TLO} + COMMAND ${GENERATE_DOTNET_API_CMD} COMMENT "Generate .NET API files" - DEPENDS td_generate_dotnet_api ${TL_TD_API_TLO} DotnetTlDocumentationGenerator.php + DEPENDS td_generate_dotnet_api tl_generate_tlo auto/tlo/td_api.tlo scheme/td_api.tl DotnetTlDocumentationGenerator.php ) endif() diff --git a/td/generate/generate_c.cpp b/td/generate/generate_c.cpp index ac38a2df7..01831e72d 100644 --- a/td/generate/generate_c.cpp +++ b/td/generate/generate_c.cpp @@ -10,7 +10,7 @@ #include "td/tl/tl_generate.h" int main() { - td::tl::tl_config config_td = td::tl::read_tl_config_from_file("scheme/td_api.tlo"); + td::tl::tl_config config_td = td::tl::read_tl_config_from_file("auto/tlo/td_api.tlo"); td::tl::write_tl_to_file(config_td, "auto/td/telegram/td_tdc_api.h", td::TlWriterCCommon("TdApi", 1, "#include \"td/telegram/td_api.h\"\n")); td::tl::write_tl_to_file(config_td, "auto/td/telegram/td_tdc_api_inner.h", diff --git a/td/generate/generate_common.cpp b/td/generate/generate_common.cpp index 11f507138..8fcdc8e2c 100644 --- a/td/generate/generate_common.cpp +++ b/td/generate/generate_common.cpp @@ -22,7 +22,7 @@ static void generate_cpp(const std::string &directory, const std::string &tl_nam const std::string &bytes_type, const std::vector &ext_cpp_includes, const std::vector &ext_h_includes) { std::string path = directory + "/" + tl_name; - td::tl::tl_config config = td::tl::read_tl_config_from_file("scheme/" + tl_name + ".tlo"); + td::tl::tl_config config = td::tl::read_tl_config_from_file("auto/tlo/" + tl_name + ".tlo"); td::tl::write_tl_to_file(config, path + ".cpp", WriterCpp(tl_name, string_type, bytes_type, ext_cpp_includes)); td::tl::write_tl_to_file(config, path + ".h", WriterH(tl_name, string_type, bytes_type, ext_h_includes)); td::tl::write_tl_to_file(config, path + ".hpp", WriterHpp(tl_name, string_type, bytes_type)); diff --git a/td/generate/generate_json.cpp b/td/generate/generate_json.cpp index 993fa2933..b1e47add3 100644 --- a/td/generate/generate_json.cpp +++ b/td/generate/generate_json.cpp @@ -10,6 +10,6 @@ #include "td/tl/tl_generate.h" int main() { - td::gen_json_converter(td::tl::read_tl_config_from_file("scheme/td_api.tlo"), "td/telegram/td_api_json", + td::gen_json_converter(td::tl::read_tl_config_from_file("auto/tlo/td_api.tlo"), "td/telegram/td_api_json", td::tl::TL_writer::Server); } diff --git a/td/generate/scheme/mtproto_api.tlo b/td/generate/scheme/mtproto_api.tlo deleted file mode 100644 index 2e00490e578efab41e4c40d3937ca8e8c204bdf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8636 zcmds6eQX?85kJRg`+WA<7k>n$g{Da>J}V%hp{NK%;%jK2L>D_Mttf2QyU+IGyS?@9 zUK}SX${#2yRRt1K|3FbwArPTL%3n>ZRuEAHmC{h4Dj!XV8c~%G(UcZdC6x*)^LuaS z?s;$be6E}zaZ=*l_vX#~X5P%3d9yDb{m9Lk=(mOb9+`M~$;9dm;c55SASgk z+{jMT{z5RjrxrRhj%9{U&CRSM(&BP*$xmGRUcI=}aqS)TaPISBX}9YNpXj<`w0H&o zeUDDej7?M=;f6-(Rzs2-B{MYJtT}eas~gsrL=gJ5rAW-A2r`*&T9brn`}pgJnSM$H z0pXYQX*%qCvbd$URI5){L@Ms{XLtAKch$qXFQ&{&DCy!lLca%cnZ4Ms#5Bt0S%*KO*lT$m& z?r-h7?~hkciO{ebXN)K17^c)BO^55<_n+*Y3Vnw>8+MD^4mN(*n-_1$?pyMNZhY+- zG~+)%`R?A^g%y$|n2*luLqz|(Yw~xkfAiJjW7DQ>RD)T=)lb??250cur$%lA)~o+= z;RAW5b8IOs_o*XKA`Y1^XbMadhy|B?89&hBT+r$A0-I!5HCt&Br|~CxZ@?HZC@u=` zq0*0D8HS8N?O#BLAmhvu;g!Y-dBd())bX&+x^2?*LEajjro3gCl(>v}v7{mq}W196q zOtKu-1K4yue*34p|21l>+MsFL+B|9o=m1(?i!pueom(EtkGHHPEoi?xSwDKT`1jWJ zGnDf>TmSD{u6=QY@?sjX7;G2ZK2e`mEnn$j{9^>DEbEhw2 z4RFPM9yqWDKg2F-$AMX@9Su(wk9gPpWk3z9qJq|dZ&EB_on#vEMXgz|>(%N~v*5%S zc!Q2@hcSJftUVn!#9w{?@vhU!8kFczgW_4o%ychY`_|d#k3Mo9XAk>7&&6%Zc)*Kk zU>{k>k{r~rcy1>rw+cDnrOT1Ea7EQ+c-D2tyH5Mmr>pN1^rpbU`+ReK4oWV0O*fcgsyrl>NaJYfRAny+(~l8ixRLk zPZM}w3pwDU%fY>-pyX64k?&v(vepqH_H?y6$+J?qp%eu>B%KqGqhrC{X;@-`6A6h1 zV>*^j&dO{DvcXrEjk_53HZYWI>@CLD!sg(k%fel5RMnWWiC|d1Abo9&LmvD3;Edx3 z;j)lSjLH7G>nzmCUbd0~d+R=i{TR^dYf!~$8h%I^bgT~;Z#=x-GZs`8E02D|wlW#q zGl8{_h{1oIhZ}ZlPQYB3C#R%L-4EoPT9!DsR`p>&Y^@cM&>d$k=R=7fN=5kfyA9g~ z`YJ8dnRV}l|J=SIW*1pn6Cbt>a=`zyZJ=A@p5VCBJg{vi_LIE_Pvtl~>oLAkuIlsX zHl1>J;r#+neZSDh81e(t@MI$c7qbk;bh%whg+yQM((Y=y53@Y*(&fos2;~X%LhuYZ zoQv1WuiM4kHBr1b)iq|wYQp!Z6%iGS*sD(S6Liq^bD#U*S4Mo#irODW-JPcj)y<&4 z16@JOw!xUb_Q;7~srw^(Wx&GrSu0;0YMimgoEgkD?5W??*WfkDUk_@?u_1|y8JO+=Gd2NEHvzAa)5Kc2@k?)0yX z-M0tNpv6CZ{=~0#Vo3hjKjQgLr^yo~{QB;_6VDd8zr}4_n(L%@_ukEW|2oWTqyE}j z_1&^R+5C8MmcBux^pjZsZns{A(rIxV@B2Ko{X5_8pQCSq#-jWf$h{fP5&c#30Nu+g zCO-Zo@P*CuSZbIvq5MkQDjPI8@KGWX`S2da`#={LPaVvn=A%_yE3&Tdq!zsY(9YM# zWeq8nGHfTXYV^6xBEMw*jk|A~S`#`In~QZ5rP@ATm!5iQS6HT diff --git a/td/generate/scheme/secret_api.tlo b/td/generate/scheme/secret_api.tlo deleted file mode 100644 index d265dbfc9a2eef81b422785544235dbdb4818e06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14668 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 TUm~4MW?Ry_{oc=T?`8c5CnWoo diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo deleted file mode 100644 index e594e6fd1ec13960deb8190e51ee02130f2aecda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198140 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 diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo deleted file mode 100644 index 4c7daf5e995851a3309bb7fea5168b1d2f45eb4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233388 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 c=Rxq^6-cT8#W^ Date: Thu, 4 Mar 2021 14:06:32 +0300 Subject: [PATCH 229/232] Silence tl-parser build warning. --- td/generate/tl-parser/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td/generate/tl-parser/CMakeLists.txt b/td/generate/tl-parser/CMakeLists.txt index 05c3ca016..251eb2b39 100644 --- a/td/generate/tl-parser/CMakeLists.txt +++ b/td/generate/tl-parser/CMakeLists.txt @@ -8,7 +8,7 @@ if (WIN32) add_definitions("-D_CRT_SECURE_NO_WARNINGS") list(APPEND SOURCES wgetopt.c wgetopt.h) if (MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8 /wd4101 /wd4244") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8 /wd4101 /wd4244 /wd4267") endif() endif() From 1c3290bada844f4469afbd342bdab7f31618f984 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 4 Mar 2021 15:00:52 +0300 Subject: [PATCH 230/232] Fix warning. --- td/telegram/ContactsManager.cpp | 8 ++++---- td/telegram/ContactsManager.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 30705821a..566e03c51 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -11361,10 +11361,10 @@ const DialogParticipant *ContactsManager::get_chat_participant(ChatId chat_id, U if (chat_full == nullptr) { return nullptr; } - return get_chat_participant(chat_full, user_id); + return get_chat_full_participant(chat_full, user_id); } -const DialogParticipant *ContactsManager::get_chat_participant(const ChatFull *chat_full, UserId user_id) { +const DialogParticipant *ContactsManager::get_chat_full_participant(const ChatFull *chat_full, UserId user_id) { for (const auto &dialog_participant : chat_full->participants) { if (dialog_participant.user_id == user_id) { return &dialog_participant; @@ -14594,8 +14594,8 @@ void ContactsManager::do_search_chat_participants(ChatId chat_id, const string & int32 total_count; std::tie(total_count, user_ids) = search_among_users(user_ids, query, limit); - promise.set_value(DialogParticipants{total_count, transform(user_ids, [this, chat_full](UserId user_id) { - return *get_chat_participant(chat_full, user_id); + promise.set_value(DialogParticipants{total_count, transform(user_ids, [chat_full](UserId user_id) { + return *ContactsManager::get_chat_full_participant(chat_full, user_id); })}); } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 8a34325e4..749eaba3e 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -1411,7 +1411,7 @@ class ContactsManager : public Actor { const DialogParticipant *get_chat_participant(ChatId chat_id, UserId user_id) const; - static const DialogParticipant *get_chat_participant(const ChatFull *chat_full, UserId user_id); + static const DialogParticipant *get_chat_full_participant(const ChatFull *chat_full, UserId user_id); std::pair> search_among_users(const vector &user_ids, const string &query, int32 limit) const; From 2b090918c21a4c0d2b5ce3daf78237bd3dea9ec8 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Mar 2021 02:47:27 +0300 Subject: [PATCH 231/232] Fix handling of errors in CallActor::send_call_signaling_data. --- td/telegram/CallActor.cpp | 76 +++++++++++++++++++++------------------ td/telegram/CallActor.h | 16 ++++----- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/td/telegram/CallActor.cpp b/td/telegram/CallActor.cpp index ceefd7716..7181834c0 100644 --- a/td/telegram/CallActor.cpp +++ b/td/telegram/CallActor.cpp @@ -189,8 +189,8 @@ void CallActor::send_call_signaling_data(string &&data, Promise<> promise) { auto query = G()->net_query_creator().create( telegram_api::phone_sendSignalingData(get_input_phone_call("send_call_signaling_data"), BufferSlice(data))); send_with_promise(std::move(query), - PromiseCreator::lambda([promise = std::move(promise)](NetQueryPtr net_query) mutable { - auto res = fetch_result(std::move(net_query)); + PromiseCreator::lambda([promise = std::move(promise)](Result r_net_query) mutable { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { promise.set_error(res.move_as_error()); } else { @@ -298,14 +298,15 @@ void CallActor::rate_call(int32 rating, string comment, vectornet_query_creator().create(tl_query); - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_set_rating_query_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_set_rating_query_result, std::move(r_net_query)); })); loop(); } -void CallActor::on_set_rating_query_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_set_rating_query_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } @@ -321,14 +322,15 @@ void CallActor::send_call_debug_information(string data, Promise<> promise) { auto tl_query = telegram_api::phone_saveCallDebug(get_input_phone_call("send_call_debug_information"), make_tl_object(std::move(data))); auto query = G()->net_query_creator().create(tl_query); - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_set_debug_query_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_set_debug_query_result, std::move(r_net_query)); })); loop(); } -void CallActor::on_set_debug_query_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_set_debug_query_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } @@ -603,13 +605,14 @@ void CallActor::do_load_dh_config(Promise> promise) { void CallActor::send_received_query() { auto tl_query = telegram_api::phone_receivedCall(get_input_phone_call("send_received_query")); auto query = G()->net_query_creator().create(tl_query); - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_received_query_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_received_query_result, std::move(r_net_query)); })); } -void CallActor::on_received_query_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_received_query_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } @@ -637,13 +640,14 @@ void CallActor::try_send_request_query() { set_timeout_in(timeout); query->total_timeout_limit_ = max(timeout, 10.0); request_query_ref_ = query.get_weak(); - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_request_query_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_request_query_result, std::move(r_net_query)); })); } -void CallActor::on_request_query_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_request_query_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } @@ -666,13 +670,14 @@ void CallActor::try_send_accept_query() { call_state_.protocol.get_input_phone_call_protocol()); auto query = G()->net_query_creator().create(tl_query); state_ = State::WaitAcceptResult; - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_accept_query_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_accept_query_result, std::move(r_net_query)); })); } -void CallActor::on_accept_query_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_accept_query_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } @@ -690,13 +695,14 @@ void CallActor::try_send_confirm_query() { call_state_.protocol.get_input_phone_call_protocol()); auto query = G()->net_query_creator().create(tl_query); state_ = State::WaitConfirmResult; - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_confirm_query_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_confirm_query_result, std::move(r_net_query)); })); } -void CallActor::on_confirm_query_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_confirm_query_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } @@ -720,13 +726,14 @@ void CallActor::try_send_discard_query() { get_input_phone_call_discard_reason(call_state_.discard_reason), connection_id_); auto query = G()->net_query_creator().create(tl_query); state_ = State::WaitDiscardResult; - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_discard_query_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_discard_query_result, std::move(r_net_query)); })); } -void CallActor::on_discard_query_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_discard_query_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } @@ -768,13 +775,14 @@ void CallActor::flush_call_state() { void CallActor::start_up() { auto tl_query = telegram_api::phone_getCallConfig(); auto query = G()->net_query_creator().create(tl_query); - send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { - send_closure(actor_id, &CallActor::on_get_call_config_result, std::move(net_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { + send_closure(actor_id, &CallActor::on_get_call_config_result, std::move(r_net_query)); })); } -void CallActor::on_get_call_config_result(NetQueryPtr net_query) { - auto res = fetch_result(std::move(net_query)); +void CallActor::on_get_call_config_result(Result r_net_query) { + auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } diff --git a/td/telegram/CallActor.h b/td/telegram/CallActor.h index 7d282661f..d4ac600ff 100644 --- a/td/telegram/CallActor.h +++ b/td/telegram/CallActor.h @@ -164,28 +164,28 @@ class CallActor : public NetQueryCallback { Status do_update_call(telegram_api::phoneCallDiscarded &call); void send_received_query(); - void on_received_query_result(NetQueryPtr net_query); + void on_received_query_result(Result r_net_query); void try_send_request_query(); - void on_request_query_result(NetQueryPtr net_query); + void on_request_query_result(Result r_net_query); void try_send_accept_query(); - void on_accept_query_result(NetQueryPtr net_query); + void on_accept_query_result(Result r_net_query); void try_send_confirm_query(); - void on_confirm_query_result(NetQueryPtr net_query); + void on_confirm_query_result(Result r_net_query); void try_send_discard_query(); - void on_discard_query_result(NetQueryPtr net_query); + void on_discard_query_result(Result r_net_query); void on_begin_exchanging_key(); void on_call_discarded(CallDiscardReason reason, bool need_rating, bool need_debug, bool is_video); - void on_set_rating_query_result(NetQueryPtr net_query); - void on_set_debug_query_result(NetQueryPtr net_query); + void on_set_rating_query_result(Result r_net_query); + void on_set_debug_query_result(Result r_net_query); - void on_get_call_config_result(NetQueryPtr net_query); + void on_get_call_config_result(Result r_net_query); void flush_call_state(); From 29ded98a46cf98e77de1d725ed33e28f76c0a23d Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 5 Mar 2021 23:15:52 +0300 Subject: [PATCH 232/232] Use full paths for file dependencies. --- td/generate/CMakeLists.txt | 20 +++++++++++--------- tdutils/generate/CMakeLists.txt | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/td/generate/CMakeLists.txt b/td/generate/CMakeLists.txt index f7a93d8f6..8bcd1d724 100644 --- a/td/generate/CMakeLists.txt +++ b/td/generate/CMakeLists.txt @@ -106,14 +106,16 @@ if (NOT CMAKE_CROSSCOMPILING) add_subdirectory(tl-parser) + set(TLO_AUTO_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/auto/tlo) + add_custom_target(tl_generate_tlo WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND tl-parser -e auto/tlo/mtproto_api.tlo scheme/mtproto_api.tl COMMAND tl-parser -e auto/tlo/secret_api.tlo scheme/secret_api.tl COMMAND tl-parser -e auto/tlo/td_api.tlo scheme/td_api.tl COMMAND tl-parser -e auto/tlo/telegram_api.tlo scheme/telegram_api.tl - COMMENT "Generate tlo files" - DEPENDS tl-parser scheme/mtproto_api.tl scheme/secret_api.tl scheme/td_api.tl scheme/telegram_api.tl + COMMENT "Generate TLO files" + DEPENDS tl-parser ${CMAKE_CURRENT_SOURCE_DIR}/scheme/mtproto_api.tl ${CMAKE_CURRENT_SOURCE_DIR}/scheme/secret_api.tl ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tl ${CMAKE_CURRENT_SOURCE_DIR}/scheme/telegram_api.tl ) add_executable(generate_common ${TL_GENERATE_COMMON_SOURCE}) @@ -122,7 +124,7 @@ if (NOT CMAKE_CROSSCOMPILING) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${GENERATE_COMMON_CMD} COMMENT "Generate common TL source files" - DEPENDS generate_common tl_generate_tlo auto/tlo/mtproto_api.tlo auto/tlo/secret_api.tlo auto/tlo/td_api.tlo auto/tlo/telegram_api.tlo scheme/td_api.tl DoxygenTlDocumentationGenerator.php + DEPENDS generate_common tl_generate_tlo ${TLO_AUTO_INCLUDE_DIR}/mtproto_api.tlo ${TLO_AUTO_INCLUDE_DIR}/secret_api.tlo ${TLO_AUTO_INCLUDE_DIR}/td_api.tlo ${TLO_AUTO_INCLUDE_DIR}/telegram_api.tlo ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tl ${CMAKE_CURRENT_SOURCE_DIR}/DoxygenTlDocumentationGenerator.php ) if (TD_ENABLE_JNI) target_compile_definitions(generate_common PRIVATE TD_ENABLE_JNI=1) @@ -137,7 +139,7 @@ if (NOT CMAKE_CROSSCOMPILING) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND generate_c COMMENT "Generate C TL source files" - DEPENDS generate_c tl_generate_tlo auto/tlo/td_api.tlo scheme/td_api.tl + DEPENDS generate_c tl_generate_tlo ${TLO_AUTO_INCLUDE_DIR}/td_api.tlo ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tl ) add_executable(td_generate_java_api ${TL_GENERATE_JAVA_SOURCE}) @@ -149,20 +151,20 @@ if (NOT CMAKE_CROSSCOMPILING) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND generate_json COMMENT "Generate JSON TL source files" - DEPENDS generate_json tl_generate_tlo auto/tlo/td_api.tlo scheme/td_api.tl + DEPENDS generate_json tl_generate_tlo ${TLO_AUTO_INCLUDE_DIR}/td_api.tlo ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tl ) if (TD_ENABLE_JNI) install(TARGETS td_generate_java_api RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") install(FILES JavadocTlDocumentationGenerator.php TlDocumentationGenerator.php DESTINATION "${CMAKE_INSTALL_BINDIR}/td/generate") - install(FILES auto/tlo/td_api.tlo scheme/td_api.tl DESTINATION "${CMAKE_INSTALL_BINDIR}/td/generate/scheme") + install(FILES ${TLO_AUTO_INCLUDE_DIR}/td_api.tlo scheme/td_api.tl DESTINATION "${CMAKE_INSTALL_BINDIR}/td/generate/scheme") endif() if (TD_ENABLE_DOTNET) if (PHP_EXECUTABLE) - set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${CMAKE_CURRENT_SOURCE_DIR}/auto/tlo/td_api.tlo && ${PHP_EXECUTABLE} DotnetTlDocumentationGenerator.php scheme/td_api.tl auto/td/telegram/TdDotNetApi.h) + set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TLO_AUTO_INCLUDE_DIR}/td_api.tlo && ${PHP_EXECUTABLE} DotnetTlDocumentationGenerator.php scheme/td_api.tl auto/td/telegram/TdDotNetApi.h) else() - set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${CMAKE_CURRENT_SOURCE_DIR}/auto/tlo/td_api.tlo) + set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TLO_AUTO_INCLUDE_DIR}/td_api.tlo) endif() add_executable(td_generate_dotnet_api generate_dotnet.cpp tl_writer_dotnet.h) @@ -171,7 +173,7 @@ if (NOT CMAKE_CROSSCOMPILING) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${GENERATE_DOTNET_API_CMD} COMMENT "Generate .NET API files" - DEPENDS td_generate_dotnet_api tl_generate_tlo auto/tlo/td_api.tlo scheme/td_api.tl DotnetTlDocumentationGenerator.php + DEPENDS td_generate_dotnet_api tl_generate_tlo ${TLO_AUTO_INCLUDE_DIR}/td_api.tlo ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tl ${CMAKE_CURRENT_SOURCE_DIR}/DotnetTlDocumentationGenerator.php ) endif() diff --git a/tdutils/generate/CMakeLists.txt b/tdutils/generate/CMakeLists.txt index acad005c3..c0254e925 100644 --- a/tdutils/generate/CMakeLists.txt +++ b/tdutils/generate/CMakeLists.txt @@ -51,7 +51,7 @@ if (NOT CMAKE_CROSSCOMPILING) OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/auto/mime_type_to_extension.cpp WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${MIME_TYPE_TO_EXTENSION_CMD} - DEPENDS auto/mime_type_to_extension.gperf + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/auto/mime_type_to_extension.gperf ) if (CMAKE_HOST_WIN32) @@ -63,6 +63,6 @@ if (NOT CMAKE_CROSSCOMPILING) OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/auto/extension_to_mime_type.cpp WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${EXTENSION_TO_MIME_TYPE_CMD} - DEPENDS auto/extension_to_mime_type.gperf + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/auto/extension_to_mime_type.gperf ) endif()