diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 438971a86..87bdbf9cf 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -7273,8 +7273,11 @@ getWebPagePreview text:formattedText = WebPage; getWebPageInstantView url:string force_full:Bool = WebPageInstantView; -//@description Changes a profile photo for the current user @photo Profile photo to set @is_public Pass true to set a public photo, which will be visible even the main photo is hidden by privacy settings -setProfilePhoto photo:InputChatPhoto is_public:Bool = Ok; +//@description Changes a profile photo for the current user +//@photo Profile photo to set +//@custom_emoji Custom-emoji-based version of the profile photo; pass null if none or an existing photo is set +//@is_public Pass true to set a public photo, which will be visible even the main photo is hidden by privacy settings +setProfilePhoto photo:InputChatPhoto custom_emoji:chatPhotoCustomEmoji is_public:Bool = Ok; //@description Deletes a profile photo @profile_photo_id Identifier of the profile photo to delete deleteProfilePhoto profile_photo_id:int64 = Ok; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index a270d5779..99247587a 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -447,8 +447,9 @@ class UploadProfilePhotoQuery final : public Td::ResultHandler { explicit UploadProfilePhotoQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(UserId user_id, FileId file_id, tl_object_ptr &&input_file, bool is_fallback, - bool only_suggest, bool is_animation, double main_frame_timestamp) { + void send(UserId user_id, FileId file_id, tl_object_ptr &&input_file, + const CustomEmojiSize &custom_emoji_size, bool is_fallback, bool only_suggest, bool is_animation, + double main_frame_timestamp) { CHECK(input_file != nullptr); CHECK(file_id.is_valid()); @@ -485,9 +486,13 @@ class UploadProfilePhotoQuery final : public Td::ResultHandler { if (is_fallback) { flags |= telegram_api::photos_uploadProfilePhoto::FALLBACK_MASK; } + if (custom_emoji_size.custom_emoji_id.is_valid()) { + flags |= telegram_api::photos_uploadProfilePhoto::VIDEO_EMOJI_MARKUP_MASK; + } send_query(G()->net_query_creator().create( telegram_api::photos_uploadProfilePhoto(flags, false /*ignored*/, std::move(photo_input_file), - std::move(video_input_file), main_frame_timestamp, nullptr), + std::move(video_input_file), main_frame_timestamp, + get_input_video_size_object(custom_emoji_size)), {{"me"}})); } else { if (only_suggest) { @@ -7048,13 +7053,15 @@ FileId ContactsManager::get_profile_photo_file_id(int64 photo_id) const { return it->second; } -void ContactsManager::set_profile_photo(const td_api::object_ptr &input_photo, bool is_fallback, - Promise &&promise) { - set_profile_photo_impl(get_my_id(), input_photo, is_fallback, false, std::move(promise)); +void ContactsManager::set_profile_photo(const td_api::object_ptr &input_photo, + const td_api::object_ptr &custom_emoji, + bool is_fallback, Promise &&promise) { + set_profile_photo_impl(get_my_id(), input_photo, custom_emoji, is_fallback, false, std::move(promise)); } void ContactsManager::set_profile_photo_impl(UserId user_id, const td_api::object_ptr &input_photo, + const td_api::object_ptr &custom_emoji, bool is_fallback, bool only_suggest, Promise &&promise) { if (input_photo == nullptr) { return promise.set_error(Status::Error(400, "New profile photo must be non-empty")); @@ -7068,6 +7075,9 @@ void ContactsManager::set_profile_photo_impl(UserId user_id, if (user_id != get_my_id()) { return promise.set_error(Status::Error(400, "Can't use inputChatPhotoPrevious")); } + if (custom_emoji != nullptr) { + return promise.set_error(Status::Error(400, "Can't use custom emoji with a previous photo")); + } auto photo = static_cast(input_photo.get()); auto photo_id = photo->chat_photo_id_; auto *u = get_user(user_id); @@ -7114,8 +7124,10 @@ void ContactsManager::set_profile_photo_impl(UserId user_id, FileId file_id = r_file_id.ok(); CHECK(file_id.is_valid()); - upload_profile_photo(user_id, td_->file_manager_->dup_file_id(file_id, "set_profile_photo_impl"), is_fallback, - only_suggest, is_animation, main_frame_timestamp, std::move(promise)); + auto custom_emoji_size = get_custom_emoji_size(custom_emoji); + upload_profile_photo(user_id, td_->file_manager_->dup_file_id(file_id, "set_profile_photo_impl"), + std::move(custom_emoji_size), is_fallback, only_suggest, is_animation, main_frame_timestamp, + std::move(promise)); } void ContactsManager::set_user_profile_photo(UserId user_id, @@ -7139,7 +7151,7 @@ void ContactsManager::set_user_profile_photo(UserId user_id, return; } - set_profile_photo_impl(user_id, input_photo, false, only_suggest, std::move(promise)); + set_profile_photo_impl(user_id, input_photo, nullptr, false, only_suggest, std::move(promise)); } void ContactsManager::send_update_profile_photo_query(FileId file_id, int64 old_photo_id, bool is_fallback, @@ -7149,15 +7161,16 @@ void ContactsManager::send_update_profile_photo_query(FileId file_id, int64 old_ ->send(file_id, old_photo_id, is_fallback, file_view.main_remote_location().as_input_photo()); } -void ContactsManager::upload_profile_photo(UserId user_id, FileId file_id, bool is_fallback, bool only_suggest, - bool is_animation, double main_frame_timestamp, Promise &&promise, - int reupload_count, vector bad_parts) { +void ContactsManager::upload_profile_photo(UserId user_id, FileId file_id, CustomEmojiSize custom_emoji_size, + bool is_fallback, bool only_suggest, bool is_animation, + double main_frame_timestamp, Promise &&promise, int reupload_count, + vector bad_parts) { CHECK(file_id.is_valid()); - bool is_inserted = - uploaded_profile_photos_ - .emplace(file_id, UploadedProfilePhoto{user_id, is_fallback, only_suggest, main_frame_timestamp, is_animation, - reupload_count, std::move(promise)}) - .second; + bool is_inserted = uploaded_profile_photos_ + .emplace(file_id, UploadedProfilePhoto{user_id, std::move(custom_emoji_size), is_fallback, + only_suggest, main_frame_timestamp, is_animation, + reupload_count, std::move(promise)}) + .second; CHECK(is_inserted); LOG(INFO) << "Ask to upload " << (is_animation ? "animated" : "static") << " profile photo " << file_id << " for user " << user_id << " with bad parts " << bad_parts; @@ -17889,6 +17902,7 @@ void ContactsManager::on_upload_profile_photo(FileId file_id, tl_object_ptrsecond.user_id; + auto custom_emoji_size = std::move(it->second.custom_emoji_size); bool is_fallback = it->second.is_fallback; bool only_suggest = it->second.only_suggest; double main_frame_timestamp = it->second.main_frame_timestamp; @@ -17921,14 +17935,15 @@ void ContactsManager::on_upload_profile_photo(FileId file_id, tl_object_ptrfile_manager_->delete_file_reference(file_id, file_reference); - upload_profile_photo(user_id, file_id, is_fallback, only_suggest, is_animation, main_frame_timestamp, - std::move(promise), reupload_count + 1, {-1}); + upload_profile_photo(user_id, file_id, std::move(custom_emoji_size), is_fallback, only_suggest, is_animation, + main_frame_timestamp, std::move(promise), reupload_count + 1, {-1}); return; } CHECK(input_file != nullptr); td_->create_handler(std::move(promise)) - ->send(user_id, file_id, std::move(input_file), is_fallback, only_suggest, is_animation, main_frame_timestamp); + ->send(user_id, file_id, std::move(input_file), custom_emoji_size, is_fallback, only_suggest, is_animation, + main_frame_timestamp); } void ContactsManager::on_upload_profile_photo_error(FileId file_id, Status status) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 7527679e8..9ad90136a 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -365,7 +365,8 @@ class ContactsManager final : public Actor { FileId get_profile_photo_file_id(int64 photo_id) const; - void set_profile_photo(const td_api::object_ptr &input_photo, bool is_fallback, + void set_profile_photo(const td_api::object_ptr &input_photo, + const td_api::object_ptr &custom_emoji, bool is_fallback, Promise &&promise); void set_user_profile_photo(UserId user_id, const td_api::object_ptr &input_photo, @@ -1351,11 +1352,12 @@ class ContactsManager final : public Actor { void apply_pending_user_photo(User *u, UserId user_id); void set_profile_photo_impl(UserId user_id, const td_api::object_ptr &input_photo, - bool is_fallback, bool only_suggest, Promise &&promise); + const td_api::object_ptr &custom_emoji, bool is_fallback, + bool only_suggest, Promise &&promise); - void upload_profile_photo(UserId user_id, FileId file_id, bool is_fallback, bool only_suggest, bool is_animation, - double main_frame_timestamp, Promise &&promise, int reupload_count = 0, - vector bad_parts = {}); + void upload_profile_photo(UserId user_id, FileId file_id, CustomEmojiSize custom_emoji_size, bool is_fallback, + bool only_suggest, bool is_animation, double main_frame_timestamp, Promise &&promise, + int reupload_count = 0, vector bad_parts = {}); void on_upload_profile_photo(FileId file_id, tl_object_ptr input_file); void on_upload_profile_photo_error(FileId file_id, Status status); @@ -1894,6 +1896,7 @@ class ContactsManager final : public Actor { struct UploadedProfilePhoto { UserId user_id; + CustomEmojiSize custom_emoji_size; bool is_fallback; bool only_suggest; double main_frame_timestamp; @@ -1901,9 +1904,10 @@ class ContactsManager final : public Actor { int reupload_count; Promise promise; - UploadedProfilePhoto(UserId user_id, bool is_fallback, bool only_suggest, double main_frame_timestamp, - bool is_animation, int32 reupload_count, Promise promise) + UploadedProfilePhoto(UserId user_id, CustomEmojiSize custom_emoji_size, bool is_fallback, bool only_suggest, + double main_frame_timestamp, bool is_animation, int32 reupload_count, Promise promise) : user_id(user_id) + , custom_emoji_size(custom_emoji_size) , is_fallback(is_fallback) , only_suggest(only_suggest) , main_frame_timestamp(main_frame_timestamp) diff --git a/td/telegram/PhotoSize.cpp b/td/telegram/PhotoSize.cpp index ae83783c8..ebe29a944 100644 --- a/td/telegram/PhotoSize.cpp +++ b/td/telegram/PhotoSize.cpp @@ -303,6 +303,9 @@ Variant get_animation_size(FileManager *file_man LOG(ERROR) << "Receive invalid " << result; return {}; } + for (auto &color : result.background_colors) { + color &= 0xFFFFFF; + } return std::move(result); } case telegram_api::videoSizeStickerMarkup::ID: @@ -312,6 +315,53 @@ Variant get_animation_size(FileManager *file_man } } +CustomEmojiSize get_custom_emoji_size(const td_api::object_ptr &custom_emoji) { + if (custom_emoji == nullptr || custom_emoji->background_fill_ == nullptr) { + return {}; + } + CustomEmojiSize result; + result.custom_emoji_id = CustomEmojiId(custom_emoji->custom_emoji_id_); + auto fill = custom_emoji->background_fill_.get(); + switch (fill->get_id()) { + case td_api::backgroundFillSolid::ID: { + auto solid = static_cast(fill); + result.background_colors.push_back(solid->color_); + break; + } + case td_api::backgroundFillGradient::ID: { + auto gradient = static_cast(fill); + result.background_colors.push_back(gradient->top_color_); + result.background_colors.push_back(gradient->bottom_color_); + break; + } + case td_api::backgroundFillFreeformGradient::ID: { + auto freeform = static_cast(fill); + if (freeform->colors_.size() != 3 && freeform->colors_.size() != 4) { + return {}; + } + result.background_colors = freeform->colors_; + break; + } + default: + UNREACHABLE(); + break; + } + for (auto &color : result.background_colors) { + color &= 0xFFFFFF; + } + return result; +} + +telegram_api::object_ptr get_input_video_size_object( + const CustomEmojiSize &custom_emoji_size) { + if (!custom_emoji_size.custom_emoji_id.is_valid()) { + return nullptr; + } + + return telegram_api::make_object( + custom_emoji_size.custom_emoji_id.get(), vector(custom_emoji_size.background_colors)); +} + PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id, tl_object_ptr web_document_ptr) { if (web_document_ptr == nullptr) { diff --git a/td/telegram/PhotoSize.h b/td/telegram/PhotoSize.h index 079982afc..f627e986f 100644 --- a/td/telegram/PhotoSize.h +++ b/td/telegram/PhotoSize.h @@ -64,6 +64,10 @@ Variant get_animation_size(FileManager *file_man DialogId owner_dialog_id, tl_object_ptr &&size_ptr); +CustomEmojiSize get_custom_emoji_size(const td_api::object_ptr &custom_emoji); + +telegram_api::object_ptr get_input_video_size_object(const CustomEmojiSize &custom_emoji_size); + PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id, tl_object_ptr web_document_ptr); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 20bd54024..95a9ce1fe 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6980,7 +6980,7 @@ void Td::on_request(uint64 id, const td_api::setLocation &request) { void Td::on_request(uint64 id, td_api::setProfilePhoto &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->set_profile_photo(request.photo_, request.is_public_, std::move(promise)); + contacts_manager_->set_profile_photo(request.photo_, request.custom_emoji_, request.is_public_, std::move(promise)); } void Td::on_request(uint64 id, const td_api::deleteProfilePhoto &request) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 2038bc054..83be4343d 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4859,10 +4859,10 @@ class CliClient final : public Actor { int64 profile_photo_id; get_args(args, profile_photo_id); send_request(td_api::make_object( - td_api::make_object(profile_photo_id), op == "spppf")); + td_api::make_object(profile_photo_id), nullptr, op == "spppf")); } else if (op == "spp" || op == "sppf") { send_request(td_api::make_object( - td_api::make_object(as_input_file(args)), op == "sppf")); + td_api::make_object(as_input_file(args)), nullptr, op == "sppf")); } else if (op == "sppa" || op == "sppaf") { string animation; string main_frame_timestamp; @@ -4870,7 +4870,27 @@ class CliClient final : public Actor { send_request( td_api::make_object(td_api::make_object( as_input_file(animation), to_double(main_frame_timestamp)), - op == "sppaf")); + nullptr, op == "sppaf")); + } else if (op == "spppce" || op == "spppcef") { + string photo; + int64 custom_emoji_id; + get_args(args, photo, custom_emoji_id); + send_request(td_api::make_object( + td_api::make_object(as_input_file(photo)), + td_api::make_object( + custom_emoji_id, td_api::make_object(0x7FFFFFFF)), + op == "spppcef")); + } else if (op == "sppace" || op == "sppacef") { + string animation; + string main_frame_timestamp; + int64 custom_emoji_id; + get_args(args, animation, main_frame_timestamp, custom_emoji_id); + send_request(td_api::make_object( + td_api::make_object(as_input_file(animation), + to_double(main_frame_timestamp)), + td_api::make_object( + custom_emoji_id, td_api::make_object(0x7FFFFFFF)), + op == "sppacef")); } else if (op == "suppp") { UserId user_id; string photo;