diff --git a/build.html b/build.html
index b9af48310..e16968de2 100644
--- a/build.html
+++ b/build.html
@@ -734,7 +734,7 @@ function onOptionsChanged() {
pre_text.push('Install Git, ' + compiler + ', make, CMake >= 3.0.2, OpenSSL-dev, zlib-dev, gperf, PHP' + jdk + ' using your package manager.');
}
if (os_linux && os.includes('Node.js')) {
- pre_text.push('Note that for Node.js ≤ 9.11.2 you must build TDLight with OpenSSL 1.0.* and for Node.js ≥ 10 with OpenSSL 1.1.* instead, so you may need to modify the following commands to install a proper OpenSSL version.');
+ pre_text.push('Note that for Node.js ≥ 17 you must build TDLib with OpenSSL 3.0.*, for Node.js ≥ 10 with OpenSSL 1.1.*, and for Node.js < 10 with OpenSSL 1.0.*, so you may need to modify the following commands to install a proper OpenSSL version.');
}
if (os_freebsd) {
pre_text.push('Note that the following instruction is for FreeBSD 11.');
diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl
index 9bacc744c..29acb7de8 100644
--- a/td/generate/scheme/td_api.tl
+++ b/td/generate/scheme/td_api.tl
@@ -2353,8 +2353,8 @@ callStateReady protocol:callProtocol servers:vector config:string en
//@description The call is hanging up after discardCall has been called
callStateHangingUp = CallState;
-//@description The call has ended successfully @reason The reason, why the call has ended @need_rating True, if the call rating must be sent to the server @need_debug_information True, if the call debug information must be sent to the server
-callStateDiscarded reason:CallDiscardReason need_rating:Bool need_debug_information:Bool = CallState;
+//@description The call has ended successfully @reason The reason, why the call has ended @need_rating True, if the call rating must be sent to the server @need_debug_information True, if the call debug information must be sent to the server @need_log True, if the call log must be sent to the server
+callStateDiscarded reason:CallDiscardReason need_rating:Bool need_debug_information:Bool need_log:Bool = CallState;
//@description The call has ended with an error @error Error. An error with the code 4005000 will be returned if an outgoing call is missed because of an expired timeout
callStateError error:error = CallState;
@@ -3295,18 +3295,73 @@ userPrivacySettingAllowFindingByPhoneNumber = UserPrivacySetting;
accountTtl days:int32 = AccountTtl;
+//@class SessionType @description Represents the type of a session
+
+//@description The session is running on an Android device
+sessionTypeAndroid = SessionType;
+
+//@description The session is running on a generic Apple device
+sessionTypeApple = SessionType;
+
+//@description The session is running on the Brave browser
+sessionTypeBrave = SessionType;
+
+//@description The session is running on the Chrome browser
+sessionTypeChrome = SessionType;
+
+//@description The session is running on the Edge browser
+sessionTypeEdge = SessionType;
+
+//@description The session is running on the Firefox browser
+sessionTypeFirefox = SessionType;
+
+//@description The session is running on an iPad device
+sessionTypeIpad = SessionType;
+
+//@description The session is running on an iPhone device
+sessionTypeIphone = SessionType;
+
+//@description The session is running on a Linux device
+sessionTypeLinux = SessionType;
+
+//@description The session is running on a Mac device
+sessionTypeMac = SessionType;
+
+//@description The session is running on the Opera browser
+sessionTypeOpera = SessionType;
+
+//@description The session is running on the Safari browser
+sessionTypeSafari = SessionType;
+
+//@description The session is running on an Ubuntu device
+sessionTypeUbuntu = SessionType;
+
+//@description The session is running on an unknown type of device
+sessionTypeUnknown = SessionType;
+
+//@description The session is running on the Vivaldi browser
+sessionTypeVivaldi = SessionType;
+
+//@description The session is running on a Windows device
+sessionTypeWindows = SessionType;
+
+//@description The session is running on an Xbox console
+sessionTypeXbox = SessionType;
+
+
//@description Contains information about one session in a Telegram application used by the current user. Sessions must be shown to the user in the returned order
//@id Session identifier @is_current True, if this session is the current session
//@is_password_pending True, if a password is needed to complete authorization of the session
//@can_accept_secret_chats True, if incoming secret chats can be accepted by the session
//@can_accept_calls True, if incoming calls can be accepted by the session
+//@type Session type based on the system and application version, which can be used to display a corresponding icon
//@api_id Telegram API identifier, as provided by the application @application_name Name of the application, as provided by the application
//@application_version The version of the application, as provided by the application @is_official_application True, if the application is an official application or uses the api_id of an official application
//@device_model Model of the device the application has been run or is running on, as provided by the application @platform Operating system the application has been run or is running on, as provided by the application
//@system_version Version of the operating system the application has been run or is running on, as provided by the application @log_in_date Point in time (Unix timestamp) when the user has logged in
//@last_active_date Point in time (Unix timestamp) when the session was last used @ip IP address from which the session was created, in human-readable format
//@country A two-letter country code for the country from which the session was created, based on the IP address @region Region code from which the session was created, based on the IP address
-session id:int64 is_current:Bool is_password_pending:Bool can_accept_secret_chats:Bool can_accept_calls:Bool api_id:int32 application_name:string application_version:string is_official_application:Bool device_model:string platform:string system_version:string log_in_date:int32 last_active_date:int32 ip:string country:string region:string = Session;
+session id:int64 is_current:Bool is_password_pending:Bool can_accept_secret_chats:Bool can_accept_calls:Bool type:SessionType api_id:int32 application_name:string application_version:string is_official_application:Bool device_model:string platform:string system_version:string log_in_date:int32 last_active_date:int32 ip:string country:string region:string = Session;
//@description Contains a list of sessions @sessions List of sessions @inactive_session_ttl_days Number of days of inactivity before sessions will automatically be terminated; 1-366 days
sessions sessions:vector inactive_session_ttl_days:int32 = Sessions;
@@ -5442,9 +5497,12 @@ discardCall call_id:int32 is_disconnected:Bool duration:int32 is_video:Bool conn
//@description Sends a call rating @call_id Call identifier @rating Call rating; 1-5 @comment An optional user comment if the rating is less than 5 @problems List of the exact types of problems with the call, specified by the user
sendCallRating call_id:int32 rating:int32 comment:string problems:vector = Ok;
-//@description Sends debug information for a call @call_id Call identifier @debug_information Debug information in application-specific format
+//@description Sends debug information for a call to Telegram servers @call_id Call identifier @debug_information Debug information in application-specific format
sendCallDebugInformation call_id:int32 debug_information:string = Ok;
+//@description Sends log file for a call to Telegram servers @call_id Call identifier @log_file Call log file. Only inputFileLocal and inputFileGenerated are supported
+sendCallLog call_id:int32 log_file:InputFile = Ok;
+
//@description Returns list of participant identifiers, on whose behalf a video chat in the chat can be joined @chat_id Chat identifier
getVideoChatAvailableParticipants chat_id:int53 = MessageSenders;
@@ -5610,7 +5668,7 @@ changeImportedContacts contacts:vector = ImportedContacts;
clearImportedContacts = Ok;
-//@description Searches a user by their phone number @phone_number Phone number to search for
+//@description Searches a user by their phone number. Returns a 404 error if the user can't be found @phone_number Phone number to search for
searchUserByPhoneNumber phone_number:string = User;
//@description Shares the phone number of the current user with a mutual contact. Supposed to be called when the user clicks on chatActionBarSharePhoneNumber @user_id Identifier of the user with whom to share the phone number. The user must be a mutual contact
diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl
index 6ecfd1bfd..5d6f3d2b1 100644
--- a/td/generate/scheme/telegram_api.tl
+++ b/td/generate/scheme/telegram_api.tl
@@ -1797,6 +1797,7 @@ phone.joinGroupCallPresentation#cbea6bc4 call:InputGroupCall params:DataJSON = U
phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates;
phone.getGroupCallStreamChannels#1ab21940 call:InputGroupCall = phone.GroupCallStreamChannels;
phone.getGroupCallStreamRtmpUrl#deb3abbf peer:InputPeer revoke:Bool = phone.GroupCallStreamRtmpUrl;
+phone.saveCallLog#41248786 peer:InputPhoneCall file:InputFile = Bool;
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector;
diff --git a/td/telegram/Account.cpp b/td/telegram/Account.cpp
index 24d657ecf..2ccccb973 100644
--- a/td/telegram/Account.cpp
+++ b/td/telegram/Account.cpp
@@ -28,15 +28,85 @@
namespace td {
+static td_api::object_ptr get_session_type_object(
+ const tl_object_ptr &authorization) {
+ auto contains = [](const string &str, const char *substr) {
+ return str.find(substr) != string::npos;
+ };
+
+ const string &app_name = authorization->app_name_;
+ auto device_model = to_lower(authorization->device_model_);
+ auto platform = to_lower(authorization->platform_);
+ auto system_version = to_lower(authorization->system_version_);
+
+ if (device_model.find("xbox") != string::npos) {
+ return td_api::make_object();
+ }
+
+ bool is_web = [&] {
+ CSlice web_name("Web");
+ auto pos = app_name.find(web_name.c_str());
+ if (pos == string::npos) {
+ return false;
+ }
+
+ auto next_character = app_name[pos + web_name.size()];
+ return !('a' <= next_character && next_character <= 'z');
+ }();
+
+ if (is_web) {
+ if (contains(device_model, "brave")) {
+ return td_api::make_object();
+ } else if (contains(device_model, "vivaldi")) {
+ return td_api::make_object();
+ } else if (contains(device_model, "opera") || contains(device_model, "opr")) {
+ return td_api::make_object();
+ } else if (contains(device_model, "edg")) {
+ return td_api::make_object();
+ } else if (contains(device_model, "chrome")) {
+ return td_api::make_object();
+ } else if (contains(device_model, "firefox") || contains(device_model, "fxios")) {
+ return td_api::make_object();
+ } else if (contains(device_model, "safari")) {
+ return td_api::make_object();
+ }
+ }
+
+ if (begins_with(platform, "android") || contains(system_version, "android")) {
+ return td_api::make_object();
+ } else if (begins_with(platform, "windows") || contains(system_version, "windows")) {
+ return td_api::make_object();
+ } else if (begins_with(platform, "ubuntu") || contains(system_version, "ubuntu")) {
+ return td_api::make_object();
+ } else if (begins_with(platform, "linux") || contains(system_version, "linux")) {
+ return td_api::make_object();
+ }
+
+ auto is_ios = begins_with(platform, "ios") || contains(system_version, "ios");
+ auto is_macos = begins_with(platform, "macos") || contains(system_version, "macos");
+ if (is_ios && contains(device_model, "iphone")) {
+ return td_api::make_object();
+ } else if (is_ios && contains(device_model, "ipad")) {
+ return td_api::make_object();
+ } else if (is_macos && contains(device_model, "mac")) {
+ return td_api::make_object();
+ } else if (is_ios || is_macos) {
+ return td_api::make_object();
+ }
+
+ return td_api::make_object();
+}
+
static td_api::object_ptr convert_authorization_object(
tl_object_ptr &&authorization) {
CHECK(authorization != nullptr);
return td_api::make_object(
authorization->hash_, authorization->current_, authorization->password_pending_,
- !authorization->encrypted_requests_disabled_, !authorization->call_requests_disabled_, authorization->api_id_,
- authorization->app_name_, authorization->app_version_, authorization->official_app_, authorization->device_model_,
- authorization->platform_, authorization->system_version_, authorization->date_created_,
- authorization->date_active_, authorization->ip_, authorization->country_, authorization->region_);
+ !authorization->encrypted_requests_disabled_, !authorization->call_requests_disabled_,
+ get_session_type_object(authorization), authorization->api_id_, authorization->app_name_,
+ authorization->app_version_, authorization->official_app_, authorization->device_model_, authorization->platform_,
+ authorization->system_version_, authorization->date_created_, authorization->date_active_, authorization->ip_,
+ authorization->country_, authorization->region_);
}
class SetAccountTtlQuery final : public Td::ResultHandler {
diff --git a/td/telegram/AttachMenuManager.cpp b/td/telegram/AttachMenuManager.cpp
index 17a243076..c5c13f04f 100644
--- a/td/telegram/AttachMenuManager.cpp
+++ b/td/telegram/AttachMenuManager.cpp
@@ -598,7 +598,9 @@ Result AttachMenuManager::get_attach_menu_bot(
auto parsed_document =
td_->documents_manager_->on_get_document(move_tl_object_as(icon->icon_), DialogId());
if (parsed_document.type != expected_document_type) {
- LOG(ERROR) << "Receive wrong attachment menu bot icon \"" << name << "\" for " << user_id;
+ if (user_id != UserId(5000860301) || !G()->is_test_dc() || name != "macos_animated") {
+ LOG(ERROR) << "Receive wrong attachment menu bot icon \"" << name << "\" for " << user_id;
+ }
continue;
}
bool expect_colors = false;
diff --git a/td/telegram/CallActor.cpp b/td/telegram/CallActor.cpp
index e43aeb96e..7e23512dc 100644
--- a/td/telegram/CallActor.cpp
+++ b/td/telegram/CallActor.cpp
@@ -10,6 +10,7 @@
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DhCache.h"
#include "td/telegram/DialogId.h"
+#include "td/telegram/files/FileManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/NetQueryCreator.h"
@@ -128,7 +129,7 @@ tl_object_ptr CallState::get_call_state_object() const {
return make_tl_object();
case Type::Discarded:
return make_tl_object(get_call_discard_reason_object(discard_reason), need_rating,
- need_debug_information);
+ need_debug_information, need_log);
case Type::Error:
CHECK(error.is_error());
return make_tl_object(make_tl_object(error.code(), error.message().str()));
@@ -159,7 +160,7 @@ void CallActor::create_call(UserId user_id, tl_object_ptr promise) {
+void CallActor::accept_call(CallProtocol &&protocol, Promise promise) {
if (state_ != State::SendAcceptQuery) {
return promise.set_error(Status::Error(400, "Unexpected acceptCall"));
}
@@ -180,7 +181,7 @@ void CallActor::update_call_signaling_data(string data) {
send_closure(G()->td(), &Td::send_update, std::move(update));
}
-void CallActor::send_call_signaling_data(string &&data, Promise<> promise) {
+void CallActor::send_call_signaling_data(string &&data, Promise promise) {
if (call_state_.type != CallState::Type::Ready) {
return promise.set_error(Status::Error(400, "Call is not active"));
}
@@ -199,7 +200,7 @@ void CallActor::send_call_signaling_data(string &&data, Promise<> promise) {
}
void CallActor::discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id,
- Promise<> promise) {
+ Promise promise) {
promise.set_value(Unit());
if (state_ == State::Discarded || state_ == State::WaitDiscardResult || state_ == State::SendDiscardQuery) {
return;
@@ -244,7 +245,7 @@ void CallActor::discard_call(bool is_disconnected, int32 duration, bool is_video
}
void CallActor::rate_call(int32 rating, string comment, vector> &&problems,
- Promise<> promise) {
+ Promise promise) {
if (!call_state_.need_rating) {
return promise.set_error(Status::Error(400, "Unexpected sendCallRating"));
}
@@ -309,11 +310,15 @@ void CallActor::on_set_rating_query_result(Result r_net_query) {
if (res.is_error()) {
return on_error(res.move_as_error());
}
- call_state_.need_rating = false;
+ if (call_state_.need_rating) {
+ call_state_.need_rating = false;
+ call_state_need_flush_ = true;
+ loop();
+ }
send_closure(G()->updates_manager(), &UpdatesManager::on_get_updates, res.move_as_ok(), Promise());
}
-void CallActor::send_call_debug_information(string data, Promise<> promise) {
+void CallActor::send_call_debug_information(string data, Promise promise) {
if (!call_state_.need_debug_information) {
return promise.set_error(Status::Error(400, "Unexpected sendCallDebugInformation"));
}
@@ -323,17 +328,148 @@ void CallActor::send_call_debug_information(string data, Promise<> promise) {
auto query = G()->net_query_creator().create(tl_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));
+ send_closure(actor_id, &CallActor::on_save_debug_query_result, std::move(r_net_query));
}));
loop();
}
-void CallActor::on_set_debug_query_result(Result r_net_query) {
+void CallActor::on_save_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());
}
- call_state_.need_debug_information = false;
+ if (!res.ok() && !call_state_.need_log) {
+ call_state_.need_log = true;
+ call_state_need_flush_ = true;
+ }
+ if (call_state_.need_debug_information) {
+ call_state_.need_debug_information = false;
+ call_state_need_flush_ = true;
+ }
+ loop();
+}
+
+void CallActor::send_call_log(td_api::object_ptr log_file, Promise promise) {
+ if (!call_state_.need_log) {
+ return promise.set_error(Status::Error(400, "Unexpected sendCallLog"));
+ }
+ TRY_STATUS_PROMISE(promise, G()->close_status());
+
+ auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
+ auto r_file_id = file_manager->get_input_file_id(FileType::CallLog, log_file, DialogId(), false, false);
+ if (r_file_id.is_error()) {
+ return promise.set_error(Status::Error(400, r_file_id.error().message()));
+ }
+ auto file_id = r_file_id.move_as_ok();
+
+ FileView file_view = file_manager->get_file_view(file_id);
+ if (file_view.is_encrypted()) {
+ return promise.set_error(Status::Error(400, "Can't use encrypted file"));
+ }
+ if (!file_view.has_local_location() && !file_view.has_generate_location()) {
+ return promise.set_error(Status::Error(400, "Need local or generate location to upload call log"));
+ }
+
+ upload_log_file(file_id, std::move(promise));
+}
+
+void CallActor::upload_log_file(FileId file_id, Promise &&promise) {
+ auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
+ auto upload_file_id = file_manager->dup_file_id(file_id);
+ LOG(INFO) << "Ask to upload call log file " << upload_file_id;
+
+ class UploadLogFileCallback final : public FileManager::UploadCallback {
+ ActorId actor_id_;
+ FileId file_id_;
+ Promise promise_;
+
+ public:
+ UploadLogFileCallback(ActorId actor_id, FileId file_id, Promise &&promise)
+ : actor_id_(actor_id), file_id_(file_id), promise_(std::move(promise)) {
+ }
+
+ void on_upload_ok(FileId file_id, tl_object_ptr input_file) final {
+ CHECK(file_id == file_id_);
+ send_closure_later(actor_id_, &CallActor::on_upload_log_file, file_id, std::move(promise_),
+ std::move(input_file));
+ }
+
+ void on_upload_encrypted_ok(FileId file_id, tl_object_ptr input_file) final {
+ UNREACHABLE();
+ }
+
+ void on_upload_secure_ok(FileId file_id, tl_object_ptr input_file) final {
+ UNREACHABLE();
+ }
+
+ void on_upload_error(FileId file_id, Status error) final {
+ CHECK(file_id == file_id_);
+ send_closure_later(actor_id_, &CallActor::on_upload_log_file_error, file_id, std::move(promise_),
+ std::move(error));
+ }
+ };
+
+ file_manager->upload(upload_file_id,
+ std::make_shared(actor_id(this), upload_file_id, std::move(promise)), 1,
+ 0);
+}
+
+void CallActor::on_upload_log_file(FileId file_id, Promise &&promise,
+ tl_object_ptr input_file) {
+ LOG(INFO) << "Log file " << file_id << " has been uploaded";
+ TRY_STATUS_PROMISE(promise, G()->close_status());
+
+ do_upload_log_file(file_id, std::move(input_file), std::move(promise));
+}
+
+void CallActor::on_upload_log_file_error(FileId file_id, Promise &&promise, Status status) {
+ LOG(WARNING) << "Log file " << file_id << " has upload error " << status;
+ CHECK(status.is_error());
+
+ TRY_STATUS_PROMISE(promise, G()->close_status());
+
+ promise.set_error(Status::Error(status.code() > 0 ? status.code() : 500,
+ status.message())); // TODO CHECK that status has always a code
+}
+
+void CallActor::do_upload_log_file(FileId file_id, tl_object_ptr &&input_file,
+ Promise &&promise) {
+ if (input_file == nullptr) {
+ return promise.set_error(Status::Error(500, "Failed to reupload call log"));
+ }
+
+ auto tl_query = telegram_api::phone_saveCallLog(get_input_phone_call("do_upload_log_file"), std::move(input_file));
+ send_with_promise(G()->net_query_creator().create(tl_query),
+ PromiseCreator::lambda([actor_id = actor_id(this), file_id,
+ promise = std::move(promise)](Result r_net_query) mutable {
+ send_closure(actor_id, &CallActor::on_save_log_query_result, file_id, std::move(promise),
+ std::move(r_net_query));
+ }));
+ loop();
+}
+
+void CallActor::on_save_log_query_result(FileId file_id, Promise promise, Result r_net_query) {
+ TRY_STATUS_PROMISE(promise, G()->close_status());
+
+ auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
+ file_manager->delete_partial_remote_location(file_id);
+ file_manager->cancel_upload(file_id);
+
+ auto res = fetch_result(std::move(r_net_query));
+ if (res.is_error()) {
+ auto error = res.move_as_error();
+ if (begins_with(error.message(), "FILE_PART_") && ends_with(error.message(), "_MISSING")) {
+ // TODO on_upload_log_file_part_missing(file_id, to_integer(error.message().substr(10)));
+ // return;
+ }
+ return promise.set_error(std::move(error));
+ }
+ if (call_state_.need_log) {
+ call_state_.need_log = false;
+ call_state_need_flush_ = true;
+ }
+ loop();
+ promise.set_value(Unit());
}
// Requests
@@ -810,7 +946,7 @@ void CallActor::loop() {
break;
case State::Discarded: {
if (call_state_.type == CallState::Type::Discarded &&
- (call_state_.need_rating || call_state_.need_debug_information)) {
+ (call_state_.need_rating || call_state_.need_debug_information || call_state_.need_log)) {
break;
}
LOG(INFO) << "Close " << local_call_id_;
diff --git a/td/telegram/CallActor.h b/td/telegram/CallActor.h
index e2f70b844..0c936099b 100644
--- a/td/telegram/CallActor.h
+++ b/td/telegram/CallActor.h
@@ -9,6 +9,7 @@
#include "td/telegram/CallDiscardReason.h"
#include "td/telegram/CallId.h"
#include "td/telegram/DhConfig.h"
+#include "td/telegram/files/FileId.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@@ -76,6 +77,7 @@ struct CallState {
bool is_received{false};
bool need_debug_information{false};
bool need_rating{false};
+ bool need_log{false};
int64 key_fingerprint{0};
string key;
@@ -94,13 +96,14 @@ class CallActor final : public NetQueryCallback {
void create_call(UserId user_id, tl_object_ptr &&input_user, CallProtocol &&protocol,
bool is_video, Promise &&promise);
- void accept_call(CallProtocol &&protocol, Promise<> promise);
+ void accept_call(CallProtocol &&protocol, Promise promise);
void update_call_signaling_data(string data);
- void send_call_signaling_data(string &&data, Promise<> promise);
- void discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id, Promise<> promise);
+ void send_call_signaling_data(string &&data, Promise promise);
+ void discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id, Promise promise);
void rate_call(int32 rating, string comment, vector> &&problems,
- Promise<> promise);
- void send_call_debug_information(string data, Promise<> promise);
+ Promise promise);
+ void send_call_debug_information(string data, Promise promise);
+ void send_call_log(td_api::object_ptr log_file, Promise promise);
void update_call(tl_object_ptr call);
@@ -182,7 +185,18 @@ class CallActor final : public NetQueryCallback {
void on_call_discarded(CallDiscardReason reason, bool need_rating, bool need_debug, bool is_video);
void on_set_rating_query_result(Result r_net_query);
- void on_set_debug_query_result(Result r_net_query);
+
+ void on_save_debug_query_result(Result r_net_query);
+
+ void upload_log_file(FileId file_id, Promise &&promise);
+
+ void on_upload_log_file(FileId file_id, Promise &&promise, tl_object_ptr input_file);
+
+ void on_upload_log_file_error(FileId file_id, Promise &&promise, Status status);
+
+ void do_upload_log_file(FileId file_id, tl_object_ptr &&input_file, Promise &&promise);
+
+ void on_save_log_query_result(FileId file_id, Promise promise, Result r_net_query);
void on_get_call_config_result(Result r_net_query);
diff --git a/td/telegram/CallManager.cpp b/td/telegram/CallManager.cpp
index 92c3abe8e..702655775 100644
--- a/td/telegram/CallManager.cpp
+++ b/td/telegram/CallManager.cpp
@@ -70,7 +70,7 @@ void CallManager::create_call(UserId user_id, tl_object_ptr promise) {
+void CallManager::accept_call(CallId call_id, CallProtocol &&protocol, Promise promise) {
auto actor = get_call_actor(call_id);
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
@@ -78,7 +78,7 @@ void CallManager::accept_call(CallId call_id, CallProtocol &&protocol, Promise<>
send_closure(actor, &CallActor::accept_call, std::move(protocol), std::move(promise));
}
-void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promise<> promise) {
+void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promise promise) {
auto actor = get_call_actor(call_id);
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
@@ -87,7 +87,7 @@ void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promis
}
void CallManager::discard_call(CallId call_id, bool is_disconnected, int32 duration, bool is_video, int64 connection_id,
- Promise<> promise) {
+ Promise promise) {
auto actor = get_call_actor(call_id);
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
@@ -96,7 +96,7 @@ void CallManager::discard_call(CallId call_id, bool is_disconnected, int32 durat
}
void CallManager::rate_call(CallId call_id, int32 rating, string comment,
- vector> &&problems, Promise<> promise) {
+ vector> &&problems, Promise promise) {
auto actor = get_call_actor(call_id);
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
@@ -104,7 +104,7 @@ void CallManager::rate_call(CallId call_id, int32 rating, string comment,
send_closure(actor, &CallActor::rate_call, rating, std::move(comment), std::move(problems), std::move(promise));
}
-void CallManager::send_call_debug_information(CallId call_id, string data, Promise<> promise) {
+void CallManager::send_call_debug_information(CallId call_id, string data, Promise promise) {
auto actor = get_call_actor(call_id);
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
@@ -112,6 +112,14 @@ void CallManager::send_call_debug_information(CallId call_id, string data, Promi
send_closure(actor, &CallActor::send_call_debug_information, std::move(data), std::move(promise));
}
+void CallManager::send_call_log(CallId call_id, td_api::object_ptr log_file, Promise promise) {
+ auto actor = get_call_actor(call_id);
+ if (actor.empty()) {
+ return promise.set_error(Status::Error(400, "Call not found"));
+ }
+ send_closure(actor, &CallActor::send_call_log, std::move(log_file), std::move(promise));
+}
+
CallId CallManager::create_call_actor() {
if (next_call_id_ == std::numeric_limits::max()) {
next_call_id_ = 1;
diff --git a/td/telegram/CallManager.h b/td/telegram/CallManager.h
index 98923f3c6..2430fdc65 100644
--- a/td/telegram/CallManager.h
+++ b/td/telegram/CallManager.h
@@ -29,13 +29,20 @@ class CallManager final : public Actor {
void create_call(UserId user_id, tl_object_ptr &&input_user, CallProtocol &&protocol,
bool is_video, Promise promise);
- void accept_call(CallId call_id, CallProtocol &&protocol, Promise<> promise);
- void send_call_signaling_data(CallId call_id, string &&data, Promise<> promise);
+
+ void accept_call(CallId call_id, CallProtocol &&protocol, Promise promise);
+
+ void send_call_signaling_data(CallId call_id, string &&data, Promise promise);
+
void discard_call(CallId call_id, bool is_disconnected, int32 duration, bool is_video, int64 connection_id,
- Promise<> promise);
+ Promise promise);
+
void rate_call(CallId call_id, int32 rating, string comment,
- vector> &&problems, Promise<> promise);
- void send_call_debug_information(CallId call_id, string data, Promise<> promise);
+ vector> &&problems, Promise promise);
+
+ void send_call_debug_information(CallId call_id, string data, Promise promise);
+
+ void send_call_log(CallId call_id, td_api::object_ptr log_file, Promise promise);
private:
bool close_flag_ = false;
diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp
index de7647377..f74c8180c 100644
--- a/td/telegram/ContactsManager.cpp
+++ b/td/telegram/ContactsManager.cpp
@@ -3318,6 +3318,11 @@ ContactsManager::~ContactsManager() = default;
void ContactsManager::tear_down() {
parent_.reset();
+
+ LOG(DEBUG) << "Have " << users_.size() << " users, " << chats_.size() << " basic groups, " << channels_.size()
+ << " supergroups and " << secret_chats_.size() << " secret chats to free";
+ LOG(DEBUG) << "Have " << users_full_.size() << " full users, " << chats_full_.size() << " full basic groups and "
+ << channels_full_.size() << " full supergroups to free";
}
UserId ContactsManager::load_my_id() {
@@ -4657,20 +4662,26 @@ tl_object_ptr ContactsManager::get_input_encry
return make_tl_object(secret_chat_id.get(), sc->access_hash);
}
+void ContactsManager::apply_pending_user_photo(User *u, UserId user_id) {
+ if (u == nullptr || u->is_photo_inited) {
+ return;
+ }
+
+ auto it = pending_user_photos_.find(user_id);
+ if (it != pending_user_photos_.end()) {
+ do_update_user_photo(u, user_id, std::move(it->second), "get_user_dialog_photo");
+ pending_user_photos_.erase(it);
+ update_user(u, user_id);
+ }
+}
+
const DialogPhoto *ContactsManager::get_user_dialog_photo(UserId user_id) {
auto u = get_user(user_id);
if (u == nullptr) {
return nullptr;
}
- if (!u->is_photo_inited) {
- auto it = pending_user_photos_.find(user_id);
- if (it != pending_user_photos_.end()) {
- do_update_user_photo(u, user_id, std::move(it->second), "get_user_dialog_photo");
- pending_user_photos_.erase(it);
- update_user(u, user_id);
- }
- }
+ apply_pending_user_photo(u, user_id);
return &u->photo;
}
@@ -5287,7 +5298,7 @@ std::pair, vector> ContactsManager::import_contacts(const
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || imported_contacts_.find(random_id) != imported_contacts_.end());
+ } while (random_id == 0 || imported_contacts_.count(random_id) > 0);
imported_contacts_[random_id]; // reserve place for result
do_import_contacts(contacts, random_id, std::move(promise));
@@ -8378,6 +8389,14 @@ ChannelId ContactsManager::get_channel_id(const tl_object_ptr &chat) {
+ auto channel_id = get_channel_id(chat);
+ if (channel_id.is_valid()) {
+ return DialogId(channel_id);
+ }
+ return DialogId(get_chat_id(chat));
+}
+
void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, const char *source, bool is_me,
bool expect_support) {
LOG(DEBUG) << "Receive from " << source << ' ' << to_string(user_ptr);
@@ -9808,18 +9827,18 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val
}
}
- 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, DialogId(chat_id)).small_file_id)
- .get_unique_file_id()) {
+ if (!is_same_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), chat_full->photo, c->photo)) {
chat_full->photo = Photo();
if (c->photo.small_file_id.is_valid()) {
reload_chat_full(chat_id, Auto());
}
}
- td_->group_call_manager_->on_update_dialog_about(DialogId(chat_id), chat_full->description, false);
+ auto photo = std::move(chat_full->photo);
+ chat_full->photo = Photo();
+ on_update_chat_full_photo(chat_full, chat_id, std::move(photo));
- on_update_chat_full_photo(chat_full, chat_id, std::move(chat_full->photo));
+ td_->group_call_manager_->on_update_dialog_about(DialogId(chat_id), chat_full->description, false);
chat_full->is_update_chat_full_sent = true;
update_chat_full(chat_full, chat_id, "on_load_chat_full_from_database", true);
@@ -9919,15 +9938,14 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s
}
}
- 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, DialogId(channel_id)).small_file_id)
- .get_unique_file_id()) {
+ if (!is_same_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), channel_full->photo, c->photo)) {
channel_full->photo = Photo();
if (c->photo.small_file_id.is_valid()) {
channel_full->expires_at = 0.0;
}
}
auto photo = std::move(channel_full->photo);
+ channel_full->photo = Photo();
on_update_channel_full_photo(channel_full, channel_id, std::move(photo));
if (channel_full->participant_count < channel_full->administrator_count) {
@@ -10113,10 +10131,20 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo
void ContactsManager::update_chat(Chat *c, ChatId chat_id, bool from_binlog, bool from_database) {
CHECK(c != nullptr);
+ bool need_update_chat_full = false;
if (c->is_photo_changed) {
td_->messages_manager_->on_dialog_photo_updated(DialogId(chat_id));
- drop_chat_photos(chat_id, !c->photo.small_file_id.is_valid(), true, "update_chat");
c->is_photo_changed = false;
+
+ auto chat_full = get_chat_full(chat_id); // must not load ChatFull
+ if (chat_full != nullptr &&
+ !is_same_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), chat_full->photo, c->photo)) {
+ on_update_chat_full_photo(chat_full, chat_id, Photo());
+ need_update_chat_full = true;
+ if (c->photo.small_file_id.is_valid()) {
+ reload_chat_full(chat_id, Auto());
+ }
+ }
}
if (c->is_title_changed) {
td_->messages_manager_->on_dialog_title_updated(DialogId(chat_id));
@@ -10168,14 +10196,34 @@ void ContactsManager::update_chat(Chat *c, ChatId chat_id, bool from_binlog, boo
LOG(INFO) << "Repairing cache of " << chat_id;
reload_chat(chat_id, Promise());
}
+
+ if (need_update_chat_full) {
+ auto chat_full = get_chat_full(chat_id);
+ CHECK(chat_full != nullptr);
+ update_chat_full(chat_full, chat_id, "drop_chat_photos");
+ }
}
void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from_binlog, bool from_database) {
CHECK(c != nullptr);
+ bool need_update_channel_full = false;
if (c->is_photo_changed) {
td_->messages_manager_->on_dialog_photo_updated(DialogId(channel_id));
- drop_channel_photos(channel_id, !c->photo.small_file_id.is_valid(), true, "update_channel");
c->is_photo_changed = false;
+
+ auto channel_full = get_channel_full(channel_id, true, "update_channel");
+ if (channel_full != nullptr &&
+ !is_same_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), channel_full->photo, c->photo)) {
+ on_update_channel_full_photo(channel_full, channel_id, Photo());
+ need_update_channel_full = true;
+ if (c->photo.small_file_id.is_valid()) {
+ if (channel_full->expires_at > 0.0) {
+ channel_full->expires_at = 0.0;
+ channel_full->need_save_to_database = true;
+ }
+ send_get_channel_full_query(channel_full, channel_id, Auto(), "update_channel");
+ }
+ }
}
if (c->is_title_changed) {
td_->messages_manager_->on_dialog_title_updated(DialogId(channel_id));
@@ -10283,6 +10331,12 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from
LOG(INFO) << "Repairing cache of " << channel_id;
reload_channel(channel_id, Promise());
}
+
+ if (need_update_channel_full) {
+ auto channel_full = get_channel_full(channel_id, true, "update_channel");
+ CHECK(channel_full != nullptr);
+ update_channel_full(channel_full, channel_id, "update_channel");
+ }
}
void ContactsManager::update_secret_chat(SecretChat *c, SecretChatId secret_chat_id, bool from_binlog,
@@ -10494,6 +10548,8 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u
return;
}
+ apply_pending_user_photo(u, user_id);
+
td_->messages_manager_->on_update_dialog_notify_settings(DialogId(user_id), std::move(user->notify_settings_),
"on_get_user_full");
@@ -10753,16 +10809,17 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c
return promise.set_value(Unit());
}
+ Chat *c = get_chat(chat_id);
+ if (c == nullptr) {
+ LOG(ERROR) << "Can't find " << chat_id;
+ return promise.set_value(Unit());
+ }
{
MessageId pinned_message_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) {
- LOG(ERROR) << "Can't find " << chat_id;
- return promise.set_value(Unit());
- } else if (c->version >= c->pinned_message_version) {
+ if (c->version >= c->pinned_message_version) {
LOG(INFO) << "Receive pinned " << pinned_message_id << " in " << chat_id << " with version " << c->version
<< ". Current version is " << c->pinned_message_version;
td_->messages_manager_->on_update_dialog_last_pinned_message_id(DialogId(chat_id), pinned_message_id);
@@ -10809,8 +10866,10 @@ 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_));
- on_update_chat_full_photo(chat_full, chat_id,
- get_photo(td_->file_manager_.get(), std::move(chat->chat_photo_), DialogId(chat_id)));
+ auto photo = get_photo(td_->file_manager_.get(), 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, as_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), 0, photo));
+ on_update_chat_full_photo(chat_full, chat_id, std::move(photo));
if (chat_full->description != chat->about_) {
chat_full->description = std::move(chat->about_);
chat_full->is_changed = true;
@@ -10840,6 +10899,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c
}
chat_full->is_update_chat_full_sent = true;
+ update_chat(c, chat_id);
update_chat_full(chat_full, chat_id, "on_get_chat_full");
} else {
CHECK(chat_full_ptr->get_id() == telegram_api::channelFull::ID);
@@ -10975,9 +11035,10 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c
channel_full->need_save_to_database = true;
}
- on_update_channel_full_photo(
- channel_full, channel_id,
- get_photo(td_->file_manager_.get(), std::move(channel->chat_photo_), DialogId(channel_id)));
+ auto photo = get_photo(td_->file_manager_.get(), 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, as_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), c->access_hash, photo));
+ on_update_channel_full_photo(channel_full, channel_id, std::move(photo));
td_->messages_manager_->on_read_channel_outbox(channel_id,
MessageId(ServerMessageId(channel->read_outbox_max_id_)));
@@ -11108,6 +11169,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c
}
channel_full->is_update_channel_full_sent = true;
+ update_channel(c, channel_id);
update_channel_full(channel_full, channel_id, "on_get_channel_full");
if (linked_channel_id.is_valid()) {
@@ -11296,7 +11358,7 @@ void ContactsManager::do_update_user_photo(User *u, UserId user_id,
void ContactsManager::do_update_user_photo(User *u, UserId user_id, ProfilePhoto &&new_photo,
bool invalidate_photo_cache, const char *source) {
u->is_photo_inited = true;
- if (new_photo != u->photo) {
+ if (need_update_profile_photo(u->photo, new_photo)) {
LOG_IF(ERROR, u->access_hash == -1 && new_photo.small_file_id.is_valid())
<< "Update profile photo of " << user_id << " without access hash from " << source;
u->photo = new_photo;
@@ -12579,26 +12641,6 @@ void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId
update_channel_full(channel_full, channel_id, "speculative_add_channel_user");
}
-void ContactsManager::drop_channel_photos(ChannelId channel_id, bool is_empty, bool drop_channel_full_photo,
- const char *source) {
- if (drop_channel_full_photo) {
- auto channel_full = get_channel_full(channel_id, true, "drop_channel_photos"); // must not load ChannelFull
- if (channel_full == nullptr) {
- return;
- }
-
- on_update_channel_full_photo(channel_full, channel_id, Photo());
- if (!is_empty) {
- if (channel_full->expires_at > 0.0) {
- channel_full->expires_at = 0.0;
- channel_full->need_save_to_database = true;
- }
- send_get_channel_full_query(channel_full, channel_id, Auto(), "drop_channel_photos");
- }
- update_channel_full(channel_full, channel_id, "drop_channel_photos");
- }
-}
-
void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool need_drop_slow_mode_delay) {
LOG(INFO) << "Invalidate supergroup full for " << channel_id;
auto channel_full = get_channel_full(channel_id, true, "invalidate_channel_full"); // must not load ChannelFull
@@ -12632,9 +12674,6 @@ void ContactsManager::on_update_chat_full_photo(ChatFull *chat_full, ChatId chat
chat_full->photo = std::move(photo);
chat_full->is_changed = true;
}
- if (chat_full->photo.is_empty()) {
- drop_chat_photos(chat_id, true, false, "on_update_chat_full_photo");
- }
auto photo_file_ids = photo_get_file_ids(chat_full->photo);
if (chat_full->registered_photo_file_ids == photo_file_ids) {
@@ -12669,9 +12708,6 @@ void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, Ch
channel_full->photo = std::move(photo);
channel_full->is_changed = true;
}
- if (channel_full->photo.is_empty()) {
- drop_channel_photos(channel_id, true, false, "on_update_channel_full_photo");
- }
auto photo_file_ids = photo_get_file_ids(channel_full->photo);
if (channel_full->registered_photo_file_ids == photo_file_ids) {
@@ -12950,7 +12986,7 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link,
LOG(ERROR) << "Receive invalid " << channel_id;
channel_id = ChannelId();
}
- if (!channel_id.is_valid() || accessible_before < 0) {
+ if (accessible_before != 0 && (!channel_id.is_valid() || accessible_before < 0)) {
LOG(ERROR) << "Receive expires = " << accessible_before << " for invite link " << invite_link << " to "
<< to_string(chat);
accessible_before = 0;
@@ -13449,14 +13485,16 @@ void ContactsManager::on_update_chat_participant_count(Chat *c, ChatId chat_id,
void ContactsManager::on_update_chat_photo(Chat *c, ChatId chat_id,
tl_object_ptr &&chat_photo_ptr) {
- DialogPhoto new_chat_photo =
- get_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), 0, std::move(chat_photo_ptr));
+ on_update_chat_photo(c, get_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), 0, std::move(chat_photo_ptr)));
+}
+
+void ContactsManager::on_update_chat_photo(Chat *c, DialogPhoto &&photo) {
if (td_->auth_manager_->is_bot()) {
- new_chat_photo.minithumbnail.clear();
+ photo.minithumbnail.clear();
}
- if (new_chat_photo != c->photo) {
- c->photo = new_chat_photo;
+ if (need_update_dialog_photo(c->photo, photo)) {
+ c->photo = std::move(photo);
c->is_photo_changed = true;
c->need_save_to_database = true;
}
@@ -13555,25 +13593,9 @@ void ContactsManager::on_update_chat_full_participants(ChatFull *chat_full, Chat
update_chat_online_member_count(chat_full, chat_id, true);
}
-void ContactsManager::drop_chat_photos(ChatId chat_id, bool is_empty, bool drop_chat_full_photo, const char *source) {
- if (drop_chat_full_photo) {
- auto chat_full = get_chat_full(chat_id); // must not load ChatFull
- if (chat_full == nullptr) {
- return;
- }
-
- on_update_chat_full_photo(chat_full, chat_id, Photo());
- if (!is_empty) {
- reload_chat_full(chat_id, Auto());
- }
- update_chat_full(chat_full, chat_id, "drop_chat_photos");
- }
-}
-
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");
return;
}
@@ -13591,14 +13613,17 @@ void ContactsManager::drop_chat_full(ChatId chat_id) {
void ContactsManager::on_update_channel_photo(Channel *c, ChannelId channel_id,
tl_object_ptr &&chat_photo_ptr) {
- DialogPhoto new_chat_photo =
- get_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), c->access_hash, std::move(chat_photo_ptr));
+ on_update_channel_photo(
+ c, get_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), c->access_hash, std::move(chat_photo_ptr)));
+}
+
+void ContactsManager::on_update_channel_photo(Channel *c, DialogPhoto &&photo) {
if (td_->auth_manager_->is_bot()) {
- new_chat_photo.minithumbnail.clear();
+ photo.minithumbnail.clear();
}
- if (new_chat_photo != c->photo) {
- c->photo = new_chat_photo;
+ if (need_update_dialog_photo(c->photo, photo)) {
+ c->photo = std::move(photo);
c->is_photo_changed = true;
c->need_save_to_database = true;
}
@@ -14358,7 +14383,7 @@ std::pair> ContactsManager::get_user_profile_photos
return result;
}
- get_user_dialog_photo(user_id); // apply pending user photo
+ apply_pending_user_photo(get_user(user_id), user_id);
auto user_photos = &user_photos_[user_id];
if (user_photos->getting_now) {
diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h
index 906d64c04..bf4d7d893 100644
--- a/td/telegram/ContactsManager.h
+++ b/td/telegram/ContactsManager.h
@@ -80,6 +80,7 @@ class ContactsManager final : public Actor {
static UserId get_user_id(const tl_object_ptr &user);
static ChatId get_chat_id(const tl_object_ptr &chat);
static ChannelId get_channel_id(const tl_object_ptr &chat);
+ static DialogId get_dialog_id(const tl_object_ptr &chat);
Result> get_input_user(UserId user_id) const;
@@ -1229,6 +1230,7 @@ class ContactsManager final : public Actor {
const char *source);
void do_update_user_photo(User *u, UserId user_id, ProfilePhoto &&new_photo, bool invalidate_photo_cache,
const char *source);
+ void apply_pending_user_photo(User *u, UserId user_id);
void upload_profile_photo(FileId file_id, bool is_animation, double main_frame_timestamp, Promise &&promise,
int reupload_count = 0, vector bad_parts = {});
@@ -1258,6 +1260,7 @@ class ContactsManager final : public Actor {
void on_update_chat_participant_count(Chat *c, ChatId chat_id, int32 participant_count, int32 version,
const string &debug_str);
void on_update_chat_photo(Chat *c, ChatId chat_id, tl_object_ptr &&chat_photo_ptr);
+ void on_update_chat_photo(Chat *c, DialogPhoto &&photo);
static void on_update_chat_title(Chat *c, ChatId chat_id, string &&title);
static void on_update_chat_active(Chat *c, ChatId chat_id, bool is_active);
static void on_update_chat_migrated_to_channel_id(Chat *c, ChatId chat_id, ChannelId migrated_to_channel_id);
@@ -1272,6 +1275,7 @@ class ContactsManager final : public Actor {
void on_update_channel_photo(Channel *c, ChannelId channel_id,
tl_object_ptr &&chat_photo_ptr);
+ void on_update_channel_photo(Channel *c, DialogPhoto &&photo);
static void on_update_channel_title(Channel *c, ChannelId channel_id, string &&title);
void on_update_channel_username(Channel *c, ChannelId channel_id, string &&username);
void on_update_channel_status(Channel *c, ChannelId channel_id, DialogParticipantStatus &&status);
@@ -1310,11 +1314,8 @@ class ContactsManager final : public Actor {
void speculative_add_channel_user(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &new_status,
const DialogParticipantStatus &old_status);
- void drop_chat_photos(ChatId chat_id, bool is_empty, bool drop_chat_full_photo, const char *source);
void drop_chat_full(ChatId chat_id);
- void drop_channel_photos(ChannelId channel_id, bool is_empty, bool drop_channel_full_photo, const char *source);
-
void do_invalidate_channel_full(ChannelFull *channel_full, ChannelId channel_id, bool need_drop_slow_mode_delay);
void update_user_online_member_count(User *u);
diff --git a/td/telegram/DialogFilter.hpp b/td/telegram/DialogFilter.hpp
index 0921e88c7..2becce043 100644
--- a/td/telegram/DialogFilter.hpp
+++ b/td/telegram/DialogFilter.hpp
@@ -50,9 +50,9 @@ void DialogFilter::store(StorerT &storer) const {
template
void DialogFilter::parse(ParserT &parser) {
using td::parse;
- bool has_pinned_dialog_ids = !pinned_dialog_ids.empty();
- bool has_included_dialog_ids = !included_dialog_ids.empty();
- bool has_excluded_dialog_ids = !excluded_dialog_ids.empty();
+ bool has_pinned_dialog_ids;
+ bool has_included_dialog_ids;
+ bool has_excluded_dialog_ids;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(exclude_muted);
PARSE_FLAG(exclude_read);
diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp
index dec9049e9..f04690ec1 100644
--- a/td/telegram/MessageContent.cpp
+++ b/td/telegram/MessageContent.cpp
@@ -2435,36 +2435,30 @@ tl_object_ptr get_fake_input_media(Td *td, tl_object_p
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_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.str()));
- }
- string mime_type = MimeType::from_extension(path_view.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);
+ if (is_document_file_type(file_type)) {
+ vector> attributes;
+ 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.str()));
}
- case FileType::Photo:
- return make_tl_object(
- 0, std::move(input_file), vector>(), 0);
- default:
- UNREACHABLE();
+ string mime_type = MimeType::from_extension(path_view.extension());
+ int32 flags = 0;
+ if (file_type == FileType::Video) {
+ flags |= telegram_api::inputMediaUploadedDocument::NOSOUND_VIDEO_MASK;
+ }
+ if (file_type == FileType::DocumentAsFile) {
+ flags |= telegram_api::inputMediaUploadedDocument::FORCE_FILE_MASK;
+ }
+ return make_tl_object(
+ flags, false /*ignored*/, false /*ignored*/, std::move(input_file), nullptr, mime_type, std::move(attributes),
+ vector>(), 0);
+ } else {
+ CHECK(file_type == FileType::Photo);
+ return make_tl_object(
+ 0, std::move(input_file), vector>(), 0);
}
- return nullptr;
}
void delete_message_content_thumbnail(MessageContent *content, Td *td) {
@@ -5769,10 +5763,6 @@ bool is_unsent_animated_emoji_click(Td *td, DialogId dialog_id, const DialogActi
return !td->stickers_manager_->is_sent_animated_emoji_click(dialog_id, remove_emoji_modifiers(emoji));
}
-bool is_active_reaction(Td *td, const string &reaction) {
- return td->stickers_manager_->is_active_reaction(reaction);
-}
-
void init_stickers_manager(Td *td) {
td->stickers_manager_->init();
}
diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h
index edb52d201..1d996453d 100644
--- a/td/telegram/MessageContent.h
+++ b/td/telegram/MessageContent.h
@@ -248,8 +248,6 @@ void on_sent_message_content(Td *td, const MessageContent *content);
bool is_unsent_animated_emoji_click(Td *td, DialogId dialog_id, const DialogAction &action);
-bool is_active_reaction(Td *td, const string &reaction);
-
void init_stickers_manager(Td *td);
void on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date);
diff --git a/td/telegram/MessageReaction.cpp b/td/telegram/MessageReaction.cpp
index feade1105..95b6f1e1e 100644
--- a/td/telegram/MessageReaction.cpp
+++ b/td/telegram/MessageReaction.cpp
@@ -217,6 +217,17 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
}
};
+void MessageReaction::add_recent_chooser_dialog_id(DialogId dialog_id) {
+ recent_chooser_dialog_ids_.insert(recent_chooser_dialog_ids_.begin(), dialog_id);
+ if (recent_chooser_dialog_ids_.size() > MAX_RECENT_CHOOSERS) {
+ recent_chooser_dialog_ids_.resize(MAX_RECENT_CHOOSERS);
+ }
+}
+
+bool MessageReaction::remove_recent_chooser_dialog_id(DialogId dialog_id) {
+ return td::remove(recent_chooser_dialog_ids_, dialog_id);
+}
+
void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, bool can_get_added_reactions) {
if (is_chosen_ == is_chosen) {
return;
@@ -227,12 +238,9 @@ void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id,
if (chooser_dialog_id.is_valid()) {
choose_count_ += is_chosen_ ? 1 : -1;
if (can_get_added_reactions) {
- td::remove(recent_chooser_dialog_ids_, chooser_dialog_id);
+ remove_recent_chooser_dialog_id(chooser_dialog_id);
if (is_chosen_) {
- recent_chooser_dialog_ids_.insert(recent_chooser_dialog_ids_.begin(), chooser_dialog_id);
- if (recent_chooser_dialog_ids_.size() > MAX_RECENT_CHOOSERS) {
- recent_chooser_dialog_ids_.resize(MAX_RECENT_CHOOSERS);
- }
+ add_recent_chooser_dialog_id(chooser_dialog_id);
}
}
}
@@ -410,6 +418,24 @@ void MessageReactions::sort_reactions(const FlatHashMap &active_
});
}
+void MessageReactions::fix_chosen_reaction(DialogId my_dialog_id) {
+ bool need_fix = false;
+ for (auto &reaction : reactions_) {
+ if (!reaction.is_chosen() && reaction.remove_recent_chooser_dialog_id(my_dialog_id)) {
+ LOG(WARNING) << "Fix recent chosen reaction in " << *this;
+ need_fix = true;
+ }
+ }
+ if (!need_fix) {
+ return;
+ }
+ for (auto &reaction : reactions_) {
+ if (reaction.is_chosen() && !td::contains(reaction.get_recent_chooser_dialog_ids(), my_dialog_id)) {
+ reaction.add_recent_chooser_dialog_id(my_dialog_id);
+ }
+ }
+}
+
bool MessageReactions::need_update_message_reactions(const MessageReactions *old_reactions,
const MessageReactions *new_reactions) {
if (old_reactions == nullptr) {
diff --git a/td/telegram/MessageReaction.h b/td/telegram/MessageReaction.h
index 2b59a6479..a2ce9e918 100644
--- a/td/telegram/MessageReaction.h
+++ b/td/telegram/MessageReaction.h
@@ -78,6 +78,10 @@ class MessageReaction {
return recent_chooser_min_channels_;
}
+ void add_recent_chooser_dialog_id(DialogId dialog_id);
+
+ bool remove_recent_chooser_dialog_id(DialogId dialog_id);
+
td_api::object_ptr get_message_reaction_object(Td *td) const;
template
@@ -149,6 +153,8 @@ struct MessageReactions {
void sort_reactions(const FlatHashMap &active_reaction_pos);
+ void fix_chosen_reaction(DialogId my_dialog_id);
+
static bool need_update_message_reactions(const MessageReactions *old_reactions,
const MessageReactions *new_reactions);
diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp
index adf6a71a6..b725bb666 100644
--- a/td/telegram/MessagesManager.cpp
+++ b/td/telegram/MessagesManager.cpp
@@ -6455,7 +6455,7 @@ void MessagesManager::skip_old_pending_pts_update(tl_object_ptrget_id() == telegram_api::updateNewMessage::ID) {
auto update_new_message = static_cast(update.get());
auto full_message_id = get_full_message_id(update_new_message->message_, false);
- if (update_message_ids_.find(full_message_id) != update_message_ids_.end()) {
+ if (update_message_ids_.count(full_message_id) > 0) {
if (new_pts == old_pts) { // otherwise message can be already deleted
// apply sent message anyway
on_get_message(std::move(update_new_message->message_), true, false, false, true, true,
@@ -6716,41 +6716,28 @@ void MessagesManager::on_update_message_reactions(FullMessageId full_message_id,
Promise &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
+ auto new_reactions = MessageReactions::get_message_reactions(td_, std::move(reactions), td_->auth_manager_->is_bot());
if (!have_message_force(full_message_id, "on_update_message_reactions")) {
auto dialog_id = full_message_id.get_dialog_id();
if (!have_input_peer(dialog_id, AccessRights::Read)) {
LOG(INFO) << "Ignore updateMessageReaction in inaccessible " << full_message_id;
return;
}
-
- switch (dialog_id.get_type()) {
- case DialogType::User:
- case DialogType::Chat: {
- const Dialog *d = get_dialog(dialog_id);
- if (d == nullptr) {
- LOG(INFO) << "Ignore updateMessageReaction in unknown " << dialog_id;
- return;
- }
- if (d->last_new_message_id != MessageId() && full_message_id.get_message_id() > d->last_new_message_id) {
- LOG(INFO) << "Ignore updateMessageReaction about too new " << full_message_id << ", last known is "
- << d->last_new_message_id;
- return;
- }
- break;
- }
- case DialogType::Channel:
- // the message will be added after get_channel_difference_if_needed
- break;
- case DialogType::SecretChat:
- default:
- UNREACHABLE();
- break;
+ const Dialog *d = get_dialog(dialog_id);
+ if (d == nullptr) {
+ LOG(INFO) << "Ignore updateMessageReaction in unknown " << dialog_id;
+ return;
}
- LOG(INFO) << "Need to load " << full_message_id << " to process updateMessageReaction";
- return get_message_from_server(full_message_id, std::move(promise), "on_update_message_reactions");
+
+ // there is no message, so the update can be ignored
+ if ((new_reactions != nullptr && !new_reactions->unread_reactions_.empty()) || d->unread_reaction_count > 0) {
+ // but if there are unread reactions or the chat has unread reactions,
+ // then number of unread reactions could have been changed, so reload the number of unread reactions
+ send_get_dialog_query(dialog_id, std::move(promise), 0, "on_update_message_reactions");
+ }
+ return;
}
- auto new_reactions = MessageReactions::get_message_reactions(td_, std::move(reactions), td_->auth_manager_->is_bot());
update_message_interaction_info(full_message_id, -1, -1, false, nullptr, true, std::move(new_reactions));
promise.set_value(Unit());
}
@@ -7063,6 +7050,11 @@ bool MessagesManager::update_message_interaction_info(Dialog *d, Message *m, int
reactions->update_from(*m->reactions);
}
reactions->sort_reactions(active_reaction_pos_);
+ reactions->fix_chosen_reaction(get_my_dialog_id());
+ if (d->default_send_message_as_dialog_id.is_valid()) {
+ // the reaction could be set by previous owner of the broadcast
+ // reactions->fix_chosen_reaction(d->default_send_message_as_dialog_id);
+ }
}
bool need_update_reactions =
has_reactions && MessageReactions::need_update_message_reactions(m->reactions.get(), reactions.get());
@@ -7679,7 +7671,7 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p
auto update_new_channel_message = static_cast(update.get());
auto message_id = get_message_id(update_new_channel_message->message_, false);
FullMessageId full_message_id(dialog_id, message_id);
- if (update_message_ids_.find(full_message_id) != update_message_ids_.end()) {
+ if (update_message_ids_.count(full_message_id) > 0) {
// apply sent channel message
on_get_message(std::move(update_new_channel_message->message_), true, true, false, true, true,
"updateNewChannelMessage with an awaited message");
@@ -7755,79 +7747,78 @@ bool MessagesManager::is_old_channel_update(DialogId dialog_id, int32 new_pts) {
return new_pts <= (d == nullptr ? load_channel_pts(dialog_id) : d->pts);
}
-void MessagesManager::process_pts_update(tl_object_ptr &&update) {
- switch (update->get_id()) {
+void MessagesManager::process_pts_update(tl_object_ptr &&update_ptr) {
+ switch (update_ptr->get_id()) {
case dummyUpdate::ID:
LOG(INFO) << "Process dummyUpdate";
break;
case telegram_api::updateNewMessage::ID: {
- auto update_new_message = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateNewMessage";
- on_get_message(std::move(update_new_message->message_), true, false, false, true, true, "updateNewMessage");
+ on_get_message(std::move(update->message_), true, false, false, true, true, "updateNewMessage");
break;
}
case updateSentMessage::ID: {
- 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(),
+ auto update = move_tl_object_as(update_ptr);
+ LOG(INFO) << "Process updateSentMessage " << update->random_id_;
+ on_send_message_success(update->random_id_, update->message_id_, update->date_, update->ttl_period_, FileId(),
"process updateSentMessage");
break;
}
case telegram_api::updateReadMessagesContents::ID: {
- auto read_contents_update = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateReadMessageContents";
- for (auto &message_id : read_contents_update->messages_) {
+ for (auto &message_id : update->messages_) {
read_message_content_from_updates(MessageId(ServerMessageId(message_id)));
}
break;
}
case telegram_api::updateEditMessage::ID: {
- auto update_edit_message = move_tl_object_as(update);
- auto full_message_id = on_get_message(std::move(update_edit_message->message_), false, false, false, false, false,
- "updateEditMessage");
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateEditMessage";
- on_message_edited(full_message_id, update_edit_message->pts_);
+ bool had_message = have_message_force(get_full_message_id(update->message_, false), "updateEditMessage");
+ auto full_message_id =
+ on_get_message(std::move(update->message_), false, false, false, false, false, "updateEditMessage");
+ on_message_edited(full_message_id, update->pts_, had_message);
break;
}
case telegram_api::updateDeleteMessages::ID: {
- auto delete_update = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateDeleteMessages";
vector message_ids;
- for (auto message : delete_update->messages_) {
+ for (auto message : update->messages_) {
message_ids.push_back(MessageId(ServerMessageId(message)));
}
delete_messages_from_updates(message_ids);
break;
}
case telegram_api::updateReadHistoryInbox::ID: {
- auto read_update = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateReadHistoryInbox";
- DialogId dialog_id(read_update->peer_);
+ DialogId dialog_id(update->peer_);
FolderId folder_id;
- if ((read_update->flags_ & telegram_api::updateReadHistoryInbox::FOLDER_ID_MASK) != 0) {
- folder_id = FolderId(read_update->folder_id_);
+ if ((update->flags_ & telegram_api::updateReadHistoryInbox::FOLDER_ID_MASK) != 0) {
+ folder_id = FolderId(update->folder_id_);
}
on_update_dialog_folder_id(dialog_id, folder_id);
- read_history_inbox(dialog_id, MessageId(ServerMessageId(read_update->max_id_)),
- -1 /*read_update->still_unread_count*/, "updateReadHistoryInbox");
+ read_history_inbox(dialog_id, MessageId(ServerMessageId(update->max_id_)), -1 /*update->still_unread_count*/,
+ "updateReadHistoryInbox");
break;
}
case telegram_api::updateReadHistoryOutbox::ID: {
- auto read_update = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateReadHistoryOutbox";
- read_history_outbox(DialogId(read_update->peer_), MessageId(ServerMessageId(read_update->max_id_)));
+ read_history_outbox(DialogId(update->peer_), MessageId(ServerMessageId(update->max_id_)));
break;
}
case telegram_api::updatePinnedMessages::ID: {
- auto pinned_messages_update = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updatePinnedMessages";
vector message_ids;
- for (auto message : pinned_messages_update->messages_) {
+ for (auto message : update->messages_) {
message_ids.push_back(MessageId(ServerMessageId(message)));
}
- update_dialog_pinned_messages_from_updates(DialogId(pinned_messages_update->peer_), message_ids,
- pinned_messages_update->pinned_);
+ update_dialog_pinned_messages_from_updates(DialogId(update->peer_), message_ids, update->pinned_);
break;
}
default:
@@ -7836,37 +7827,35 @@ void MessagesManager::process_pts_update(tl_object_ptr &&u
CHECK(!td_->updates_manager_->running_get_difference());
}
-void MessagesManager::process_channel_update(tl_object_ptr &&update) {
- switch (update->get_id()) {
+void MessagesManager::process_channel_update(tl_object_ptr &&update_ptr) {
+ switch (update_ptr->get_id()) {
case dummyUpdate::ID:
LOG(INFO) << "Process dummyUpdate";
break;
case updateSentMessage::ID: {
- 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(),
+ auto update = move_tl_object_as(update_ptr);
+ LOG(INFO) << "Process updateSentMessage " << update->random_id_;
+ on_send_message_success(update->random_id_, update->message_id_, update->date_, update->ttl_period_, FileId(),
"process updateSentChannelMessage");
break;
}
case telegram_api::updateNewChannelMessage::ID: {
- auto update_new_channel_message = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateNewChannelMessage";
- on_get_message(std::move(update_new_channel_message->message_), true, true, false, true, true,
- "updateNewChannelMessage");
+ on_get_message(std::move(update->message_), true, true, false, true, true, "updateNewChannelMessage");
break;
}
case telegram_api::updateDeleteChannelMessages::ID: {
- auto delete_channel_messages_update = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateDeleteChannelMessages";
- ChannelId channel_id(delete_channel_messages_update->channel_id_);
+ ChannelId channel_id(update->channel_id_);
if (!channel_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << channel_id;
break;
}
vector message_ids;
- for (auto &message : delete_channel_messages_update->messages_) {
+ for (auto &message : update->messages_) {
message_ids.push_back(MessageId(ServerMessageId(message)));
}
@@ -7875,29 +7864,29 @@ void MessagesManager::process_channel_update(tl_object_ptr
break;
}
case telegram_api::updateEditChannelMessage::ID: {
- auto update_edit_channel_message = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updateEditChannelMessage";
- auto full_message_id = on_get_message(std::move(update_edit_channel_message->message_), false, true, false, false,
- false, "updateEditChannelMessage");
- on_message_edited(full_message_id, update_edit_channel_message->pts_);
+ bool had_message = have_message_force(get_full_message_id(update->message_, false), "updateEditChannelMessage");
+ auto full_message_id =
+ on_get_message(std::move(update->message_), false, true, false, false, false, "updateEditChannelMessage");
+ on_message_edited(full_message_id, update->pts_, had_message);
break;
}
case telegram_api::updatePinnedChannelMessages::ID: {
- auto pinned_channel_messages_update = move_tl_object_as(update);
+ auto update = move_tl_object_as(update_ptr);
LOG(INFO) << "Process updatePinnedChannelMessages";
- ChannelId channel_id(pinned_channel_messages_update->channel_id_);
+ ChannelId channel_id(update->channel_id_);
if (!channel_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << channel_id;
break;
}
vector message_ids;
- for (auto &message : pinned_channel_messages_update->messages_) {
+ for (auto &message : update->messages_) {
message_ids.push_back(MessageId(ServerMessageId(message)));
}
- update_dialog_pinned_messages_from_updates(DialogId(channel_id), message_ids,
- pinned_channel_messages_update->pinned_);
+ update_dialog_pinned_messages_from_updates(DialogId(channel_id), message_ids, update->pinned_);
break;
}
default:
@@ -7905,7 +7894,7 @@ void MessagesManager::process_channel_update(tl_object_ptr
}
}
-void MessagesManager::on_message_edited(FullMessageId full_message_id, int32 pts) {
+void MessagesManager::on_message_edited(FullMessageId full_message_id, int32 pts, bool had_message) {
if (full_message_id == FullMessageId()) {
return;
}
@@ -7920,6 +7909,13 @@ void MessagesManager::on_message_edited(FullMessageId full_message_id, int32 pts
send_update_message_edited(dialog_id, m);
}
update_used_hashtags(dialog_id, m);
+
+ if (!had_message &&
+ ((m->reactions != nullptr && !m->reactions->unread_reactions_.empty()) || d->unread_reaction_count > 0)) {
+ // if new message with unread reactions was added or the chat has unread reactions,
+ // then number of unread reactions could have been changed, so reload the number of unread reactions
+ send_get_dialog_query(dialog_id, Promise(), 0, "on_message_edited");
+ }
}
bool MessagesManager::update_dialog_notification_settings(DialogId dialog_id,
@@ -10214,7 +10210,7 @@ void MessagesManager::on_get_dialog_messages_search_result(
if (filter == MessageSearchFilter::UnreadReaction) {
d->unread_reaction_count = old_message_count;
// update_dialog_mention_notification_count(d);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "on_get_dialog_messages_search_result");
}
update_dialog = true;
}
@@ -11084,6 +11080,52 @@ void MessagesManager::on_failed_scheduled_message_deletion(DialogId dialog_id, c
load_dialog_scheduled_messages(dialog_id, false, 0, Promise());
}
+MessagesManager::CanDeleteDialog MessagesManager::can_delete_dialog(const Dialog *d) const {
+ auto chat_source = sponsored_dialog_source_.get_chat_source_object();
+ if (chat_source != nullptr) {
+ switch (chat_source->get_id()) {
+ case td_api::chatSourcePublicServiceAnnouncement::ID:
+ // can delete for self (but only while removing from dialog list)
+ return {true, false};
+ default:
+ return {false, false};
+ }
+ }
+ if (td_->auth_manager_->is_bot() || !have_input_peer(d->dialog_id, AccessRights::Read)) {
+ return {false, false};
+ }
+
+ switch (d->dialog_id.get_type()) {
+ case DialogType::User:
+ if (d->dialog_id == get_my_dialog_id() || td_->contacts_manager_->is_user_deleted(d->dialog_id.get_user_id()) ||
+ td_->contacts_manager_->is_user_bot(d->dialog_id.get_user_id())) {
+ return {true, false};
+ }
+ return {true, G()->shared_config().get_option_boolean("revoke_pm_inbox", true)};
+ case DialogType::Chat:
+ // chats can be deleted only for self and can be deleted for everyone by their creator
+ return {true, td_->contacts_manager_->get_chat_status(d->dialog_id.get_chat_id()).is_creator()};
+ case DialogType::Channel:
+ // private supergroups can be deleted for self
+ return {!is_broadcast_channel(d->dialog_id) &&
+ !td_->contacts_manager_->is_channel_public(d->dialog_id.get_channel_id()),
+ td_->contacts_manager_->get_channel_can_be_deleted(d->dialog_id.get_channel_id())};
+ case DialogType::SecretChat:
+ if (td_->contacts_manager_->get_secret_chat_state(d->dialog_id.get_secret_chat_id()) == SecretChatState::Closed) {
+ // in a closed secret chats there is no way to delete messages for both users
+ return {true, false};
+ } else {
+ // active secret chats can be deleted only for both users
+ return {false, true};
+ }
+ break;
+ case DialogType::None:
+ default:
+ UNREACHABLE();
+ return {false, false};
+ }
+}
+
void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from_dialog_list, bool revoke,
Promise &&promise) {
LOG(INFO) << "Receive deleteChatHistory request to delete all messages in " << dialog_id
@@ -11104,8 +11146,7 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from
return promise.set_error(Status::Error(400, "Can't delete the chat"));
}
if (!remove_from_dialog_list) {
- return promise.set_error(
- Status::Error(400, "Can't delete only chat history without removing the chat from the chat list"));
+ return promise.set_error(Status::Error(400, "Can't delete chat history without removing the chat"));
}
removed_sponsored_dialog_id_ = dialog_id;
@@ -11116,37 +11157,25 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from
return;
}
- auto dialog_type = dialog_id.get_type();
- switch (dialog_type) {
- case DialogType::User:
- case DialogType::Chat:
- // ok
- break;
- case DialogType::Channel:
- if (revoke) {
- if (!td_->contacts_manager_->get_channel_can_be_deleted(d->dialog_id.get_channel_id())) {
- return promise.set_error(Status::Error(400, "Can't delete chat history for all chat members"));
- }
- } else {
- if (is_broadcast_channel(dialog_id)) {
- return promise.set_error(Status::Error(400, "Can't delete chat history in a channel"));
- }
- if (td_->contacts_manager_->is_channel_public(dialog_id.get_channel_id())) {
- return promise.set_error(Status::Error(400, "Can't delete chat history in a public supergroup"));
- }
+ auto can_delete = can_delete_dialog(d);
+ if (revoke) {
+ if (!can_delete.for_all_users_) {
+ if (!can_delete.for_self_) {
+ return promise.set_error(Status::Error(400, "Chat history can't be deleted"));
}
- break;
- case DialogType::SecretChat:
- // ok
- break;
- case DialogType::None:
- default:
- UNREACHABLE();
- break;
+
+ LOG(INFO) << "Can't delete history of " << dialog_id << " for everyone; delete it only for self";
+ revoke = false;
+ }
+ } else {
+ if (!can_delete.for_self_) {
+ return promise.set_error(
+ Status::Error(400, PSLICE() << "Can't delete history of " << dialog_id << " only for self"));
+ }
}
auto last_new_message_id = d->last_new_message_id;
- if (dialog_type != DialogType::SecretChat && last_new_message_id == MessageId()) {
+ if (dialog_id.get_type() != DialogType::SecretChat && last_new_message_id == MessageId()) {
// TODO get dialog from the server and delete history from last message identifier
}
@@ -11697,7 +11726,7 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia
}
if (d->unread_reaction_count > 0) {
set_dialog_unread_reaction_count(d, 0);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "delete_all_dialog_messages");
}
bool has_last_message_id = d->last_message_id != MessageId();
@@ -11919,7 +11948,7 @@ void MessagesManager::read_all_dialog_reactions(DialogId dialog_id, Promiseunread_reaction_count != 0) {
set_dialog_unread_reaction_count(d, 0);
if (!is_update_sent) {
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "read_all_dialog_reactions");
} else {
LOG(INFO) << "Update unread reaction message count in " << dialog_id << " to " << d->unread_reaction_count;
on_dialog_updated(dialog_id, "read_all_dialog_reactions");
@@ -13117,6 +13146,8 @@ class MessagesManager::DialogFiltersLogEvent {
void MessagesManager::tear_down() {
parent_.reset();
+
+ LOG(DEBUG) << "Have " << dialogs_.size() << " chats with " << added_message_count_ << " messages to free";
}
void MessagesManager::hangup() {
@@ -14853,7 +14884,7 @@ void MessagesManager::set_dialog_is_empty(Dialog *d, const char *source) {
}
if (d->unread_reaction_count > 0) {
set_dialog_unread_reaction_count(d, 0);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "set_dialog_is_empty");
}
if (d->reply_markup_message_id != MessageId()) {
set_dialog_reply_markup(d, MessageId());
@@ -15569,7 +15600,7 @@ void MessagesManager::on_get_dialogs(FolderId folder_id, vectorunread_reaction_count != dialog->unread_reactions_count_) {
set_dialog_unread_reaction_count(d, dialog->unread_reactions_count_);
// update_dialog_mention_notification_count(d);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "on_get_dialogs");
}
}
@@ -16183,7 +16214,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog *
}
} else {
set_dialog_unread_reaction_count(d, d->unread_reaction_count - 1);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "do_delete_message");
}
}
@@ -16251,6 +16282,8 @@ void MessagesManager::on_message_deleted(Dialog *d, Message *m, bool is_permanen
if (m->notification_id.is_valid()) {
delete_notification_id_to_message_id_correspondence(d, m->notification_id, m->message_id);
}
+
+ added_message_count_--;
}
unique_ptr MessagesManager::do_delete_scheduled_message(Dialog *d, MessageId message_id,
@@ -17558,62 +17591,11 @@ void MessagesManager::on_get_common_dialogs(UserId user_id, int64 offset_chat_id
}
bool is_last = chats.empty() && offset_chat_id == 0;
for (auto &chat : chats) {
- DialogId dialog_id;
- switch (chat->get_id()) {
- case telegram_api::chatEmpty::ID: {
- auto c = static_cast(chat.get());
- ChatId chat_id(c->id_);
- if (!chat_id.is_valid()) {
- LOG(ERROR) << "Receive invalid " << chat_id;
- continue;
- }
- dialog_id = DialogId(chat_id);
- break;
- }
- case telegram_api::chat::ID: {
- auto c = static_cast(chat.get());
- ChatId chat_id(c->id_);
- if (!chat_id.is_valid()) {
- LOG(ERROR) << "Receive invalid " << chat_id;
- continue;
- }
- dialog_id = DialogId(chat_id);
- break;
- }
- case telegram_api::chatForbidden::ID: {
- auto c = static_cast(chat.get());
- ChatId chat_id(c->id_);
- if (!chat_id.is_valid()) {
- LOG(ERROR) << "Receive invalid " << chat_id;
- continue;
- }
- dialog_id = DialogId(chat_id);
- break;
- }
- case telegram_api::channel::ID: {
- auto c = static_cast(chat.get());
- ChannelId channel_id(c->id_);
- if (!channel_id.is_valid()) {
- LOG(ERROR) << "Receive invalid " << channel_id;
- continue;
- }
- dialog_id = DialogId(channel_id);
- break;
- }
- case telegram_api::channelForbidden::ID: {
- auto c = static_cast(chat.get());
- ChannelId channel_id(c->id_);
- if (!channel_id.is_valid()) {
- LOG(ERROR) << "Receive invalid " << channel_id;
- continue;
- }
- dialog_id = DialogId(channel_id);
- break;
- }
- default:
- UNREACHABLE();
+ auto dialog_id = ContactsManager::get_dialog_id(chat);
+ if (!dialog_id.is_valid()) {
+ LOG(ERROR) << "Receive invalid " << to_string(chat);
+ continue;
}
- CHECK(dialog_id.is_valid());
td_->contacts_manager_->on_get_chat(std::move(chat), "on_get_common_dialogs");
if (!td::contains(result, dialog_id)) {
@@ -19256,7 +19238,7 @@ void MessagesManager::add_dialog_filter(unique_ptr dialog_filter,
}
auto dialog_list_id = DialogListId(dialog_filter_id);
- CHECK(dialog_lists_.find(dialog_list_id) == dialog_lists_.end());
+ CHECK(dialog_lists_.count(dialog_list_id) == 0);
auto &list = add_dialog_list(dialog_list_id);
auto folder_ids = get_dialog_list_folder_ids(list);
@@ -20407,7 +20389,7 @@ DialogId MessagesManager::create_new_group_chat(const vector &user_ids,
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || created_dialogs_.find(random_id) != created_dialogs_.end());
+ } while (random_id == 0 || created_dialogs_.count(random_id) > 0);
created_dialogs_[random_id]; // reserve place for result
td_->create_handler(std::move(promise))->send(std::move(input_users), new_title, random_id);
@@ -20445,7 +20427,7 @@ DialogId MessagesManager::create_new_channel_chat(const string &title, bool is_m
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || created_dialogs_.find(random_id) != created_dialogs_.end());
+ } while (random_id == 0 || created_dialogs_.count(random_id) > 0);
created_dialogs_[random_id]; // reserve place for result
td_->create_handler(std::move(promise))
@@ -21156,65 +21138,9 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog *
CHECK(d != nullptr);
auto chat_source = is_dialog_sponsored(d) ? sponsored_dialog_source_.get_chat_source_object() : nullptr;
-
- bool can_delete_for_self = false;
- bool can_delete_for_all_users = false;
- if (chat_source != nullptr) {
- switch (chat_source->get_id()) {
- case td_api::chatSourcePublicServiceAnnouncement::ID:
- // can delete for self (but only while removing from dialog list)
- can_delete_for_self = true;
- break;
- default:
- // can't delete
- break;
- }
- } else if (!td_->auth_manager_->is_bot() && have_input_peer(d->dialog_id, AccessRights::Read)) {
- switch (d->dialog_id.get_type()) {
- case DialogType::User:
- can_delete_for_self = true;
- can_delete_for_all_users = G()->shared_config().get_option_boolean("revoke_pm_inbox", true);
- if (d->dialog_id == get_my_dialog_id() || td_->contacts_manager_->is_user_deleted(d->dialog_id.get_user_id()) ||
- td_->contacts_manager_->is_user_bot(d->dialog_id.get_user_id())) {
- can_delete_for_all_users = false;
- }
- break;
- case DialogType::Chat:
- // chats can be deleted only for self with deleteChatHistory and for everyone by their creator
- can_delete_for_self = true;
- can_delete_for_all_users = td_->contacts_manager_->get_chat_status(d->dialog_id.get_chat_id()).is_creator();
- break;
- case DialogType::Channel:
- if (is_broadcast_channel(d->dialog_id) ||
- td_->contacts_manager_->is_channel_public(d->dialog_id.get_channel_id())) {
- // deleteChatHistory can't be used in channels and public supergroups to delete messages for self
- } else {
- // private supergroups can be deleted for self
- can_delete_for_self = true;
- }
- if (td_->contacts_manager_->get_channel_can_be_deleted(d->dialog_id.get_channel_id())) {
- can_delete_for_all_users = true;
- }
- break;
- case DialogType::SecretChat:
- if (td_->contacts_manager_->get_secret_chat_state(d->dialog_id.get_secret_chat_id()) ==
- SecretChatState::Closed) {
- // in a closed secret chats there is no way to delete messages for both users
- can_delete_for_self = true;
- } else {
- // active secret chats can be deleted only for both users
- can_delete_for_all_users = true;
- }
- break;
- case DialogType::None:
- default:
- UNREACHABLE();
- }
- }
-
+ auto can_delete = can_delete_dialog(d);
// TODO hide/show draft message when can_send_message(dialog_id) changes
auto draft_message = can_send_message(d->dialog_id).is_ok() ? get_draft_message_object(d->draft_message) : nullptr;
-
return make_tl_object(
d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_dialog_title(d->dialog_id),
get_chat_photo_info_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)),
@@ -21222,7 +21148,7 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog *
get_message_object(d->dialog_id, get_message(d, d->last_message_id), "get_chat_object"),
get_chat_positions_object(d), get_default_message_sender_object(d),
get_dialog_has_protected_content(d->dialog_id), d->is_marked_as_unread, d->is_blocked,
- get_dialog_has_scheduled_messages(d), can_delete_for_self, can_delete_for_all_users,
+ get_dialog_has_scheduled_messages(d), can_delete.for_self_, 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, d->unread_reaction_count,
@@ -22054,7 +21980,7 @@ std::pair> MessagesManager::get_message_thread_histo
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || found_dialog_messages_.find(random_id) != found_dialog_messages_.end());
+ } while (random_id == 0 || found_dialog_messages_.count(random_id) > 0);
found_dialog_messages_[random_id]; // reserve place for result
td_->create_handler(std::move(promise))
@@ -22103,7 +22029,7 @@ td_api::object_ptr MessagesManager::get_dialog_message_
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || found_dialog_message_calendars_.find(random_id) != found_dialog_message_calendars_.end());
+ } while (random_id == 0 || found_dialog_message_calendars_.count(random_id) > 0);
found_dialog_message_calendars_[random_id]; // reserve place for result
CHECK(filter != MessageSearchFilter::Call && filter != MessageSearchFilter::MissedCall);
@@ -22317,7 +22243,7 @@ std::pair> MessagesManager::search_dialog_messages(
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || found_dialog_messages_.find(random_id) != found_dialog_messages_.end());
+ } while (random_id == 0 || found_dialog_messages_.count(random_id) > 0);
found_dialog_messages_[random_id]; // reserve place for result
if (filter == MessageSearchFilter::UnreadMention || filter == MessageSearchFilter::UnreadReaction) {
@@ -22437,7 +22363,7 @@ std::pair> MessagesManager::search_call_messages(Me
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || found_call_messages_.find(random_id) != found_call_messages_.end());
+ } while (random_id == 0 || found_call_messages_.count(random_id) > 0);
found_call_messages_[random_id]; // reserve place for result
auto filter = only_missed ? MessageSearchFilter::MissedCall : MessageSearchFilter::Call;
@@ -22947,7 +22873,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo
if (filter == MessageSearchFilter::UnreadReaction) {
d->unread_reaction_count = message_count;
// update_dialog_mention_notification_count(d);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "on_search_dialog_messages_db_result");
}
on_dialog_updated(dialog_id, "on_search_dialog_messages_db_result");
}
@@ -23030,7 +22956,7 @@ MessagesManager::FoundMessages MessagesManager::offline_search_messages(DialogId
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || found_fts_messages_.find(random_id) != found_fts_messages_.end());
+ } while (random_id == 0 || found_fts_messages_.count(random_id) > 0);
found_fts_messages_[random_id]; // reserve place for result
G()->td_db()->get_messages_db_async()->get_messages_fts(
@@ -23163,7 +23089,7 @@ std::pair> MessagesManager::search_messages(
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || found_messages_.find(random_id) != found_messages_.end());
+ } while (random_id == 0 || found_messages_.count(random_id) > 0);
found_messages_[random_id]; // reserve place for result
LOG(DEBUG) << "Search all messages filtered by " << filter << " with query = \"" << query << "\" from date "
@@ -23194,8 +23120,7 @@ int64 MessagesManager::get_dialog_message_by_date(DialogId dialog_id, int32 date
int64 random_id = 0;
do {
random_id = Random::secure_int64();
- } while (random_id == 0 ||
- get_dialog_message_by_date_results_.find(random_id) != get_dialog_message_by_date_results_.end());
+ } while (random_id == 0 || get_dialog_message_by_date_results_.count(random_id) > 0);
get_dialog_message_by_date_results_[random_id]; // reserve place for result
auto message_id = find_message_by_date(d->messages.get(), date);
@@ -23511,9 +23436,14 @@ void MessagesManager::get_dialog_message_count(DialogId dialog_id, MessageSearch
return promise.set_value(std::move(message_count));
}
+ get_dialog_message_count_from_server(dialog_id, filter, std::move(promise));
+}
+
+void MessagesManager::get_dialog_message_count_from_server(DialogId dialog_id, MessageSearchFilter filter,
+ Promise &&promise) {
LOG(INFO) << "Get number of messages in " << dialog_id << " filtered by " << filter << " from the server";
- switch (dialog_type) {
+ switch (dialog_id.get_type()) {
case DialogType::User:
case DialogType::Chat:
case DialogType::Channel:
@@ -24238,7 +24168,8 @@ void MessagesManager::set_message_reaction(FullMessageId full_message_id, string
return promise.set_error(Status::Error(400, "The reaction isn't available for the message"));
}
- bool can_get_added_reactions = !is_broadcast_channel(dialog_id) && dialog_id.get_type() != DialogType::User;
+ bool can_get_added_reactions = !is_broadcast_channel(dialog_id) && dialog_id.get_type() != DialogType::User &&
+ !is_discussion_message(dialog_id, m);
if (m->reactions == nullptr) {
if (reaction.empty()) {
return promise.set_value(Unit());
@@ -24638,7 +24569,7 @@ int64 MessagesManager::generate_new_random_id() {
int64 random_id;
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || being_sent_messages_.find(random_id) != being_sent_messages_.end());
+ } while (random_id == 0 || being_sent_messages_.count(random_id) > 0);
return random_id;
}
@@ -27849,6 +27780,7 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d
}
auto schedule_date = get_message_schedule_date(messages[0]);
+ auto as_input_peer = get_send_message_as_input_peer(messages[0]);
int32 flags = 0;
if (messages[0]->disable_notification) {
@@ -27863,7 +27795,7 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d
if (schedule_date != 0) {
flags |= SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE;
}
- if (messages[0]->has_explicit_sender) {
+ if (as_input_peer != nullptr) {
flags |= SEND_MESSAGE_FLAG_HAS_SEND_AS;
}
if (messages[0]->noforwards) {
@@ -27873,8 +27805,8 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d
vector random_ids =
transform(messages, [this, to_dialog_id](const Message *m) { return begin_send_message(to_dialog_id, m); });
td_->create_handler(get_erase_log_event_promise(log_event_id))
- ->send(flags, to_dialog_id, from_dialog_id, get_send_message_as_input_peer(messages[0]), message_ids,
- std::move(random_ids), schedule_date);
+ ->send(flags, to_dialog_id, from_dialog_id, std::move(as_input_peer), message_ids, std::move(random_ids),
+ schedule_date);
}
Result> MessagesManager::forward_message(
@@ -28737,7 +28669,7 @@ void MessagesManager::start_import_messages(DialogId dialog_id, int64 import_id,
int64 random_id;
do {
random_id = Random::secure_int64();
- } while (random_id == 0 || pending_message_imports_.find(random_id) != pending_message_imports_.end());
+ } while (random_id == 0 || pending_message_imports_.count(random_id) > 0);
pending_message_imports_[random_id] = std::move(pending_message_import);
multipromise.add_promise(PromiseCreator::lambda([actor_id = actor_id(this), random_id](Result result) {
@@ -30494,17 +30426,20 @@ void MessagesManager::send_update_chat_unread_mention_count(const Dialog *d) {
make_tl_object(d->dialog_id.get(), d->unread_mention_count));
}
-void MessagesManager::send_update_chat_unread_reaction_count(const Dialog *d) {
+void MessagesManager::send_update_chat_unread_reaction_count(const Dialog *d, const char *source) {
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_unread_reaction_count";
- LOG(INFO) << "Update unread reaction message count in " << d->dialog_id << " to " << d->unread_reaction_count;
+ LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id
+ << " in send_update_chat_unread_reaction_count from " << source;
+ LOG(INFO) << "Update unread reaction message count in " << d->dialog_id << " to " << d->unread_reaction_count
+ << " from " << source;
on_dialog_updated(d->dialog_id, "send_update_chat_unread_reaction_count");
- send_closure(G()->td(), &Td::send_update,
- make_tl_object(d->dialog_id.get(), d->unread_reaction_count));
+ send_closure(
+ G()->td(), &Td::send_update,
+ td_api::make_object(d->dialog_id.get(), d->unread_reaction_count));
}
void MessagesManager::send_update_chat_position(DialogListId dialog_list_id, const Dialog *d,
@@ -32128,10 +32063,10 @@ void MessagesManager::on_create_new_dialog_success(int64 random_id, tl_object_pt
return promise.set_value(Unit());
}
- if (pending_created_dialogs_.find(dialog_id) == pending_created_dialogs_.end()) {
+ if (pending_created_dialogs_.count(dialog_id) == 0) {
pending_created_dialogs_.emplace(dialog_id, std::move(promise));
} else {
- LOG(ERROR) << dialog_id << " returned twice as result of chat creation";
+ LOG(ERROR) << "Receive twice " << dialog_id << " as result of chat creation";
return on_create_new_dialog_fail(random_id, Status::Error(500, "Chat was created earlier"), std::move(promise));
}
@@ -34456,7 +34391,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
}
if (*need_update && has_unread_message_reactions(dialog_id, message.get())) {
set_dialog_unread_reaction_count(d, d->unread_reaction_count + 1);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "add_message_to_dialog");
}
if (*need_update) {
update_message_count_by_index(d, +1, message.get());
@@ -34684,6 +34619,8 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
}
}
+ added_message_count_++;
+
return result_message;
}
@@ -35715,21 +35652,6 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me
old_file_view.size() == new_file_view.size()) {
auto old_file_type = old_file_view.get_type();
auto new_file_type = new_file_view.get_type();
- auto is_document_file_type = [](FileType file_type) {
- switch (file_type) {
- case FileType::Animation:
- case FileType::Audio:
- case FileType::Document:
- case FileType::DocumentAsFile:
- case FileType::Sticker:
- case FileType::Video:
- case FileType::VideoNote:
- case FileType::VoiceNote:
- return true;
- default:
- return false;
- }
- };
if (is_document_file_type(old_file_type) && is_document_file_type(new_file_type)) {
auto &old_location = old_file_view.local_location();
@@ -37466,7 +37388,7 @@ MessagesManager::DialogList &MessagesManager::add_dialog_list(DialogListId dialo
if (dialog_list_id.is_folder() && dialog_list_id.get_folder_id() != FolderId::archive()) {
dialog_list_id = DialogListId(FolderId::main());
}
- if (dialog_lists_.find(dialog_list_id) == dialog_lists_.end()) {
+ if (dialog_lists_.count(dialog_list_id) == 0) {
LOG(INFO) << "Create " << dialog_list_id;
}
auto &list = dialog_lists_[dialog_list_id];
@@ -37840,8 +37762,7 @@ void MessagesManager::process_get_channel_difference_updates(
auto update_new_channel_message = static_cast(update.get());
auto message_id = get_message_id(update_new_channel_message->message_, false);
FullMessageId full_message_id(dialog_id, message_id);
- if (update_message_ids_.find(full_message_id) != update_message_ids_.end() &&
- changed_message_ids.find(message_id) != changed_message_ids.end()) {
+ if (update_message_ids_.count(full_message_id) > 0 && changed_message_ids.count(message_id) > 0) {
changed_message_ids.erase(message_id);
AwaitedMessage awaited_message;
awaited_message.message = std::move(update_new_channel_message->message_);
@@ -38015,7 +37936,7 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m
if (d->unread_reaction_count != unread_reaction_count) {
set_dialog_unread_reaction_count(d, unread_reaction_count);
// update_dialog_mention_notification_count(d);
- send_update_chat_unread_reaction_count(d);
+ send_update_chat_unread_reaction_count(d, "on_get_channel_dialog 60");
}
if (d->last_read_outbox_message_id != read_outbox_max_message_id) {
diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h
index 80e620f40..3e0adee57 100644
--- a/td/telegram/MessagesManager.h
+++ b/td/telegram/MessagesManager.h
@@ -820,7 +820,7 @@ class MessagesManager final : public Actor {
tl_object_ptr get_messages_object(int32 total_count, const vector &full_message_ids,
bool skip_not_found, const char *source);
- void process_pts_update(tl_object_ptr &&update);
+ void process_pts_update(tl_object_ptr &&update_ptr);
void skip_old_pending_pts_update(tl_object_ptr &&update, int32 new_pts, int32 old_pts,
int32 pts_count, const char *source);
@@ -1844,6 +1844,8 @@ class MessagesManager final : public Actor {
void delete_update_message_id(DialogId dialog_id, MessageId message_id);
+ void get_dialog_message_count_from_server(DialogId dialog_id, MessageSearchFilter filter, Promise &&promise);
+
FullMessageId on_get_message(MessageInfo &&message_info, bool from_update, bool is_channel_message,
bool have_previous, bool have_next, const char *source);
@@ -1917,9 +1919,9 @@ class MessagesManager final : public Actor {
void add_postponed_channel_update(DialogId dialog_id, tl_object_ptr &&update, int32 new_pts,
int32 pts_count, Promise &&promise);
- void process_channel_update(tl_object_ptr &&update);
+ void process_channel_update(tl_object_ptr &&update_ptr);
- void on_message_edited(FullMessageId full_message_id, int32 pts);
+ void on_message_edited(FullMessageId full_message_id, int32 pts, bool had_message);
void delete_messages_from_updates(const vector &message_ids);
@@ -2031,6 +2033,15 @@ class MessagesManager final : public Actor {
bool can_get_message_statistics(DialogId dialog_id, const Message *m) const;
+ struct CanDeleteDialog {
+ bool for_self_;
+ bool for_all_users_;
+
+ CanDeleteDialog(bool for_self, bool for_all_users) : for_self_(for_self), for_all_users_(for_all_users) {
+ }
+ };
+ CanDeleteDialog can_delete_dialog(const Dialog *d) const;
+
static bool can_delete_channel_message(const DialogParticipantStatus &status, const Message *m, bool is_bot);
bool can_delete_message(DialogId dialog_id, const Message *m) const;
@@ -2449,7 +2460,7 @@ class MessagesManager final : public Actor {
void send_update_chat_unread_mention_count(const Dialog *d);
- void send_update_chat_unread_reaction_count(const Dialog *d);
+ void send_update_chat_unread_reaction_count(const Dialog *d, const char *source);
void send_update_chat_position(DialogListId dialog_list_id, const Dialog *d, const char *source) const;
@@ -3419,6 +3430,7 @@ class MessagesManager final : public Actor {
bool running_get_difference_ = false; // true after before_get_difference and false after after_get_difference
FlatHashMap, DialogIdHash> dialogs_;
+ int64 added_message_count_ = 0;
FlatHashSet loaded_dialogs_; // dialogs loaded from database, but not added to dialogs_
diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp
index 3c16b3634..a07c6c1ab 100644
--- a/td/telegram/NotificationManager.cpp
+++ b/td/telegram/NotificationManager.cpp
@@ -204,7 +204,7 @@ void NotificationManager::start_up() {
}
void NotificationManager::init() {
- if (is_disabled()) {
+ if (is_disabled() || is_inited_) {
return;
}
diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp
index b8aeed011..df1aff573 100644
--- a/td/telegram/Photo.cpp
+++ b/td/telegram/Photo.cpp
@@ -70,18 +70,8 @@ tl_object_ptr get_profile_photo_object(FileManager *file_m
profile_photo.has_animation);
}
-bool operator==(const ProfilePhoto &lhs, const ProfilePhoto &rhs) {
- bool location_differs = lhs.small_file_id != rhs.small_file_id || lhs.big_file_id != rhs.big_file_id;
- bool id_differs = lhs.id != rhs.id;
-
- if (location_differs) {
- return false;
- }
- return lhs.has_animation == rhs.has_animation && lhs.minithumbnail == rhs.minithumbnail && !id_differs;
-}
-
-bool operator!=(const ProfilePhoto &lhs, const ProfilePhoto &rhs) {
- return !(lhs == rhs);
+bool need_update_profile_photo(const ProfilePhoto &from, const ProfilePhoto &to) {
+ return from.id != to.id || need_update_dialog_photo(from, to);
}
StringBuilder &operator<<(StringBuilder &string_builder, const ProfilePhoto &profile_photo) {
@@ -163,9 +153,10 @@ DialogPhoto as_fake_dialog_photo(const Photo &photo, DialogId dialog_id) {
return result;
}
-ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo) {
- ProfilePhoto result;
- static_cast(result) = as_fake_dialog_photo(photo, DialogId(user_id));
+DialogPhoto as_dialog_photo(FileManager *file_manager, DialogId dialog_id, int64 dialog_access_hash,
+ const Photo &photo) {
+ DialogPhoto result;
+ static_cast(result) = as_fake_dialog_photo(photo, dialog_id);
if (!result.small_file_id.is_valid()) {
return result;
}
@@ -176,25 +167,40 @@ ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 u
auto remote = file_view.remote_location();
CHECK(remote.is_photo());
CHECK(!remote.is_web());
- remote.set_source(PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, is_big));
- return file_manager->register_remote(std::move(remote), FileLocationSource::FromServer, DialogId(),
- file_view.size(), file_view.expected_size(), file_view.remote_name());
+ remote.set_source(PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, is_big));
+ return file_manager->register_remote(std::move(remote), FileLocationSource::FromServer, DialogId(), 0, 0,
+ file_view.remote_name());
};
- result.id = photo.id.get();
result.small_file_id = reregister_photo(false, result.small_file_id);
result.big_file_id = reregister_photo(true, result.big_file_id);
return result;
}
-bool operator==(const DialogPhoto &lhs, const DialogPhoto &rhs) {
- return lhs.small_file_id == rhs.small_file_id && lhs.big_file_id == rhs.big_file_id &&
- lhs.minithumbnail == rhs.minithumbnail && lhs.has_animation == rhs.has_animation;
+ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo) {
+ ProfilePhoto result;
+ static_cast(result) = as_dialog_photo(file_manager, DialogId(user_id), user_access_hash, photo);
+ if (result.small_file_id.is_valid()) {
+ result.id = photo.id.get();
+ }
+ return result;
}
-bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs) {
- return !(lhs == rhs);
+bool is_same_dialog_photo(FileManager *file_manager, DialogId dialog_id, const Photo &photo,
+ const DialogPhoto &dialog_photo) {
+ auto get_unique_file_id = [file_manager](FileId file_id) {
+ return file_manager->get_file_view(file_id).get_unique_file_id();
+ };
+ auto fake_photo = as_fake_dialog_photo(photo, dialog_id);
+ return get_unique_file_id(fake_photo.small_file_id) == get_unique_file_id(dialog_photo.small_file_id) &&
+ get_unique_file_id(fake_photo.big_file_id) == get_unique_file_id(dialog_photo.big_file_id);
+}
+
+bool need_update_dialog_photo(const DialogPhoto &from, const DialogPhoto &to) {
+ return from.small_file_id != to.small_file_id || from.big_file_id != to.big_file_id ||
+ from.has_animation != to.has_animation ||
+ need_update_dialog_photo_minithumbnail(from.minithumbnail, to.minithumbnail);
}
StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo) {
diff --git a/td/telegram/Photo.h b/td/telegram/Photo.h
index 0b2953082..577ad4e47 100644
--- a/td/telegram/Photo.h
+++ b/td/telegram/Photo.h
@@ -57,8 +57,7 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64
tl_object_ptr get_profile_photo_object(FileManager *file_manager,
const ProfilePhoto &profile_photo);
-bool operator==(const ProfilePhoto &lhs, const ProfilePhoto &rhs);
-bool operator!=(const ProfilePhoto &lhs, const ProfilePhoto &rhs);
+bool need_update_profile_photo(const ProfilePhoto &from, const ProfilePhoto &to);
StringBuilder &operator<<(StringBuilder &string_builder, const ProfilePhoto &profile_photo);
@@ -69,12 +68,17 @@ tl_object_ptr get_chat_photo_info_object(FileManager *fil
DialogPhoto as_fake_dialog_photo(const Photo &photo, DialogId dialog_id);
+DialogPhoto as_dialog_photo(FileManager *file_manager, DialogId dialog_id, int64 dialog_access_hash,
+ const Photo &photo);
+
ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo);
+bool is_same_dialog_photo(FileManager *file_manager, DialogId dialog_id, const Photo &photo,
+ const DialogPhoto &dialog_photo);
+
vector dialog_photo_get_file_ids(const DialogPhoto &dialog_photo);
-bool operator==(const DialogPhoto &lhs, const DialogPhoto &rhs);
-bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs);
+bool need_update_dialog_photo(const DialogPhoto &from, const DialogPhoto &to);
StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo);
diff --git a/td/telegram/PhotoSize.cpp b/td/telegram/PhotoSize.cpp
index db1688c72..4772c4d39 100644
--- a/td/telegram/PhotoSize.cpp
+++ b/td/telegram/PhotoSize.cpp
@@ -57,6 +57,27 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimen
return string_builder << "(" << dimensions.width << ", " << dimensions.height << ")";
}
+static int32 get_minithumbnail_size(const string &packed) {
+ if (packed.size() < 3) {
+ return 0;
+ }
+ if (packed[0] == '\x01') {
+ return max(static_cast(packed[1]), static_cast