Improve setProfilePhoto.

GitOrigin-RevId: 9a5ee470c0a38c7989cef642b2f69faa2228baaa
This commit is contained in:
levlam 2020-06-28 17:44:56 +03:00
parent 004e1dcc8d
commit 0ed26e5321
6 changed files with 140 additions and 49 deletions

View File

@ -63,7 +63,7 @@ authenticationCodeInfo phone_number:string type:AuthenticationCodeType next_type
emailAddressAuthenticationCodeInfo email_address_pattern:string length:int32 = EmailAddressAuthenticationCodeInfo;
//@description Represents a part of the text that needs to be formatted in some unusual way @offset Offset of the entity in UTF-16 code units @length Length of the entity, in UTF-16 code units @type Type of the entity
//@description Represents a part of the text that needs to be formatted in some unusual way @offset Offset of the entity, in UTF-16 code units @length Length of the entity, in UTF-16 code units @type Type of the entity
textEntity offset:int32 length:int32 type:TextEntityType = TextEntity;
//@description Contains a list of text entities @entities List of text entities
@ -355,6 +355,18 @@ chatPhoto id:int64 added_date:int32 sizes:vector<photoSize> animation:photoSize
chatPhotos total_count:int32 photos:vector<chatPhoto> = 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
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
inputChatPhotoStatic photo:InputFile = InputChatPhoto;
//@description An animation in MPEG4 format; must be square, shorter than 10 seconds, have width between 160 and 800 and be at most 2MB in size @animation Animation to be set as profile photo. Only inputFileLocal and inputFileGenerated are allowed
inputChatPhotoAnimation animation:InputFile = InputChatPhoto;
//@description Represents a user @id User identifier @first_name First name of the user @last_name Last name of the user @username Username of the user
//@phone_number Phone number of the user @status Current online status of the user @profile_photo Profile photo of the user; may be null
//@is_contact The user is a contact of the current user
@ -1623,7 +1635,7 @@ textEntityTypeTextUrl url:string = TextEntityType;
textEntityTypeMentionName user_id:int32 = TextEntityType;
//@description A thumbnail to be sent along with a file; should be in JPEG or WEBP format for stickers, and less than 200 KB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported
//@description A thumbnail to be sent along with a file; must be in JPEG or WEBP format for stickers, and less than 200 KB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported
//@width Thumbnail width, usually shouldn't exceed 320. Use 0 if unknown @height Thumbnail height, usually shouldn't exceed 320. Use 0 if unknown
inputThumbnail thumbnail:InputFile width:int32 height:int32 = InputThumbnail;
@ -4117,19 +4129,19 @@ getWebPagePreview text:formattedText = WebPage;
getWebPageInstantView url:string force_full:Bool = WebPageInstantView;
//@description Uploads a new profile photo for the current user. If something changes, updateUser will be sent @photo Profile photo to set. inputFileId and inputFileRemote may still be unsupported
setProfilePhoto photo:InputFile = Ok;
//@description Changes a profile photo for the current user @photo Profile photo to set
setProfilePhoto photo:InputChatPhoto = Ok;
//@description Deletes a profile photo. If something changes, updateUser will be sent @profile_photo_id Identifier of the profile photo to delete
//@description Deletes a profile photo @profile_photo_id Identifier of the profile photo to delete
deleteProfilePhoto profile_photo_id:int64 = Ok;
//@description Changes the first and last name of the current user. If something changes, updateUser will be sent @first_name The new value of the first name for the user; 1-64 characters @last_name The new value of the optional last name for the user; 0-64 characters
//@description Changes the first and last name of the current user @first_name The new value of the first name for the user; 1-64 characters @last_name The new value of the optional last name for the user; 0-64 characters
setName first_name:string last_name:string = Ok;
//@description Changes the bio of the current user @bio The new value of the user bio; 0-70 characters without line feeds
setBio bio:string = Ok;
//@description Changes the username of the current user. If something changes, updateUser will be sent @username The new value of the username. Use an empty string to remove the username
//@description Changes the username of the current user @username The new value of the username. Use an empty string to remove the username
setUsername username:string = Ok;
//@description Changes the location of the current user. Needs to be called if GetOption("is_location_visible") is true and location changes for more than 1 kilometer @location The new location of the user

Binary file not shown.

View File

@ -809,15 +809,24 @@ class UploadProfilePhotoQuery : public Td::ResultHandler {
explicit UploadProfilePhotoQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(FileId file_id, tl_object_ptr<telegram_api::InputFile> &&input_file) {
void send(FileId file_id, tl_object_ptr<telegram_api::InputFile> &&input_file, bool is_animation) {
CHECK(input_file != nullptr);
CHECK(file_id.is_valid());
file_id_ = file_id;
int32 flags = telegram_api::photos_uploadProfilePhoto::FILE_MASK;
int32 flags = 0;
tl_object_ptr<telegram_api::InputFile> photo_input_file;
tl_object_ptr<telegram_api::InputFile> video_input_file;
if (is_animation) {
flags |= telegram_api::photos_uploadProfilePhoto::VIDEO_MASK;
video_input_file = std::move(input_file);
} else {
flags |= telegram_api::photos_uploadProfilePhoto::FILE_MASK;
photo_input_file = std::move(input_file);
}
send_query(G()->net_query_creator().create(
telegram_api::photos_uploadProfilePhoto(flags, std::move(input_file), nullptr, 0)));
telegram_api::photos_uploadProfilePhoto(flags, std::move(photo_input_file), std::move(video_input_file), 0)));
}
void on_result(uint64 id, BufferSlice packet) override {
@ -876,7 +885,16 @@ class UpdateProfilePhotoQuery : public Td::ResultHandler {
if (file_id_.is_valid()) {
VLOG(file_references) << "Receive " << status << " for " << file_id_;
td->file_manager_->delete_file_reference(file_id_, file_reference_);
td->contacts_manager_->upload_profile_photo(file_id_, std::move(promise_));
td->file_reference_manager_->repair_file_reference(
file_id_,
PromiseCreator::lambda([file_id = file_id_, promise = std::move(promise_)](Result<Unit> result) mutable {
if (result.is_error()) {
return promise.set_error(Status::Error(400, "Can't find the photo"));
}
send_closure(G()->contacts_manager(), &ContactsManager::send_update_profile_photo_query, file_id,
std::move(promise));
}));
return;
} else {
LOG(ERROR) << "Receive file reference error, but file_id = " << file_id_;
@ -5362,33 +5380,73 @@ void ContactsManager::update_is_location_visible() {
G()->shared_config().set_option_boolean("is_location_visible", expire_date != 0);
}
void ContactsManager::set_profile_photo(const tl_object_ptr<td_api::InputFile> &input_photo, Promise<Unit> &&promise) {
auto r_file_id =
td_->file_manager_->get_input_file_id(FileType::Photo, input_photo, DialogId(get_my_id()), false, false);
void ContactsManager::set_profile_photo(const td_api::object_ptr<td_api::InputChatPhoto> &input_photo,
Promise<Unit> &&promise) {
if (input_photo == nullptr) {
return promise.set_error(Status::Error(400, "New profile photo must be non-empty"));
}
const td_api::object_ptr<td_api::InputFile> *input_file = nullptr;
bool is_animation = false;
switch (input_photo->get_id()) {
case td_api::inputChatPhotoPrevious::ID: {
auto photo = static_cast<const td_api::inputChatPhotoPrevious *>(input_photo.get());
auto photo_id = photo->chat_photo_id_;
if (photo_id <= 0) {
return promise.set_error(Status::Error(400, "Wrong profile photo ID specified"));
}
auto *u = get_user(get_my_id());
if (u != nullptr && photo_id == u->photo.id) {
return promise.set_value(Unit());
}
auto it = my_photo_file_id_.find(photo_id);
if (it == my_photo_file_id_.end()) {
return promise.set_error(Status::Error(400, "Profile photo ID not found"));
}
return send_update_profile_photo_query(td_->file_manager_->dup_file_id(it->second), std::move(promise));
}
case td_api::inputChatPhotoStatic::ID: {
auto photo = static_cast<const td_api::inputChatPhotoStatic *>(input_photo.get());
input_file = &photo->photo_;
break;
}
case td_api::inputChatPhotoAnimation::ID: {
auto photo = static_cast<const td_api::inputChatPhotoAnimation *>(input_photo.get());
input_file = &photo->animation_;
is_animation = true;
break;
}
default:
UNREACHABLE();
break;
}
auto file_type = is_animation ? FileType::Animation : FileType::Photo;
auto r_file_id = td_->file_manager_->get_input_file_id(file_type, *input_file, DialogId(get_my_id()), false, false);
if (r_file_id.is_error()) {
// TODO promise.set_error(std::move(status));
return promise.set_error(Status::Error(7, r_file_id.error().message()));
return promise.set_error(Status::Error(400, r_file_id.error().message()));
}
FileId file_id = r_file_id.ok();
CHECK(file_id.is_valid());
FileView file_view = td_->file_manager_->get_file_view(file_id);
CHECK(!file_view.is_encrypted());
if (file_view.has_remote_location() && !file_view.main_remote_location().is_web()) {
td_->create_handler<UpdateProfilePhotoQuery>(std::move(promise))
->send(td_->file_manager_->dup_file_id(file_id), file_view.main_remote_location().as_input_photo());
return;
}
upload_profile_photo(td_->file_manager_->dup_file_id(file_id), std::move(promise));
upload_profile_photo(td_->file_manager_->dup_file_id(file_id), is_animation, std::move(promise));
}
void ContactsManager::upload_profile_photo(FileId file_id, Promise<Unit> &&promise) {
void ContactsManager::send_update_profile_photo_query(FileId file_id, Promise<Unit> &&promise) {
FileView file_view = td_->file_manager_->get_file_view(file_id);
td_->create_handler<UpdateProfilePhotoQuery>(std::move(promise))
->send(file_id, file_view.main_remote_location().as_input_photo());
}
void ContactsManager::upload_profile_photo(FileId file_id, bool is_animation, Promise<Unit> &&promise,
vector<int> bad_parts) {
CHECK(file_id.is_valid());
CHECK(uploaded_profile_photos_.find(file_id) == uploaded_profile_photos_.end());
uploaded_profile_photos_.emplace(file_id, std::move(promise));
uploaded_profile_photos_.emplace(file_id, UploadedProfilePhoto{is_animation, !bad_parts.empty(), std::move(promise)});
LOG(INFO) << "Ask to upload profile photo " << file_id;
td_->file_manager_->upload(file_id, upload_profile_photo_callback_, 32, 0);
// TODO use force_reupload
td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_profile_photo_callback_, 32, 0);
}
void ContactsManager::delete_profile_photo(int64 profile_photo_id, Promise<Unit> &&promise) {
@ -9541,8 +9599,10 @@ void ContactsManager::register_user_photo(User *u, UserId user_id, const Photo &
return;
}
CHECK(file_type == FileType::Photo);
CHECK(u != nullptr);
auto photo_id = photo.id.get();
if (u->photo_ids.emplace(photo_id).second) {
VLOG(file_references) << "Register photo " << photo_id << " of " << user_id;
if (user_id == get_my_id()) {
my_photo_file_id_[photo_id] = first_file_id;
}
@ -13525,25 +13585,30 @@ void ContactsManager::on_upload_profile_photo(FileId file_id, tl_object_ptr<tele
auto it = uploaded_profile_photos_.find(file_id);
CHECK(it != uploaded_profile_photos_.end());
auto promise = std::move(it->second);
bool is_animation = it->second.is_animation;
bool is_reupload = it->second.is_reupload;
auto promise = std::move(it->second.promise);
uploaded_profile_photos_.erase(it);
FileView file_view = td_->file_manager_->get_file_view(file_id);
if (file_view.has_remote_location() && input_file == nullptr) {
if (file_view.main_remote_location().is_web()) {
// TODO reupload
promise.set_error(Status::Error(400, "Can't use web photo as profile photo"));
return;
return promise.set_error(Status::Error(400, "Can't use web photo as profile photo"));
}
if (is_reupload) {
return promise.set_error(Status::Error(400, "Failed to reuplaod the file"));
}
td_->create_handler<UpdateProfilePhotoQuery>(std::move(promise))
->send(file_id, file_view.main_remote_location().as_input_photo());
// delete file reference and forcely reupload the file
auto file_reference = FileManager::extract_file_reference(file_view.main_remote_location().as_input_photo());
td_->file_manager_->delete_file_reference(file_id, file_reference);
upload_profile_photo(file_id, is_animation, std::move(promise), {-1});
return;
}
CHECK(input_file != nullptr);
td_->create_handler<UploadProfilePhotoQuery>(std::move(promise))->send(file_id, std::move(input_file));
td_->create_handler<UploadProfilePhotoQuery>(std::move(promise))->send(file_id, std::move(input_file), is_animation);
}
void ContactsManager::on_upload_profile_photo_error(FileId file_id, Status status) {
@ -13553,7 +13618,7 @@ void ContactsManager::on_upload_profile_photo_error(FileId file_id, Status statu
auto it = uploaded_profile_photos_.find(file_id);
CHECK(it != uploaded_profile_photos_.end());
auto promise = std::move(it->second);
auto promise = std::move(it->second.promise);
uploaded_profile_photos_.erase(it);

View File

@ -322,12 +322,12 @@ class ContactsManager : public Actor {
void set_location_visibility();
void set_profile_photo(const tl_object_ptr<td_api::InputFile> &input_photo, Promise<Unit> &&promise);
void set_profile_photo(const td_api::object_ptr<td_api::InputChatPhoto> &input_photo, Promise<Unit> &&promise);
void send_update_profile_photo_query(FileId file_id, Promise<Unit> &&promise);
void delete_profile_photo(int64 profile_photo_id, Promise<Unit> &&promise);
void upload_profile_photo(FileId file_id, Promise<Unit> &&promise);
void set_name(const string &first_name, const string &last_name, Promise<Unit> &&promise);
void set_bio(const string &bio, Promise<Unit> &&promise);
@ -531,9 +531,6 @@ class ContactsManager : public Actor {
void on_update_secret_chat(SecretChatId secret_chat_id, int64 access_hash, UserId user_id, SecretChatState state,
bool is_outbound, int32 ttl, int32 date, string key_hash, int32 layer);
void on_upload_profile_photo(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file);
void on_upload_profile_photo_error(FileId file_id, Status status);
tl_object_ptr<td_api::chatMember> get_chat_member_object(const DialogParticipant &dialog_participant) const;
tl_object_ptr<td_api::chatInviteLinkInfo> get_chat_invite_link_info_object(const string &invite_link) const;
@ -1113,6 +1110,11 @@ class ContactsManager : public Actor {
void do_update_user_photo(User *u, UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo,
const char *source);
void upload_profile_photo(FileId file_id, bool is_animation, Promise<Unit> &&promise, vector<int> bad_parts = {});
void on_upload_profile_photo(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file);
void on_upload_profile_photo_error(FileId file_id, Status status);
void register_user_photo(User *u, UserId user_id, const Photo &photo);
void on_update_user_full_is_blocked(UserFull *user_full, UserId user_id, bool is_blocked);
@ -1495,7 +1497,16 @@ class ContactsManager : public Actor {
class UploadProfilePhotoCallback;
std::shared_ptr<UploadProfilePhotoCallback> upload_profile_photo_callback_;
std::unordered_map<FileId, Promise<Unit>, FileIdHash> uploaded_profile_photos_; // file_id -> promise
struct UploadedProfilePhoto {
bool is_animation;
bool is_reupload;
Promise<Unit> promise;
UploadedProfilePhoto(bool is_animation, bool is_reupload, Promise<Unit> promise)
: is_animation(is_animation), is_reupload(is_reupload), promise(std::move(promise)) {
}
};
std::unordered_map<FileId, UploadedProfilePhoto, FileIdHash> uploaded_profile_photos_; // file_id -> promise
std::unordered_map<int64, std::pair<vector<UserId>, vector<int32>>> imported_contacts_;

View File

@ -815,6 +815,7 @@ SecretInputMedia photo_get_secret_input_media(FileManager *file_manager, const P
vector<FileId> photo_get_file_ids(const Photo &photo) {
auto result = transform(photo.photos, [](auto &size) { return size.file_id; });
if (!photo.animated_photos.empty()) {
// photo file IDs must be first
append(result, transform(photo.animated_photos, [](auto &size) { return size.file_id; }));
}
return result;

View File

@ -2027,7 +2027,7 @@ class CliClient final : public Actor {
send_request(td_api::make_object<td_api::getChatMessageCount>(
as_chat_id(chat_id), get_search_messages_filter(filter), as_bool(return_local)));
} else if (op == "gup" || op == "gupf") {
} else if (op == "gup" || op == "gupp") {
string user_id;
string offset;
string limit;
@ -3840,13 +3840,15 @@ class CliClient final : public Actor {
std::tie(url, force_full) = split(args);
send_request(td_api::make_object<td_api::getWebPageInstantView>(url, as_bool(force_full)));
} else if (op == "sppp") {
send_request(td_api::make_object<td_api::setProfilePhoto>(
td_api::make_object<td_api::inputChatPhotoPrevious>(to_integer<int64>(args))));
} else if (op == "spp") {
send_request(td_api::make_object<td_api::setProfilePhoto>(as_input_file(args)));
} else if (op == "sppg") {
string path;
string conversion;
std::tie(path, conversion) = split(args);
send_request(td_api::make_object<td_api::setProfilePhoto>(as_generated_file(path, conversion)));
send_request(td_api::make_object<td_api::setProfilePhoto>(
td_api::make_object<td_api::inputChatPhotoStatic>(as_input_file(args))));
} else if (op == "sppa" || op == "sppv") {
send_request(td_api::make_object<td_api::setProfilePhoto>(
td_api::make_object<td_api::inputChatPhotoAnimation>(as_input_file(args))));
} else if (op == "sh") {
auto prefix = std::move(args);
send_request(td_api::make_object<td_api::searchHashtags>(prefix, 10));