diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 909bd08ce..2ffb3678d 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -538,10 +538,22 @@ botMenuButton text:string url:string = BotMenuButton; chatLocation location:location address:string = ChatLocation; +//@class ChatPhotoStickerType @description Describes type of a sticker, which was used to create a chat photo + +//@description Information about the sticker, which was used to create the chat photo +//@sticker_set_id Sticker set identifier +//@sticker_id Identifier of the sticker in the set +chatPhotoStickerTypeRegularOrMask sticker_set_id:int64 sticker_id:int64 = ChatPhotoStickerType; + //@description Information about the custom emoji, which was used to create the chat photo //@custom_emoji_id Identifier of the custom emoji -//@background_fill Describes a fill of the background under the custom emoji; rotation angle in backgroundFillGradient isn't supported -chatPhotoCustomEmoji custom_emoji_id:int64 background_fill:BackgroundFill = ChatPhotoCustomEmoji; +chatPhotoStickerTypeCustomEmoji custom_emoji_id:int64 = ChatPhotoStickerType; + + +//@description Information about the sticker, which was used to create the chat photo +//@type Type of the sticker +//@background_fill Describes a fill of the background under the sticker; rotation angle in backgroundFillGradient isn't supported +chatPhotoSticker type:ChatPhotoStickerType background_fill:BackgroundFill = ChatPhotoSticker; //@description Animated variant of a chat photo in MPEG4 format //@length Animation width and height @@ -557,8 +569,8 @@ animatedChatPhoto length:int32 file:file main_frame_timestamp:double = AnimatedC //@sizes Available variants of the photo in JPEG format, in different size //@animation A big (640x640) animated variant of the photo in MPEG4 format; may be null //@small_animation A small (160x160) animated variant of the photo in MPEG4 format; may be null even the big animation is available -//@custom_emoji Custom-emoji-based version of the chat photo; may be null -chatPhoto id:int64 added_date:int32 minithumbnail:minithumbnail sizes:vector animation:animatedChatPhoto small_animation:animatedChatPhoto custom_emoji:chatPhotoCustomEmoji = ChatPhoto; +//@sticker Sticker-based version of the chat photo; may be null +chatPhoto id:int64 added_date:int32 minithumbnail:minithumbnail sizes:vector animation:animatedChatPhoto small_animation:animatedChatPhoto sticker:chatPhotoSticker = ChatPhoto; //@description Contains a list of chat or user profile photos @total_count Total number of photos @photos List of photos chatPhotos total_count:int32 photos:vector = ChatPhotos; @@ -7314,9 +7326,9 @@ getWebPageInstantView url:string force_full:Bool = WebPageInstantView; //@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 +//@sticker Sticker-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; +setProfilePhoto photo:InputChatPhoto sticker:chatPhotoSticker 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 abb9b74ea..fc822d513 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -448,7 +448,7 @@ class UploadProfilePhotoQuery final : public Td::ResultHandler { } 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, + const StickerPhotoSize &sticker_photo_size, bool is_fallback, bool only_suggest, bool is_animation, double main_frame_timestamp) { CHECK(input_file != nullptr); CHECK(file_id.is_valid()); @@ -486,13 +486,14 @@ 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()) { + auto video_emoji_markup = get_input_video_size_object(td_, sticker_photo_size); + if (video_emoji_markup != nullptr) { 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, - get_input_video_size_object(custom_emoji_size)), + std::move(video_emoji_markup)), {{"me"}})); } else { if (only_suggest) { @@ -7054,14 +7055,14 @@ FileId ContactsManager::get_profile_photo_file_id(int64 photo_id) const { } void ContactsManager::set_profile_photo(const td_api::object_ptr &input_photo, - const td_api::object_ptr &custom_emoji, + 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, + 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")); @@ -7124,9 +7125,9 @@ void ContactsManager::set_profile_photo_impl(UserId user_id, FileId file_id = r_file_id.ok(); CHECK(file_id.is_valid()); - auto custom_emoji_size = get_custom_emoji_size(custom_emoji); + auto sticker_photo_size = get_sticker_photo_size(td_, 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(sticker_photo_size), is_fallback, only_suggest, is_animation, main_frame_timestamp, std::move(promise)); } @@ -7161,13 +7162,13 @@ 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, CustomEmojiSize custom_emoji_size, +void ContactsManager::upload_profile_photo(UserId user_id, FileId file_id, StickerPhotoSize sticker_photo_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, std::move(custom_emoji_size), is_fallback, + .emplace(file_id, UploadedProfilePhoto{user_id, std::move(sticker_photo_size), is_fallback, only_suggest, main_frame_timestamp, is_animation, reupload_count, std::move(promise)}) .second; @@ -11983,8 +11984,7 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u FileId description_animation_file_id; if (user->bot_info_ != nullptr && !td_->auth_manager_->is_bot()) { description = std::move(user->bot_info_->description_); - description_photo = - get_photo(td_->file_manager_.get(), std::move(user->bot_info_->description_photo_), DialogId(user_id)); + description_photo = get_photo(td_, std::move(user->bot_info_->description_photo_), DialogId(user_id)); auto document = std::move(user->bot_info_->description_document_); if (document != nullptr) { int32 document_id = document->get_id(); @@ -12013,9 +12013,9 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u user_full->is_changed = true; } - auto photo = get_photo(td_->file_manager_.get(), std::move(user->profile_photo_), DialogId(user_id)); - auto personal_photo = get_photo(td_->file_manager_.get(), std::move(user->personal_photo_), DialogId(user_id)); - auto fallback_photo = get_photo(td_->file_manager_.get(), std::move(user->fallback_photo_), DialogId(user_id)); + auto photo = get_photo(td_, std::move(user->profile_photo_), DialogId(user_id)); + auto personal_photo = get_photo(td_, std::move(user->personal_photo_), DialogId(user_id)); + auto fallback_photo = get_photo(td_, std::move(user->fallback_photo_), DialogId(user_id)); // do_update_user_photo should be a no-op if server sent consistent data const Photo *photo_ptr = nullptr; bool is_personal = false; @@ -12109,7 +12109,7 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim } } - auto photo = get_photo(td_->file_manager_.get(), std::move(server_photo), DialogId(user_id)); + auto photo = get_photo(td_, std::move(server_photo), DialogId(user_id)); register_user_photo(u, user_id, photo); } } @@ -12136,7 +12136,7 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim } for (auto &photo : photos) { - auto user_photo = get_photo(td_->file_manager_.get(), std::move(photo), DialogId(user_id)); + auto user_photo = get_photo(td_, std::move(photo), DialogId(user_id)); if (user_photo.is_empty()) { LOG(ERROR) << "Receive empty profile photo in getUserPhotos request for " << user_id << " with offset " << offset << " and limit " << limit << ". Receive " << photo_count << " photos out of " << total_count @@ -12278,7 +12278,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c ChatFull *chat_full = add_chat_full(chat_id); on_update_chat_full_invite_link(chat_full, std::move(chat->exported_invite_)); - auto photo = get_photo(td_->file_manager_.get(), std::move(chat->chat_photo_), DialogId(chat_id)); + auto photo = get_photo(td_, std::move(chat->chat_photo_), DialogId(chat_id)); // on_update_chat_photo should be a no-op if server sent consistent data on_update_chat_photo(c, chat_id, as_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), 0, photo, false), false); @@ -12454,7 +12454,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c channel_full->need_save_to_database = true; } - auto photo = get_photo(td_->file_manager_.get(), std::move(channel->chat_photo_), DialogId(channel_id)); + auto photo = get_photo(td_, std::move(channel->chat_photo_), DialogId(channel_id)); // on_update_channel_photo should be a no-op if server sent consistent data on_update_channel_photo( c, channel_id, as_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), c->access_hash, photo, false), @@ -13121,8 +13121,7 @@ void ContactsManager::on_set_profile_photo(UserId user_id, tl_object_ptrusers_), "on_set_profile_photo"); - add_set_profile_photo_to_cache( - user_id, get_photo(td_->file_manager_.get(), std::move(photo->photo_), DialogId(user_id)), is_fallback); + add_set_profile_photo_to_cache(user_id, get_photo(td_, std::move(photo->photo_), DialogId(user_id)), is_fallback); } void ContactsManager::on_delete_profile_photo(int64 profile_photo_id, Promise promise) { @@ -14541,7 +14540,7 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, } invite_link_info->dialog_id = DialogId(); invite_link_info->title = chat_invite->title_; - invite_link_info->photo = get_photo(td_->file_manager_.get(), std::move(chat_invite->photo_), DialogId()); + invite_link_info->photo = get_photo(td_, std::move(chat_invite->photo_), DialogId()); invite_link_info->description = std::move(chat_invite->about_); invite_link_info->participant_count = chat_invite->participants_count_; invite_link_info->participant_user_ids = std::move(participant_user_ids); @@ -17909,7 +17908,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); + auto sticker_photo_size = std::move(it->second.sticker_photo_size); bool is_fallback = it->second.is_fallback; bool only_suggest = it->second.only_suggest; double main_frame_timestamp = it->second.main_frame_timestamp; @@ -17942,14 +17941,14 @@ 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, std::move(custom_emoji_size), is_fallback, only_suggest, is_animation, + upload_profile_photo(user_id, file_id, std::move(sticker_photo_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), custom_emoji_size, is_fallback, only_suggest, is_animation, + ->send(user_id, file_id, std::move(input_file), sticker_photo_size, is_fallback, only_suggest, is_animation, main_frame_timestamp); } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 9ad90136a..79adc1016 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -366,7 +366,7 @@ 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, - const td_api::object_ptr &custom_emoji, bool is_fallback, + 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, @@ -1352,10 +1352,10 @@ 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, - const td_api::object_ptr &custom_emoji, bool is_fallback, + 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, CustomEmojiSize custom_emoji_size, bool is_fallback, + void upload_profile_photo(UserId user_id, FileId file_id, StickerPhotoSize sticker_photo_size, bool is_fallback, bool only_suggest, bool is_animation, double main_frame_timestamp, Promise &&promise, int reupload_count = 0, vector bad_parts = {}); @@ -1896,7 +1896,7 @@ class ContactsManager final : public Actor { struct UploadedProfilePhoto { UserId user_id; - CustomEmojiSize custom_emoji_size; + StickerPhotoSize sticker_photo_size; bool is_fallback; bool only_suggest; double main_frame_timestamp; @@ -1904,10 +1904,10 @@ class ContactsManager final : public Actor { int reupload_count; Promise promise; - UploadedProfilePhoto(UserId user_id, CustomEmojiSize custom_emoji_size, bool is_fallback, bool only_suggest, + UploadedProfilePhoto(UserId user_id, StickerPhotoSize sticker_photo_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) + , sticker_photo_size(sticker_photo_size) , is_fallback(is_fallback) , only_suggest(only_suggest) , main_frame_timestamp(main_frame_timestamp) diff --git a/td/telegram/DialogEventLog.cpp b/td/telegram/DialogEventLog.cpp index cfdee4218..b1cd069e4 100644 --- a/td/telegram/DialogEventLog.cpp +++ b/td/telegram/DialogEventLog.cpp @@ -135,9 +135,9 @@ static td_api::object_ptr get_chat_event_action_object( } case telegram_api::channelAdminLogEventActionChangePhoto::ID: { auto action = move_tl_object_as(action_ptr); + auto old_photo = get_photo(td, std::move(action->prev_photo_), DialogId(channel_id)); + auto new_photo = get_photo(td, std::move(action->new_photo_), DialogId(channel_id)); auto file_manager = td->file_manager_.get(); - auto old_photo = get_photo(file_manager, std::move(action->prev_photo_), DialogId(channel_id)); - auto new_photo = get_photo(file_manager, std::move(action->new_photo_), DialogId(channel_id)); return td_api::make_object(get_chat_photo_object(file_manager, old_photo), get_chat_photo_object(file_manager, new_photo)); } diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index 431e51f18..b693ebce8 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -340,8 +340,8 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo if (thumb->type_ == "v") { if (!animated_thumbnail.file_id.is_valid()) { auto animation_size = - get_animation_size(td_->file_manager_.get(), PhotoSizeSource::thumbnail(FileType::Thumbnail, 0), id, - access_hash, file_reference, DcId::create(dc_id), owner_dialog_id, std::move(thumb)); + get_animation_size(td_, PhotoSizeSource::thumbnail(FileType::Thumbnail, 0), id, access_hash, + file_reference, DcId::create(dc_id), owner_dialog_id, std::move(thumb)); if (animation_size.get_offset() == 0) { animated_thumbnail = std::move(animation_size.get<0>()); } else { diff --git a/td/telegram/Game.cpp b/td/telegram/Game.cpp index 79c273b29..9907ecd42 100644 --- a/td/telegram/Game.cpp +++ b/td/telegram/Game.cpp @@ -35,7 +35,7 @@ Game::Game(Td *td, string title, string description, tl_object_ptrfile_manager_.get(), std::move(photo), owner_dialog_id); + photo_ = get_photo(td, std::move(photo), owner_dialog_id); if (photo_.is_empty()) { LOG(ERROR) << "Receive empty photo for game " << title_; photo_.id = 0; // to prevent null photo in td_api diff --git a/td/telegram/InlineQueriesManager.cpp b/td/telegram/InlineQueriesManager.cpp index a1546fd0f..e6852561d 100644 --- a/td/telegram/InlineQueriesManager.cpp +++ b/td/telegram/InlineQueriesManager.cpp @@ -1651,7 +1651,7 @@ void InlineQueriesManager::on_get_inline_query_results(DialogId dialog_id, UserI LOG_IF(ERROR, !is_photo) << "Wrong result type " << result->type_; auto photo = make_tl_object(); photo->id_ = std::move(result->id_); - Photo p = get_photo(td_->file_manager_.get(), std::move(result->photo_), DialogId()); + Photo p = get_photo(td_, std::move(result->photo_), DialogId()); if (p.is_empty()) { LOG(ERROR) << "Receive empty cached photo in the result of inline query"; break; diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 911158810..b920feaa2 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -4654,7 +4654,7 @@ unique_ptr get_message_content(Td *td, FormattedText message, return make_unique(); } - auto photo = get_photo(td->file_manager_.get(), std::move(media->photo_), owner_dialog_id); + auto photo = get_photo(td, std::move(media->photo_), owner_dialog_id); if (photo.is_empty()) { return make_unique(); } @@ -5085,7 +5085,7 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(action_ptr); - auto photo = get_photo(td->file_manager_.get(), std::move(action->photo_), owner_dialog_id); + auto photo = get_photo(td, std::move(action->photo_), owner_dialog_id); if (photo.is_empty()) { break; } @@ -5359,7 +5359,7 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(action_ptr); - auto photo = get_photo(td->file_manager_.get(), std::move(action->photo_), owner_dialog_id); + auto photo = get_photo(td, std::move(action->photo_), owner_dialog_id); if (photo.is_empty()) { break; } diff --git a/td/telegram/MessageExtendedMedia.cpp b/td/telegram/MessageExtendedMedia.cpp index f4cef209f..fdf93cd5b 100644 --- a/td/telegram/MessageExtendedMedia.cpp +++ b/td/telegram/MessageExtendedMedia.cpp @@ -53,7 +53,7 @@ MessageExtendedMedia::MessageExtendedMedia( break; } - photo_ = get_photo(td->file_manager_.get(), std::move(photo->photo_), owner_dialog_id); + photo_ = get_photo(td, std::move(photo->photo_), owner_dialog_id); if (photo_.is_empty()) { break; } diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index faf7ae569..fb7007e42 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -3377,8 +3377,7 @@ Status NotificationManager::process_push_notification_payload(string payload, bo VLOG(notifications) << "Have attached photo"; loc_key.resize(loc_key.rfind('_') + 1); loc_key += "PHOTO"; - attached_photo = get_photo(td_->file_manager_.get(), - telegram_api::move_object_as(result), dialog_id); + attached_photo = get_photo(td_, telegram_api::move_object_as(result), dialog_id); } else { LOG(ERROR) << "Receive attached photo for " << loc_key; } diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index 6b251ae5d..33ae2c195 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -14,6 +14,7 @@ #include "td/telegram/net/DcId.h" #include "td/telegram/PhotoFormat.h" #include "td/telegram/PhotoSizeSource.h" +#include "td/telegram/Td.h" #include "td/utils/algorithm.h" #include "td/utils/common.h" @@ -265,11 +266,28 @@ static tl_object_ptr get_animated_chat_photo_object(F animation_size->main_frame_timestamp); } -static tl_object_ptr get_chat_photo_custom_emoji_object( - const CustomEmojiSize &custom_emoji_size) { - if (!custom_emoji_size.custom_emoji_id.is_valid()) { - return nullptr; +static tl_object_ptr get_chat_photo_sticker_object( + const StickerPhotoSize &sticker_photo_size) { + td_api::object_ptr type; + switch (sticker_photo_size.type) { + case StickerPhotoSize::Type::Sticker: + if (!sticker_photo_size.sticker_set_id.is_valid()) { + return nullptr; + } + type = td_api::make_object(sticker_photo_size.sticker_set_id.get(), + sticker_photo_size.sticker_id); + break; + case StickerPhotoSize::Type::CustomEmoji: + if (!sticker_photo_size.custom_emoji_id.is_valid()) { + return nullptr; + } + type = td_api::make_object(sticker_photo_size.custom_emoji_id.get()); + break; + default: + UNREACHABLE(); + return nullptr; } + CHECK(type != nullptr); auto background_fill = [&](vector colors) -> td_api::object_ptr { switch (colors.size()) { @@ -284,10 +302,9 @@ static tl_object_ptr get_chat_photo_custom_emoji_o UNREACHABLE(); return nullptr; } - }(custom_emoji_size.background_colors); + }(sticker_photo_size.background_colors); - return td_api::make_object(custom_emoji_size.custom_emoji_id.get(), - std::move(background_fill)); + return td_api::make_object(std::move(type), std::move(background_fill)); } Photo get_encrypted_file_photo(FileManager *file_manager, unique_ptr &&file, @@ -318,15 +335,15 @@ Photo get_encrypted_file_photo(FileManager *file_manager, unique_ptr &&photo, DialogId owner_dialog_id) { +Photo get_photo(Td *td, tl_object_ptr &&photo, DialogId owner_dialog_id) { if (photo == nullptr || photo->get_id() == telegram_api::photoEmpty::ID) { return Photo(); } CHECK(photo->get_id() == telegram_api::photo::ID); - return get_photo(file_manager, move_tl_object_as(photo), owner_dialog_id); + return get_photo(td, move_tl_object_as(photo), owner_dialog_id); } -Photo get_photo(FileManager *file_manager, tl_object_ptr &&photo, DialogId owner_dialog_id) { +Photo get_photo(Td *td, tl_object_ptr &&photo, DialogId owner_dialog_id) { CHECK(photo != nullptr); Photo res; @@ -341,8 +358,8 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr && DcId dc_id = DcId::create(photo->dc_id_); for (auto &size_ptr : photo->sizes_) { - auto photo_size = get_photo_size(file_manager, PhotoSizeSource::thumbnail(FileType::Photo, 0), photo->id_, - photo->access_hash_, photo->file_reference_.as_slice().str(), dc_id, + auto photo_size = get_photo_size(td->file_manager_.get(), PhotoSizeSource::thumbnail(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); if (photo_size.get_offset() == 0) { PhotoSize &size = photo_size.get<0>(); @@ -358,9 +375,9 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr && } for (auto &size_ptr : photo->video_sizes_) { - auto animation = get_animation_size(file_manager, PhotoSizeSource::thumbnail(FileType::Photo, 0), photo->id_, - photo->access_hash_, photo->file_reference_.as_slice().str(), dc_id, - owner_dialog_id, std::move(size_ptr)); + auto animation = + get_animation_size(td, PhotoSizeSource::thumbnail(FileType::Photo, 0), photo->id_, photo->access_hash_, + photo->file_reference_.as_slice().str(), dc_id, owner_dialog_id, std::move(size_ptr)); if (animation.empty()) { continue; } @@ -370,7 +387,7 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr && res.animations.push_back(std::move(animation_size)); } }, - [&](const CustomEmojiSize &custom_emoji_size) { res.custom_emoji_size = std::move(custom_emoji_size); })); + [&](const StickerPhotoSize &sticker_photo_size) { res.sticker_photo_size = std::move(sticker_photo_size); })); } return res; @@ -418,7 +435,7 @@ tl_object_ptr get_chat_photo_object(FileManager *file_manager photo.id.get(), photo.date, get_minithumbnail_object(photo.minithumbnail), get_photo_sizes_object(file_manager, photo.photos), get_animated_chat_photo_object(file_manager, big_animation), get_animated_chat_photo_object(file_manager, small_animation), - get_chat_photo_custom_emoji_object(photo.custom_emoji_size)); + get_chat_photo_sticker_object(photo.sticker_photo_size)); } void photo_delete_thumbnail(Photo &photo) { @@ -607,7 +624,7 @@ FileId get_photo_thumbnail_file_id(const Photo &photo) { bool operator==(const Photo &lhs, const Photo &rhs) { return lhs.id.get() == rhs.id.get() && lhs.photos == rhs.photos && lhs.animations == rhs.animations && - lhs.custom_emoji_size == rhs.custom_emoji_size; + lhs.sticker_photo_size == rhs.sticker_photo_size; } bool operator!=(const Photo &lhs, const Photo &rhs) { @@ -619,8 +636,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Photo &photo) { if (!photo.animations.empty()) { string_builder << ", animations = " << format::as_array(photo.animations); } - if (photo.custom_emoji_size.custom_emoji_id.is_valid()) { - string_builder << ", custom emoji size = " << photo.custom_emoji_size; + if (photo.sticker_photo_size.custom_emoji_id.is_valid()) { + string_builder << ", custom emoji size = " << photo.sticker_photo_size; } return string_builder << ']'; } diff --git a/td/telegram/Photo.h b/td/telegram/Photo.h index 05525b0e0..59443a814 100644 --- a/td/telegram/Photo.h +++ b/td/telegram/Photo.h @@ -24,6 +24,7 @@ namespace td { class FileManager; +class Td; struct DialogPhoto { FileId small_file_id; @@ -45,7 +46,7 @@ struct Photo { vector animations; - CustomEmojiSize custom_emoji_size; + StickerPhotoSize sticker_photo_size; bool has_stickers = false; vector sticker_file_ids; @@ -100,8 +101,8 @@ bool need_update_dialog_photo(const DialogPhoto &from, const DialogPhoto &to); StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo); -Photo get_photo(FileManager *file_manager, tl_object_ptr &&photo, DialogId owner_dialog_id); -Photo get_photo(FileManager *file_manager, tl_object_ptr &&photo, DialogId owner_dialog_id); +Photo get_photo(Td *td, tl_object_ptr &&photo, DialogId owner_dialog_id); +Photo get_photo(Td *td, tl_object_ptr &&photo, DialogId owner_dialog_id); Photo get_encrypted_file_photo(FileManager *file_manager, unique_ptr &&file, tl_object_ptr &&photo, DialogId owner_dialog_id); Photo get_web_document_photo(FileManager *file_manager, tl_object_ptr web_document, diff --git a/td/telegram/Photo.hpp b/td/telegram/Photo.hpp index 25e91471d..46fa8b193 100644 --- a/td/telegram/Photo.hpp +++ b/td/telegram/Photo.hpp @@ -71,12 +71,12 @@ template void store(const Photo &photo, StorerT &storer) { bool has_minithumbnail = !photo.minithumbnail.empty(); bool has_animations = !photo.animations.empty(); - bool has_custom_emoji_size = photo.custom_emoji_size.custom_emoji_id.is_valid(); + bool has_sticker_photo_size = photo.sticker_photo_size.custom_emoji_id.is_valid(); BEGIN_STORE_FLAGS(); STORE_FLAG(photo.has_stickers); STORE_FLAG(has_minithumbnail); STORE_FLAG(has_animations); - STORE_FLAG(has_custom_emoji_size); + STORE_FLAG(has_sticker_photo_size); END_STORE_FLAGS(); store(photo.id.get(), storer); store(photo.date, storer); @@ -90,8 +90,8 @@ void store(const Photo &photo, StorerT &storer) { if (has_animations) { store(photo.animations, storer); } - if (has_custom_emoji_size) { - store(photo.custom_emoji_size, storer); + if (has_sticker_photo_size) { + store(photo.sticker_photo_size, storer); } } @@ -99,12 +99,12 @@ template void parse(Photo &photo, ParserT &parser) { bool has_minithumbnail; bool has_animations; - bool has_custom_emoji_size; + bool has_sticker_photo_size; BEGIN_PARSE_FLAGS(); PARSE_FLAG(photo.has_stickers); PARSE_FLAG(has_minithumbnail); PARSE_FLAG(has_animations); - PARSE_FLAG(has_custom_emoji_size); + PARSE_FLAG(has_sticker_photo_size); END_PARSE_FLAGS(); int64 id; parse(id, parser); @@ -120,8 +120,8 @@ void parse(Photo &photo, ParserT &parser) { if (has_animations) { parse(photo.animations, parser); } - if (has_custom_emoji_size) { - parse(photo.custom_emoji_size, parser); + if (has_sticker_photo_size) { + parse(photo.sticker_photo_size, parser); } } diff --git a/td/telegram/PhotoSize.cpp b/td/telegram/PhotoSize.cpp index ebe29a944..555a4176a 100644 --- a/td/telegram/PhotoSize.cpp +++ b/td/telegram/PhotoSize.cpp @@ -8,6 +8,8 @@ #include "td/telegram/files/FileLocation.h" #include "td/telegram/files/FileManager.h" +#include "td/telegram/StickersManager.h" +#include "td/telegram/Td.h" #include "td/utils/base64.h" #include "td/utils/HttpUrl.h" @@ -258,10 +260,10 @@ Variant get_photo_size(FileManager *file_manager, PhotoSizeSo return std::move(res); } -Variant get_animation_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) { +Variant get_animation_size(Td *td, PhotoSizeSource source, int64 id, int64 access_hash, + std::string file_reference, DcId dc_id, + DialogId owner_dialog_id, + tl_object_ptr &&size_ptr) { CHECK(size_ptr != nullptr); switch (size_ptr->get_id()) { case telegram_api::videoSize::ID: { @@ -289,13 +291,14 @@ Variant get_animation_size(FileManager *file_man result.size = 0; } - result.file_id = register_photo_size(file_manager, source, id, access_hash, std::move(file_reference), + result.file_id = register_photo_size(td->file_manager_.get(), source, id, access_hash, std::move(file_reference), owner_dialog_id, result.size, dc_id, PhotoFormat::Mpeg4); return std::move(result); } case telegram_api::videoSizeEmojiMarkup::ID: { auto size = move_tl_object_as(size_ptr); - CustomEmojiSize result; + StickerPhotoSize result; + result.type = StickerPhotoSize::Type::CustomEmoji; result.custom_emoji_id = CustomEmojiId(size->emoji_id_); result.background_colors = std::move(size->background_colors_); if (!result.custom_emoji_id.is_valid() || result.background_colors.empty() || @@ -308,20 +311,55 @@ Variant get_animation_size(FileManager *file_man } return std::move(result); } - case telegram_api::videoSizeStickerMarkup::ID: - return {}; + case telegram_api::videoSizeStickerMarkup::ID: { + auto size = move_tl_object_as(size_ptr); + StickerPhotoSize result; + result.type = StickerPhotoSize::Type::Sticker; + result.sticker_set_id = td->stickers_manager_->add_sticker_set(std::move(size->stickerset_)); + result.sticker_id = size->sticker_id_; + result.background_colors = std::move(size->background_colors_); + if (!result.sticker_set_id.is_valid() || result.sticker_id == 0 || !result.custom_emoji_id.is_valid() || + result.background_colors.empty() || result.background_colors.size() > 4) { + LOG(ERROR) << "Receive invalid " << result; + return {}; + } + for (auto &color : result.background_colors) { + color &= 0xFFFFFF; + } + return std::move(result); + } default: UNREACHABLE(); } } -CustomEmojiSize get_custom_emoji_size(const td_api::object_ptr &custom_emoji) { - if (custom_emoji == nullptr || custom_emoji->background_fill_ == nullptr) { +StickerPhotoSize get_sticker_photo_size(Td *td, const td_api::object_ptr &sticker) { + if (sticker == nullptr || sticker->type_ == nullptr || sticker->background_fill_ == nullptr) { return {}; } - CustomEmojiSize result; - result.custom_emoji_id = CustomEmojiId(custom_emoji->custom_emoji_id_); - auto fill = custom_emoji->background_fill_.get(); + StickerPhotoSize result; + switch (sticker->type_->get_id()) { + case td_api::chatPhotoStickerTypeRegularOrMask::ID: { + auto type = static_cast(sticker->type_.get()); + result.type = StickerPhotoSize::Type::Sticker; + result.sticker_set_id = StickerSetId(type->sticker_set_id_); + result.sticker_id = type->sticker_id_; + //if (!td->stickers_manager_->have_sticker(result.sticker_set_id, result.sticker_id)) { + // return {}; + //} + break; + } + case td_api::chatPhotoStickerTypeCustomEmoji::ID: { + auto type = static_cast(sticker->type_.get()); + result.type = StickerPhotoSize::Type::CustomEmoji; + result.custom_emoji_id = CustomEmojiId(type->custom_emoji_id_); + //if (!td->stickers_manager_->have_custom_emoji_id(result.custom_emoji_id)) { + // return {}; + //} + break; + } + } + auto fill = sticker->background_fill_.get(); switch (fill->get_id()) { case td_api::backgroundFillSolid::ID: { auto solid = static_cast(fill); @@ -353,13 +391,25 @@ CustomEmojiSize get_custom_emoji_size(const td_api::object_ptr get_input_video_size_object( - const CustomEmojiSize &custom_emoji_size) { - if (!custom_emoji_size.custom_emoji_id.is_valid()) { - return nullptr; + Td *td, const StickerPhotoSize &sticker_photo_size) { + switch (sticker_photo_size.type) { + case StickerPhotoSize::Type::Sticker: + if (!sticker_photo_size.sticker_set_id.is_valid()) { + return nullptr; + } + return telegram_api::make_object( + td->stickers_manager_->get_input_sticker_set(sticker_photo_size.sticker_set_id), + sticker_photo_size.sticker_id, vector(sticker_photo_size.background_colors)); + case StickerPhotoSize::Type::CustomEmoji: + if (!sticker_photo_size.custom_emoji_id.is_valid()) { + return nullptr; + } + return telegram_api::make_object( + sticker_photo_size.custom_emoji_id.get(), vector(sticker_photo_size.background_colors)); + default: + UNREACHABLE(); + 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, @@ -516,16 +566,26 @@ StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &an << animation_size.main_frame_timestamp; } -bool operator==(const CustomEmojiSize &lhs, const CustomEmojiSize &rhs) { - return lhs.custom_emoji_id == rhs.custom_emoji_id && lhs.background_colors == rhs.background_colors; +bool operator==(const StickerPhotoSize &lhs, const StickerPhotoSize &rhs) { + return lhs.type == rhs.type && lhs.sticker_set_id == rhs.sticker_set_id && lhs.sticker_id == rhs.sticker_id && + lhs.custom_emoji_id == rhs.custom_emoji_id && lhs.background_colors == rhs.background_colors; } -bool operator!=(const CustomEmojiSize &lhs, const CustomEmojiSize &rhs) { +bool operator!=(const StickerPhotoSize &lhs, const StickerPhotoSize &rhs) { return !(lhs == rhs); } -StringBuilder &operator<<(StringBuilder &string_builder, const CustomEmojiSize &custom_emoji_size) { - return string_builder << custom_emoji_size.custom_emoji_id << " on " << custom_emoji_size.background_colors; +StringBuilder &operator<<(StringBuilder &string_builder, const StickerPhotoSize &sticker_photo_size) { + switch (sticker_photo_size.type) { + case StickerPhotoSize::Type::Sticker: + return string_builder << sticker_photo_size.sticker_id << " from " << sticker_photo_size.sticker_set_id << " on " + << sticker_photo_size.background_colors; + case StickerPhotoSize::Type::CustomEmoji: + return string_builder << sticker_photo_size.custom_emoji_id << " on " << sticker_photo_size.background_colors; + default: + UNREACHABLE(); + return string_builder; + } } } // namespace td diff --git a/td/telegram/PhotoSize.h b/td/telegram/PhotoSize.h index f627e986f..0ede40d51 100644 --- a/td/telegram/PhotoSize.h +++ b/td/telegram/PhotoSize.h @@ -14,6 +14,7 @@ #include "td/telegram/net/DcId.h" #include "td/telegram/PhotoFormat.h" #include "td/telegram/PhotoSizeSource.h" +#include "td/telegram/StickerSetId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -25,6 +26,7 @@ namespace td { class FileManager; +class Td; struct PhotoSize { int32 type = 0; @@ -38,8 +40,12 @@ struct AnimationSize final : public PhotoSize { double main_frame_timestamp = 0.0; }; -struct CustomEmojiSize { +struct StickerPhotoSize { + enum class Type : int32 { Sticker, CustomEmoji }; + Type type = Type::CustomEmoji; CustomEmojiId custom_emoji_id; + StickerSetId sticker_set_id; + int64 sticker_id = 0; vector background_colors; }; @@ -59,14 +65,14 @@ Variant get_photo_size(FileManager *file_manager, PhotoSizeSo DialogId owner_dialog_id, tl_object_ptr &&size_ptr, PhotoFormat format); -Variant 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_ptr); +Variant get_animation_size(Td *td, PhotoSizeSource source, int64 id, int64 access_hash, + string file_reference, DcId dc_id, DialogId owner_dialog_id, + tl_object_ptr &&size_ptr); -CustomEmojiSize get_custom_emoji_size(const td_api::object_ptr &custom_emoji); +StickerPhotoSize get_sticker_photo_size(Td *td, const td_api::object_ptr &chat_photo_sticker); -telegram_api::object_ptr get_input_video_size_object(const CustomEmojiSize &custom_emoji_size); +telegram_api::object_ptr get_input_video_size_object( + Td *td, const StickerPhotoSize &sticker_photo_size); PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id, tl_object_ptr web_document_ptr); @@ -86,9 +92,9 @@ bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs); StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size); -bool operator==(const CustomEmojiSize &lhs, const CustomEmojiSize &rhs); -bool operator!=(const CustomEmojiSize &lhs, const CustomEmojiSize &rhs); +bool operator==(const StickerPhotoSize &lhs, const StickerPhotoSize &rhs); +bool operator!=(const StickerPhotoSize &lhs, const StickerPhotoSize &rhs); -StringBuilder &operator<<(StringBuilder &string_builder, const CustomEmojiSize &custom_emoji_size); +StringBuilder &operator<<(StringBuilder &string_builder, const StickerPhotoSize &sticker_photo_size); } // namespace td diff --git a/td/telegram/PhotoSize.hpp b/td/telegram/PhotoSize.hpp index 918a0575d..ffcee8a9e 100644 --- a/td/telegram/PhotoSize.hpp +++ b/td/telegram/PhotoSize.hpp @@ -9,6 +9,7 @@ #include "td/telegram/Dimensions.hpp" #include "td/telegram/files/FileId.hpp" #include "td/telegram/PhotoSize.h" +#include "td/telegram/StickerSetId.hpp" #include "td/telegram/Version.h" #include "td/utils/tl_helpers.h" @@ -58,15 +59,41 @@ void parse(AnimationSize &animation_size, ParserT &parser) { } template -void store(const CustomEmojiSize &custom_emoji_size, StorerT &storer) { - store(custom_emoji_size.custom_emoji_id, storer); - store(custom_emoji_size.background_colors, storer); +void store(const StickerPhotoSize &sticker_photo_size, StorerT &storer) { + bool is_custom_emoji = sticker_photo_size.type == StickerPhotoSize::Type::CustomEmoji; + bool is_sticker = sticker_photo_size.type == StickerPhotoSize::Type::Sticker; + BEGIN_STORE_FLAGS(); + STORE_FLAG(is_custom_emoji); + STORE_FLAG(is_sticker); + END_STORE_FLAGS(); + if (is_custom_emoji) { + store(sticker_photo_size.custom_emoji_id, storer); + } else if (is_sticker) { + store(sticker_photo_size.sticker_set_id, storer); + store(sticker_photo_size.sticker_id, storer); + } + store(sticker_photo_size.background_colors, storer); } template -void parse(CustomEmojiSize &custom_emoji_size, ParserT &parser) { - parse(custom_emoji_size.custom_emoji_id, parser); - parse(custom_emoji_size.background_colors, parser); +void parse(StickerPhotoSize &sticker_photo_size, ParserT &parser) { + bool is_custom_emoji; + bool is_sticker; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(is_custom_emoji); + PARSE_FLAG(is_sticker); + END_PARSE_FLAGS(); + if (is_custom_emoji) { + sticker_photo_size.type = StickerPhotoSize::Type::CustomEmoji; + parse(sticker_photo_size.custom_emoji_id, parser); + } else if (is_sticker) { + sticker_photo_size.type = StickerPhotoSize::Type::Sticker; + parse(sticker_photo_size.sticker_set_id, parser); + parse(sticker_photo_size.sticker_id, parser); + } else { + UNREACHABLE(); + } + parse(sticker_photo_size.background_colors, parser); } } // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index b836ce725..22804959f 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6992,7 +6992,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.custom_emoji_, request.is_public_, std::move(promise)); + contacts_manager_->set_profile_photo(request.photo_, request.sticker_, request.is_public_, std::move(promise)); } void Td::on_request(uint64 id, const td_api::deleteProfilePhoto &request) { diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index 7cddd3a2c..17b352926 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -492,7 +492,7 @@ WebPageId WebPagesManager::on_get_web_page(tl_object_ptr page->site_name = std::move(web_page->site_name_); page->title = std::move(web_page->title_); page->description = std::move(web_page->description_); - page->photo = get_photo(td_->file_manager_.get(), std::move(web_page->photo_), owner_dialog_id); + page->photo = get_photo(td_, std::move(web_page->photo_), owner_dialog_id); page->embed_url = std::move(web_page->embed_url_); page->embed_type = std::move(web_page->embed_type_); page->embed_dimensions = get_dimensions(web_page->embed_width_, web_page->embed_height_, "webPage"); @@ -1415,7 +1415,7 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_ CHECK(page != nullptr); FlatHashMap> photos; for (auto &photo_ptr : page->photos_) { - Photo photo = get_photo(td_->file_manager_.get(), std::move(photo_ptr), owner_dialog_id); + Photo photo = get_photo(td_, std::move(photo_ptr), owner_dialog_id); if (photo.is_empty() || photo.id.get() == 0) { LOG(ERROR) << "Receive empty photo in web page instant view for " << web_page->url; } else { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index f551573c1..e55124f7d 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -872,6 +872,27 @@ class CliClient final : public Actor { } } + struct ChatPhotoStickerType { + int64 sticker_set_id = 0; + int64 sticker_id = 0; + + operator td_api::object_ptr() const { + if (sticker_set_id != 0) { + return td_api::make_object(sticker_set_id, sticker_id); + } else { + return td_api::make_object(sticker_id); + } + } + }; + + void get_args(string &args, ChatPhotoStickerType &arg) const { + string sticker_set_id; + string sticker_id; + std::tie(sticker_set_id, sticker_id) = split(args, get_delimiter(args)); + arg.sticker_set_id = to_integer(sticker_set_id); + arg.sticker_id = to_integer(sticker_id); + } + template void get_args(string &args, FirstType &first_arg, SecondType &second_arg, Types &...other_args) const { string arg; @@ -4876,25 +4897,25 @@ class CliClient final : public Actor { td_api::make_object(td_api::make_object( as_input_file(animation), to_double(main_frame_timestamp)), nullptr, op == "sppaf")); - } else if (op == "spppce" || op == "spppcef") { + } else if (op == "sppps" || op == "spppsf") { string photo; - int64 custom_emoji_id; - get_args(args, photo, custom_emoji_id); + ChatPhotoStickerType type; + get_args(args, photo, type); 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)), + td_api::make_object(type, + td_api::make_object(0x7FFFFFFF)), op == "spppcef")); - } else if (op == "sppace" || op == "sppacef") { + } else if (op == "sppas" || op == "sppasf") { string animation; string main_frame_timestamp; - int64 custom_emoji_id; - get_args(args, animation, main_frame_timestamp, custom_emoji_id); + ChatPhotoStickerType type; + get_args(args, animation, main_frame_timestamp, type); 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)), + td_api::make_object(type, + td_api::make_object(0x7FFFFFFF)), op == "sppacef")); } else if (op == "suppp") { UserId user_id;