1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/NotificationManager.h"
8 
9 #include "td/telegram/AuthManager.h"
10 #include "td/telegram/ChannelId.h"
11 #include "td/telegram/ChatId.h"
12 #include "td/telegram/ConfigShared.h"
13 #include "td/telegram/ContactsManager.h"
14 #include "td/telegram/DeviceTokenManager.h"
15 #include "td/telegram/Document.h"
16 #include "td/telegram/Document.hpp"
17 #include "td/telegram/DocumentsManager.h"
18 #include "td/telegram/files/FileManager.h"
19 #include "td/telegram/Global.h"
20 #include "td/telegram/logevent/LogEvent.h"
21 #include "td/telegram/MessageSender.h"
22 #include "td/telegram/MessagesManager.h"
23 #include "td/telegram/misc.h"
24 #include "td/telegram/net/ConnectionCreator.h"
25 #include "td/telegram/net/DcId.h"
26 #include "td/telegram/Photo.h"
27 #include "td/telegram/Photo.hpp"
28 #include "td/telegram/SecretChatId.h"
29 #include "td/telegram/ServerMessageId.h"
30 #include "td/telegram/StateManager.h"
31 #include "td/telegram/Td.h"
32 #include "td/telegram/TdDb.h"
33 #include "td/telegram/TdParameters.h"
34 #include "td/telegram/telegram_api.h"
35 
36 #include "td/mtproto/AuthKey.h"
37 #include "td/mtproto/mtproto_api.h"
38 #include "td/mtproto/PacketInfo.h"
39 #include "td/mtproto/Transport.h"
40 
41 #include "td/db/binlog/BinlogEvent.h"
42 #include "td/db/binlog/BinlogHelper.h"
43 
44 #include "td/actor/SleepActor.h"
45 
46 #include "td/utils/algorithm.h"
47 #include "td/utils/as.h"
48 #include "td/utils/base64.h"
49 #include "td/utils/buffer.h"
50 #include "td/utils/format.h"
51 #include "td/utils/Gzip.h"
52 #include "td/utils/JsonBuilder.h"
53 #include "td/utils/logging.h"
54 #include "td/utils/misc.h"
55 #include "td/utils/Slice.h"
56 #include "td/utils/SliceBuilder.h"
57 #include "td/utils/Time.h"
58 #include "td/utils/tl_helpers.h"
59 #include "td/utils/tl_parsers.h"
60 #include "td/utils/utf8.h"
61 
62 #include <algorithm>
63 #include <iterator>
64 #include <limits>
65 #include <unordered_map>
66 #include <unordered_set>
67 
68 namespace td {
69 
70 int VERBOSITY_NAME(notifications) = VERBOSITY_NAME(INFO);
71 
72 class SetContactSignUpNotificationQuery final : public Td::ResultHandler {
73   Promise<Unit> promise_;
74 
75  public:
SetContactSignUpNotificationQuery(Promise<Unit> && promise)76   explicit SetContactSignUpNotificationQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
77   }
78 
send(bool is_disabled)79   void send(bool is_disabled) {
80     send_query(G()->net_query_creator().create(telegram_api::account_setContactSignUpNotification(is_disabled)));
81   }
82 
on_result(BufferSlice packet)83   void on_result(BufferSlice packet) final {
84     auto result_ptr = fetch_result<telegram_api::account_setContactSignUpNotification>(packet);
85     if (result_ptr.is_error()) {
86       return on_error(result_ptr.move_as_error());
87     }
88 
89     promise_.set_value(Unit());
90   }
91 
on_error(Status status)92   void on_error(Status status) final {
93     if (!G()->is_expected_error(status)) {
94       LOG(ERROR) << "Receive error for set contact sign up notification: " << status;
95     }
96     promise_.set_error(std::move(status));
97   }
98 };
99 
100 class GetContactSignUpNotificationQuery final : public Td::ResultHandler {
101   Promise<Unit> promise_;
102 
103  public:
GetContactSignUpNotificationQuery(Promise<Unit> && promise)104   explicit GetContactSignUpNotificationQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
105   }
106 
send()107   void send() {
108     send_query(G()->net_query_creator().create(telegram_api::account_getContactSignUpNotification()));
109   }
110 
on_result(BufferSlice packet)111   void on_result(BufferSlice packet) final {
112     auto result_ptr = fetch_result<telegram_api::account_getContactSignUpNotification>(packet);
113     if (result_ptr.is_error()) {
114       return on_error(result_ptr.move_as_error());
115     }
116 
117     td_->notification_manager_->on_get_disable_contact_registered_notifications(result_ptr.ok());
118     promise_.set_value(Unit());
119   }
120 
on_error(Status status)121   void on_error(Status status) final {
122     if (!G()->is_expected_error(status)) {
123       LOG(ERROR) << "Receive error for get contact sign up notification: " << status;
124     }
125     promise_.set_error(std::move(status));
126   }
127 };
128 
NotificationManager(Td * td,ActorShared<> parent)129 NotificationManager::NotificationManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
130   flush_pending_notifications_timeout_.set_callback(on_flush_pending_notifications_timeout_callback);
131   flush_pending_notifications_timeout_.set_callback_data(static_cast<void *>(this));
132 
133   flush_pending_updates_timeout_.set_callback(on_flush_pending_updates_timeout_callback);
134   flush_pending_updates_timeout_.set_callback_data(static_cast<void *>(this));
135 }
136 
on_flush_pending_notifications_timeout_callback(void * notification_manager_ptr,int64 group_id_int)137 void NotificationManager::on_flush_pending_notifications_timeout_callback(void *notification_manager_ptr,
138                                                                           int64 group_id_int) {
139   if (G()->close_flag()) {
140     return;
141   }
142 
143   auto notification_manager = static_cast<NotificationManager *>(notification_manager_ptr);
144   VLOG(notifications) << "Ready to flush pending notifications for notification group " << group_id_int;
145   if (group_id_int > 0) {
146     send_closure_later(notification_manager->actor_id(notification_manager),
147                        &NotificationManager::flush_pending_notifications,
148                        NotificationGroupId(narrow_cast<int32>(group_id_int)));
149   } else if (group_id_int == 0) {
150     send_closure_later(notification_manager->actor_id(notification_manager),
151                        &NotificationManager::after_get_difference_impl);
152   } else {
153     send_closure_later(notification_manager->actor_id(notification_manager),
154                        &NotificationManager::after_get_chat_difference_impl,
155                        NotificationGroupId(narrow_cast<int32>(-group_id_int)));
156   }
157 }
158 
on_flush_pending_updates_timeout_callback(void * notification_manager_ptr,int64 group_id_int)159 void NotificationManager::on_flush_pending_updates_timeout_callback(void *notification_manager_ptr,
160                                                                     int64 group_id_int) {
161   if (G()->close_flag()) {
162     return;
163   }
164 
165   auto notification_manager = static_cast<NotificationManager *>(notification_manager_ptr);
166   send_closure_later(notification_manager->actor_id(notification_manager), &NotificationManager::flush_pending_updates,
167                      narrow_cast<int32>(group_id_int), "timeout");
168 }
169 
is_disabled() const170 bool NotificationManager::is_disabled() const {
171   return !td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot() || G()->close_flag();
172 }
173 
operator <<(StringBuilder & string_builder,const NotificationManager::ActiveNotificationsUpdate & update)174 StringBuilder &operator<<(StringBuilder &string_builder, const NotificationManager::ActiveNotificationsUpdate &update) {
175   if (update.update == nullptr) {
176     return string_builder << "null";
177   }
178   string_builder << "update[\n";
179   for (auto &group : update.update->groups_) {
180     vector<int32> added_notification_ids;
181     for (auto &notification : group->notifications_) {
182       added_notification_ids.push_back(notification->id_);
183     }
184 
185     string_builder << "    [" << NotificationGroupId(group->id_) << " of type "
186                    << get_notification_group_type(group->type_) << " from " << DialogId(group->chat_id_)
187                    << "; total_count = " << group->total_count_ << ", restore " << added_notification_ids << "]\n";
188   }
189   return string_builder << ']';
190 }
191 
as_active_notifications_update(const td_api::updateActiveNotifications * update)192 NotificationManager::ActiveNotificationsUpdate NotificationManager::as_active_notifications_update(
193     const td_api::updateActiveNotifications *update) {
194   return ActiveNotificationsUpdate{update};
195 }
196 
get_is_contact_registered_notifications_synchronized_key()197 string NotificationManager::get_is_contact_registered_notifications_synchronized_key() {
198   return "notifications_contact_registered_sync_state";
199 }
200 
start_up()201 void NotificationManager::start_up() {
202   init();
203 }
204 
init()205 void NotificationManager::init() {
206   if (is_disabled()) {
207     return;
208   }
209 
210   disable_contact_registered_notifications_ =
211       G()->shared_config().get_option_boolean("disable_contact_registered_notifications");
212   auto sync_state = G()->td_db()->get_binlog_pmc()->get(get_is_contact_registered_notifications_synchronized_key());
213   if (sync_state.empty()) {
214     sync_state = "00";
215   }
216   contact_registered_notifications_sync_state_ = static_cast<SyncState>(sync_state[0] - '0');
217   VLOG(notifications) << "Loaded disable_contact_registered_notifications = "
218                       << disable_contact_registered_notifications_ << " in state " << sync_state;
219   if (contact_registered_notifications_sync_state_ != SyncState::Completed ||
220       sync_state[1] != static_cast<int32>(disable_contact_registered_notifications_) + '0') {
221     run_contact_registered_notifications_sync();
222   }
223 
224   current_notification_id_ =
225       NotificationId(to_integer<int32>(G()->td_db()->get_binlog_pmc()->get("notification_id_current")));
226   current_notification_group_id_ =
227       NotificationGroupId(to_integer<int32>(G()->td_db()->get_binlog_pmc()->get("notification_group_id_current")));
228 
229   VLOG(notifications) << "Loaded current " << current_notification_id_ << " and " << current_notification_group_id_;
230 
231   on_notification_group_count_max_changed(false);
232   on_notification_group_size_max_changed();
233 
234   on_online_cloud_timeout_changed();
235   on_notification_cloud_delay_changed();
236   on_notification_default_delay_changed();
237 
238   last_loaded_notification_group_key_.last_notification_date = std::numeric_limits<int32>::max();
239   if (max_notification_group_count_ != 0) {
240     int32 loaded_groups = 0;
241     auto needed_groups = static_cast<int32>(max_notification_group_count_);
242     do {
243       loaded_groups += load_message_notification_groups_from_database(needed_groups, false);
244     } while (loaded_groups < needed_groups && last_loaded_notification_group_key_.last_notification_date != 0);
245   }
246 
247   auto call_notification_group_ids_string = G()->td_db()->get_binlog_pmc()->get("notification_call_group_ids");
248   if (!call_notification_group_ids_string.empty()) {
249     auto call_notification_group_ids = transform(full_split(call_notification_group_ids_string, ','), [](Slice str) {
250       return NotificationGroupId{to_integer_safe<int32>(str).ok()};
251     });
252     VLOG(notifications) << "Load call_notification_group_ids = " << call_notification_group_ids;
253     for (auto &group_id : call_notification_group_ids) {
254       if (group_id.get() > current_notification_group_id_.get()) {
255         LOG(ERROR) << "Fix current notification group identifier from " << current_notification_group_id_ << " to "
256                    << group_id;
257         current_notification_group_id_ = group_id;
258         G()->td_db()->get_binlog_pmc()->set("notification_group_id_current",
259                                             to_string(current_notification_group_id_.get()));
260       }
261       auto it = get_group_force(group_id);
262       if (it != groups_.end()) {
263         LOG(ERROR) << "Have " << it->first << " " << it->second << " as a call notification group";
264       } else {
265         call_notification_group_ids_.push_back(group_id);
266         available_call_notification_group_ids_.insert(group_id);
267       }
268     }
269   }
270 
271   auto notification_announcement_ids_string = G()->td_db()->get_binlog_pmc()->get("notification_announcement_ids");
272   if (!notification_announcement_ids_string.empty()) {
273     VLOG(notifications) << "Load announcement ids = " << notification_announcement_ids_string;
274     auto ids = transform(full_split(notification_announcement_ids_string, ','),
275                          [](Slice str) { return to_integer_safe<int32>(str).ok(); });
276     CHECK(ids.size() % 2 == 0);
277     bool is_changed = false;
278     auto min_date = G()->unix_time() - ANNOUNCEMENT_ID_CACHE_TIME;
279     for (size_t i = 0; i < ids.size(); i += 2) {
280       auto id = ids[i];
281       auto date = ids[i + 1];
282       if (date < min_date) {
283         is_changed = true;
284         continue;
285       }
286       announcement_id_date_.emplace(id, date);
287     }
288     if (is_changed) {
289       save_announcement_ids();
290     }
291   }
292 
293   class StateCallback final : public StateManager::Callback {
294    public:
295     explicit StateCallback(ActorId<NotificationManager> parent) : parent_(std::move(parent)) {
296     }
297     bool on_online(bool is_online) final {
298       if (is_online) {
299         send_closure(parent_, &NotificationManager::flush_all_pending_notifications);
300       }
301       return parent_.is_alive();
302     }
303 
304    private:
305     ActorId<NotificationManager> parent_;
306   };
307   send_closure(G()->state_manager(), &StateManager::add_callback, make_unique<StateCallback>(actor_id(this)));
308 
309   is_inited_ = true;
310   try_send_update_active_notifications();
311 }
312 
save_announcement_ids()313 void NotificationManager::save_announcement_ids() {
314   auto min_date = G()->unix_time() - ANNOUNCEMENT_ID_CACHE_TIME;
315   vector<int32> ids;
316   for (auto &it : announcement_id_date_) {
317     auto id = it.first;
318     auto date = it.second;
319     if (date < min_date) {
320       continue;
321     }
322     ids.push_back(id);
323     ids.push_back(date);
324   }
325 
326   VLOG(notifications) << "Save announcement ids " << ids;
327   if (ids.empty()) {
328     G()->td_db()->get_binlog_pmc()->erase("notification_announcement_ids");
329     return;
330   }
331 
332   auto notification_announcement_ids_string = implode(transform(ids, [](int32 id) { return to_string(id); }), ',');
333   G()->td_db()->get_binlog_pmc()->set("notification_announcement_ids", notification_announcement_ids_string);
334 }
335 
get_update_active_notifications() const336 td_api::object_ptr<td_api::updateActiveNotifications> NotificationManager::get_update_active_notifications() const {
337   auto needed_groups = max_notification_group_count_;
338   vector<td_api::object_ptr<td_api::notificationGroup>> groups;
339   for (auto &group : groups_) {
340     if (needed_groups == 0 || group.first.last_notification_date == 0) {
341       break;
342     }
343     needed_groups--;
344 
345     vector<td_api::object_ptr<td_api::notification>> notifications;
346     for (auto &notification : reversed(group.second.notifications)) {
347       auto notification_object = get_notification_object(group.first.dialog_id, notification);
348       if (notification_object->type_ != nullptr) {
349         notifications.push_back(std::move(notification_object));
350       }
351       if (notifications.size() == max_notification_group_size_) {
352         break;
353       }
354     }
355     if (!notifications.empty()) {
356       std::reverse(notifications.begin(), notifications.end());
357       groups.push_back(td_api::make_object<td_api::notificationGroup>(
358           group.first.group_id.get(), get_notification_group_type_object(group.second.type),
359           group.first.dialog_id.get(), group.second.total_count, std::move(notifications)));
360     }
361   }
362 
363   return td_api::make_object<td_api::updateActiveNotifications>(std::move(groups));
364 }
365 
tear_down()366 void NotificationManager::tear_down() {
367   parent_.reset();
368 }
369 
add_group(NotificationGroupKey && group_key,NotificationGroup && group,const char * source)370 NotificationManager::NotificationGroups::iterator NotificationManager::add_group(NotificationGroupKey &&group_key,
371                                                                                  NotificationGroup &&group,
372                                                                                  const char *source) {
373   if (group.notifications.empty()) {
374     LOG_CHECK(group_key.last_notification_date == 0) << "Trying to add empty " << group_key << " from " << source;
375   }
376   bool is_inserted = group_keys_.emplace(group_key.group_id, group_key).second;
377   CHECK(is_inserted);
378   return groups_.emplace(std::move(group_key), std::move(group)).first;
379 }
380 
get_group(NotificationGroupId group_id)381 NotificationManager::NotificationGroups::iterator NotificationManager::get_group(NotificationGroupId group_id) {
382   auto group_keys_it = group_keys_.find(group_id);
383   if (group_keys_it != group_keys_.end()) {
384     return groups_.find(group_keys_it->second);
385   }
386   return groups_.end();
387 }
388 
load_group_force(NotificationGroupId group_id)389 void NotificationManager::load_group_force(NotificationGroupId group_id) {
390   if (is_disabled() || max_notification_group_count_ == 0) {
391     return;
392   }
393 
394   auto group_it = get_group_force(group_id, true);
395   CHECK(group_it != groups_.end());
396 }
397 
get_group_force(NotificationGroupId group_id,bool send_update)398 NotificationManager::NotificationGroups::iterator NotificationManager::get_group_force(NotificationGroupId group_id,
399                                                                                        bool send_update) {
400   auto group_it = get_group(group_id);
401   if (group_it != groups_.end()) {
402     return group_it;
403   }
404 
405   if (td::contains(call_notification_group_ids_, group_id)) {
406     return groups_.end();
407   }
408 
409   auto message_group = td_->messages_manager_->get_message_notification_group_force(group_id);
410   if (!message_group.dialog_id.is_valid()) {
411     return groups_.end();
412   }
413 
414   NotificationGroupKey group_key(group_id, message_group.dialog_id, 0);
415   for (auto &notification : message_group.notifications) {
416     if (notification.date > group_key.last_notification_date) {
417       group_key.last_notification_date = notification.date;
418     }
419     if (notification.notification_id.get() > current_notification_id_.get()) {
420       LOG(ERROR) << "Fix current notification identifier from " << current_notification_id_ << " to "
421                  << notification.notification_id;
422       current_notification_id_ = notification.notification_id;
423       G()->td_db()->get_binlog_pmc()->set("notification_id_current", to_string(current_notification_id_.get()));
424     }
425   }
426   if (group_id.get() > current_notification_group_id_.get()) {
427     LOG(ERROR) << "Fix current notification group identifier from " << current_notification_group_id_ << " to "
428                << group_id;
429     current_notification_group_id_ = group_id;
430     G()->td_db()->get_binlog_pmc()->set("notification_group_id_current",
431                                         to_string(current_notification_group_id_.get()));
432   }
433 
434   NotificationGroup group;
435   group.type = message_group.type;
436   group.total_count = message_group.total_count;
437   group.notifications = std::move(message_group.notifications);
438 
439   VLOG(notifications) << "Finish to load " << group_id << " of type " << message_group.type << " with total_count "
440                       << message_group.total_count << " and notifications " << group.notifications;
441 
442   if (send_update && group_key.last_notification_date != 0) {
443     auto last_group_key = get_last_updated_group_key();
444     if (group_key < last_group_key) {
445       if (last_group_key.last_notification_date != 0) {
446         send_remove_group_update(last_group_key, groups_[last_group_key], vector<int32>());
447       }
448       send_add_group_update(group_key, group);
449     }
450   }
451   return add_group(std::move(group_key), std::move(group), "get_group_force");
452 }
453 
delete_group(NotificationGroups::iterator && group_it)454 void NotificationManager::delete_group(NotificationGroups::iterator &&group_it) {
455   auto erased_count = group_keys_.erase(group_it->first.group_id);
456   CHECK(erased_count > 0);
457   groups_.erase(group_it);
458 }
459 
load_message_notification_groups_from_database(int32 limit,bool send_update)460 int32 NotificationManager::load_message_notification_groups_from_database(int32 limit, bool send_update) {
461   CHECK(limit > 0);
462   if (last_loaded_notification_group_key_.last_notification_date == 0) {
463     // everything was already loaded
464     return 0;
465   }
466 
467   vector<NotificationGroupKey> group_keys = td_->messages_manager_->get_message_notification_group_keys_from_database(
468       last_loaded_notification_group_key_, limit);
469   last_loaded_notification_group_key_ =
470       group_keys.size() == static_cast<size_t>(limit) ? group_keys.back() : NotificationGroupKey();
471 
472   int32 result = 0;
473   for (auto &group_key : group_keys) {
474     auto group_it = get_group_force(group_key.group_id, send_update);
475     LOG_CHECK(group_it != groups_.end()) << call_notification_group_ids_ << " " << group_keys << " "
476                                          << current_notification_group_id_ << " " << limit;
477     CHECK(group_it->first.dialog_id.is_valid());
478     if (!(last_loaded_notification_group_key_ < group_it->first)) {
479       result++;
480     }
481   }
482   return result;
483 }
484 
get_first_notification_id(const NotificationGroup & group)485 NotificationId NotificationManager::get_first_notification_id(const NotificationGroup &group) {
486   if (!group.notifications.empty()) {
487     return group.notifications[0].notification_id;
488   }
489   if (!group.pending_notifications.empty()) {
490     return group.pending_notifications[0].notification_id;
491   }
492   return NotificationId();
493 }
494 
get_last_notification_id(const NotificationGroup & group)495 NotificationId NotificationManager::get_last_notification_id(const NotificationGroup &group) {
496   if (!group.pending_notifications.empty()) {
497     return group.pending_notifications.back().notification_id;
498   }
499   if (!group.notifications.empty()) {
500     return group.notifications.back().notification_id;
501   }
502   return NotificationId();
503 }
504 
get_first_message_id(const NotificationGroup & group)505 MessageId NotificationManager::get_first_message_id(const NotificationGroup &group) {
506   // it's fine to return MessageId() if first notification has no message_id, because
507   // non-message notification can't be mixed with message notifications
508   if (!group.notifications.empty()) {
509     return group.notifications[0].type->get_message_id();
510   }
511   if (!group.pending_notifications.empty()) {
512     return group.pending_notifications[0].type->get_message_id();
513   }
514   return MessageId();
515 }
516 
get_last_message_id(const NotificationGroup & group)517 MessageId NotificationManager::get_last_message_id(const NotificationGroup &group) {
518   // it's fine to return MessageId() if last notification has no message_id, because
519   // non-message notification can't be mixed with message notifications
520   if (!group.pending_notifications.empty()) {
521     return group.pending_notifications.back().type->get_message_id();
522   }
523   if (!group.notifications.empty()) {
524     return group.notifications.back().type->get_message_id();
525   }
526   return MessageId();
527 }
528 
get_last_message_id_by_notification_id(const NotificationGroup & group,NotificationId max_notification_id)529 MessageId NotificationManager::get_last_message_id_by_notification_id(const NotificationGroup &group,
530                                                                       NotificationId max_notification_id) {
531   for (auto &notification : reversed(group.pending_notifications)) {
532     if (notification.notification_id.get() <= max_notification_id.get()) {
533       auto message_id = notification.type->get_message_id();
534       if (message_id.is_valid()) {
535         return message_id;
536       }
537     }
538   }
539   for (auto &notification : reversed(group.notifications)) {
540     if (notification.notification_id.get() <= max_notification_id.get()) {
541       auto message_id = notification.type->get_message_id();
542       if (message_id.is_valid()) {
543         return message_id;
544       }
545     }
546   }
547   return MessageId();
548 }
549 
load_message_notifications_from_database(const NotificationGroupKey & group_key,NotificationGroup & group,size_t desired_size)550 void NotificationManager::load_message_notifications_from_database(const NotificationGroupKey &group_key,
551                                                                    NotificationGroup &group, size_t desired_size) {
552   if (!G()->parameters().use_message_db) {
553     return;
554   }
555   if (group.is_loaded_from_database || group.is_being_loaded_from_database ||
556       group.type == NotificationGroupType::Calls) {
557     return;
558   }
559   if (group.total_count == 0) {
560     return;
561   }
562 
563   VLOG(notifications) << "Trying to load up to " << desired_size << " notifications in " << group_key.group_id
564                       << " with " << group.notifications.size() << " current notifications";
565 
566   group.is_being_loaded_from_database = true;
567 
568   CHECK(desired_size > group.notifications.size());
569   size_t limit = desired_size - group.notifications.size();
570   auto first_notification_id = get_first_notification_id(group);
571   auto from_notification_id = first_notification_id.is_valid() ? first_notification_id : NotificationId::max();
572   auto first_message_id = get_first_message_id(group);
573   auto from_message_id = first_message_id.is_valid() ? first_message_id : MessageId::max();
574   send_closure(G()->messages_manager(), &MessagesManager::get_message_notifications_from_database, group_key.dialog_id,
575                group_key.group_id, from_notification_id, from_message_id, static_cast<int32>(limit),
576                PromiseCreator::lambda([actor_id = actor_id(this), group_id = group_key.group_id,
577                                        limit](Result<vector<Notification>> r_notifications) {
578                  send_closure_later(actor_id, &NotificationManager::on_get_message_notifications_from_database,
579                                     group_id, limit, std::move(r_notifications));
580                }));
581 }
582 
on_get_message_notifications_from_database(NotificationGroupId group_id,size_t limit,Result<vector<Notification>> r_notifications)583 void NotificationManager::on_get_message_notifications_from_database(NotificationGroupId group_id, size_t limit,
584                                                                      Result<vector<Notification>> r_notifications) {
585   auto group_it = get_group(group_id);
586   CHECK(group_it != groups_.end());
587   auto &group = group_it->second;
588   CHECK(group.is_being_loaded_from_database == true);
589   group.is_being_loaded_from_database = false;
590 
591   if (r_notifications.is_error()) {
592     group.is_loaded_from_database = true;  // do not try again to load it
593     return;
594   }
595   auto notifications = r_notifications.move_as_ok();
596 
597   CHECK(limit > 0);
598   if (notifications.empty()) {
599     group.is_loaded_from_database = true;
600   }
601 
602   auto first_notification_id = get_first_notification_id(group);
603   if (first_notification_id.is_valid()) {
604     while (!notifications.empty() && notifications.back().notification_id.get() >= first_notification_id.get()) {
605       // possible if notifications was added after the database request was sent
606       notifications.pop_back();
607     }
608   }
609   auto first_message_id = get_first_message_id(group);
610   if (first_message_id.is_valid()) {
611     while (!notifications.empty() && notifications.back().type->get_message_id() >= first_message_id) {
612       // possible if notifications was added after the database request was sent
613       notifications.pop_back();
614     }
615   }
616 
617   add_notifications_to_group_begin(std::move(group_it), std::move(notifications));
618 
619   group_it = get_group(group_id);
620   CHECK(group_it != groups_.end());
621   if (max_notification_group_size_ > group_it->second.notifications.size()) {
622     load_message_notifications_from_database(group_it->first, group_it->second, keep_notification_group_size_);
623   }
624 }
625 
add_notifications_to_group_begin(NotificationGroups::iterator group_it,vector<Notification> notifications)626 void NotificationManager::add_notifications_to_group_begin(NotificationGroups::iterator group_it,
627                                                            vector<Notification> notifications) {
628   CHECK(group_it != groups_.end());
629 
630   td::remove_if(notifications, [dialog_id = group_it->first.dialog_id](const Notification &notification) {
631     return notification.type->get_notification_type_object(dialog_id) == nullptr;
632   });
633 
634   if (notifications.empty()) {
635     return;
636   }
637   VLOG(notifications) << "Add to beginning of " << group_it->first << " of size "
638                       << group_it->second.notifications.size() << ' ' << notifications;
639 
640   auto group_key = group_it->first;
641   auto final_group_key = group_key;
642   for (auto &notification : notifications) {
643     if (notification.date > final_group_key.last_notification_date) {
644       final_group_key.last_notification_date = notification.date;
645     }
646   }
647   LOG_CHECK(final_group_key.last_notification_date != 0) << final_group_key << ' ' << *group_it << ' ' << notifications;
648 
649   bool is_position_changed = final_group_key.last_notification_date != group_key.last_notification_date;
650 
651   NotificationGroup group = std::move(group_it->second);
652   if (is_position_changed) {
653     VLOG(notifications) << "Position of notification group is changed from " << group_key << " to " << final_group_key;
654     delete_group(std::move(group_it));
655   }
656 
657   auto last_group_key = get_last_updated_group_key();
658   bool was_updated = false;
659   bool is_updated = false;
660   if (is_position_changed) {
661     was_updated = group_key.last_notification_date != 0 && group_key < last_group_key;
662     is_updated = final_group_key.last_notification_date != 0 && final_group_key < last_group_key;
663   } else {
664     CHECK(group_key.last_notification_date != 0);
665     was_updated = is_updated = !(last_group_key < group_key);
666   }
667 
668   if (!is_updated) {
669     CHECK(!was_updated);
670     VLOG(notifications) << "There is no need to send updateNotificationGroup in " << group_key
671                         << ", because of newer notification groups";
672     group.notifications.insert(group.notifications.begin(), std::make_move_iterator(notifications.begin()),
673                                std::make_move_iterator(notifications.end()));
674   } else {
675     if (!was_updated) {
676       if (last_group_key.last_notification_date != 0) {
677         // need to remove last notification group to not exceed max_notification_group_count_
678         send_remove_group_update(last_group_key, groups_[last_group_key], vector<int32>());
679       }
680       send_add_group_update(group_key, group);
681     }
682 
683     vector<Notification> new_notifications;
684     vector<td_api::object_ptr<td_api::notification>> added_notifications;
685     new_notifications.reserve(notifications.size());
686     added_notifications.reserve(notifications.size());
687     for (auto &notification : notifications) {
688       added_notifications.push_back(get_notification_object(group_key.dialog_id, notification));
689       CHECK(added_notifications.back()->type_ != nullptr);
690       new_notifications.push_back(std::move(notification));
691     }
692     notifications = std::move(new_notifications);
693 
694     size_t old_notification_count = group.notifications.size();
695     auto updated_notification_count = old_notification_count < max_notification_group_size_
696                                           ? max_notification_group_size_ - old_notification_count
697                                           : 0;
698     if (added_notifications.size() > updated_notification_count) {
699       added_notifications.erase(added_notifications.begin(), added_notifications.end() - updated_notification_count);
700     }
701     auto new_notification_count = old_notification_count < keep_notification_group_size_
702                                       ? keep_notification_group_size_ - old_notification_count
703                                       : 0;
704     if (new_notification_count > notifications.size()) {
705       new_notification_count = notifications.size();
706     }
707     if (new_notification_count != 0) {
708       VLOG(notifications) << "Add " << new_notification_count << " notifications to " << group_key.group_id
709                           << " with current size " << group.notifications.size();
710       group.notifications.insert(group.notifications.begin(),
711                                  std::make_move_iterator(notifications.end() - new_notification_count),
712                                  std::make_move_iterator(notifications.end()));
713     }
714 
715     if (!added_notifications.empty()) {
716       add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
717           group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(), 0, true,
718           group.total_count, std::move(added_notifications), vector<int32>()));
719     }
720   }
721 
722   if (is_position_changed) {
723     add_group(std::move(final_group_key), std::move(group), "add_notifications_to_group_begin");
724   } else {
725     CHECK(group_it->first.last_notification_date == 0 || !group.notifications.empty());
726     group_it->second = std::move(group);
727   }
728 }
729 
get_max_notification_group_size() const730 size_t NotificationManager::get_max_notification_group_size() const {
731   return max_notification_group_size_;
732 }
733 
get_max_notification_id() const734 NotificationId NotificationManager::get_max_notification_id() const {
735   return current_notification_id_;
736 }
737 
get_next_notification_id()738 NotificationId NotificationManager::get_next_notification_id() {
739   if (is_disabled()) {
740     return NotificationId();
741   }
742   if (current_notification_id_.get() == std::numeric_limits<int32>::max()) {
743     LOG(ERROR) << "Notification identifier overflowed";
744     return NotificationId();
745   }
746 
747   current_notification_id_ = NotificationId(current_notification_id_.get() + 1);
748   G()->td_db()->get_binlog_pmc()->set("notification_id_current", to_string(current_notification_id_.get()));
749   return current_notification_id_;
750 }
751 
get_next_notification_group_id()752 NotificationGroupId NotificationManager::get_next_notification_group_id() {
753   if (is_disabled()) {
754     return NotificationGroupId();
755   }
756   if (current_notification_group_id_.get() == std::numeric_limits<int32>::max()) {
757     LOG(ERROR) << "Notification group identifier overflowed";
758     return NotificationGroupId();
759   }
760 
761   current_notification_group_id_ = NotificationGroupId(current_notification_group_id_.get() + 1);
762   G()->td_db()->get_binlog_pmc()->set("notification_group_id_current", to_string(current_notification_group_id_.get()));
763   return current_notification_group_id_;
764 }
765 
try_reuse_notification_group_id(NotificationGroupId group_id)766 void NotificationManager::try_reuse_notification_group_id(NotificationGroupId group_id) {
767   if (is_disabled()) {
768     return;
769   }
770   if (!group_id.is_valid()) {
771     return;
772   }
773 
774   VLOG(notifications) << "Trying to reuse " << group_id;
775   if (group_id != current_notification_group_id_) {
776     // may be implemented in the future
777     return;
778   }
779 
780   auto group_it = get_group(group_id);
781   if (group_it != groups_.end()) {
782     LOG_CHECK(group_it->first.last_notification_date == 0 && group_it->second.total_count == 0)
783         << running_get_difference_ << " " << delayed_notification_update_count_ << " "
784         << unreceived_notification_update_count_ << " " << pending_updates_[group_id.get()].size() << " "
785         << group_it->first << " " << group_it->second;
786     CHECK(group_it->second.notifications.empty());
787     CHECK(group_it->second.pending_notifications.empty());
788     CHECK(!group_it->second.is_being_loaded_from_database);
789     delete_group(std::move(group_it));
790 
791     CHECK(running_get_chat_difference_.count(group_id.get()) == 0);
792 
793     flush_pending_notifications_timeout_.cancel_timeout(group_id.get());
794     flush_pending_updates_timeout_.cancel_timeout(group_id.get());
795     if (pending_updates_.erase(group_id.get()) == 1) {
796       on_delayed_notification_update_count_changed(-1, group_id.get(), "try_reuse_notification_group_id");
797     }
798   }
799 
800   current_notification_group_id_ = NotificationGroupId(current_notification_group_id_.get() - 1);
801   G()->td_db()->get_binlog_pmc()->set("notification_group_id_current", to_string(current_notification_group_id_.get()));
802 }
803 
get_last_updated_group_key() const804 NotificationGroupKey NotificationManager::get_last_updated_group_key() const {
805   size_t left = max_notification_group_count_;
806   auto it = groups_.begin();
807   while (it != groups_.end() && left > 1) {
808     ++it;
809     left--;
810   }
811   if (it == groups_.end()) {
812     return NotificationGroupKey();
813   }
814   return it->first;
815 }
816 
get_notification_delay_ms(DialogId dialog_id,const PendingNotification & notification,int32 min_delay_ms) const817 int32 NotificationManager::get_notification_delay_ms(DialogId dialog_id, const PendingNotification &notification,
818                                                      int32 min_delay_ms) const {
819   if (dialog_id.get_type() == DialogType::SecretChat) {
820     return MIN_NOTIFICATION_DELAY_MS;  // there is no reason to delay notifications in secret chats
821   }
822   if (!notification.type->can_be_delayed()) {
823     return MIN_NOTIFICATION_DELAY_MS;
824   }
825 
826   auto delay_ms = [&] {
827     auto online_info = td_->contacts_manager_->get_my_online_status();
828     if (!online_info.is_online_local && online_info.is_online_remote) {
829       // If we are offline, but online from some other client, then delay notification
830       // for 'notification_cloud_delay' seconds.
831       return notification_cloud_delay_ms_;
832     }
833 
834     if (!online_info.is_online_local &&
835         online_info.was_online_remote > max(static_cast<double>(online_info.was_online_local),
836                                             G()->server_time_cached() - online_cloud_timeout_ms_ * 1e-3)) {
837       // If we are offline, but was online from some other client in last 'online_cloud_timeout' seconds
838       // after we had gone offline, then delay notification for 'notification_cloud_delay' seconds.
839       return notification_cloud_delay_ms_;
840     }
841 
842     if (online_info.is_online_remote) {
843       // If some other client is online, then delay notification for 'notification_default_delay' seconds.
844       return notification_default_delay_ms_;
845     }
846 
847     // otherwise send update without additional delay
848     return 0;
849   }();
850 
851   auto passed_time_ms =
852       static_cast<int32>(clamp(G()->server_time_cached() - notification.date - 1, 0.0, 1000000.0) * 1000);
853   return max(max(min_delay_ms, delay_ms) - passed_time_ms, MIN_NOTIFICATION_DELAY_MS);
854 }
855 
add_notification(NotificationGroupId group_id,NotificationGroupType group_type,DialogId dialog_id,int32 date,DialogId notification_settings_dialog_id,bool initial_is_silent,bool is_silent,int32 min_delay_ms,NotificationId notification_id,unique_ptr<NotificationType> type,const char * source)856 void NotificationManager::add_notification(NotificationGroupId group_id, NotificationGroupType group_type,
857                                            DialogId dialog_id, int32 date, DialogId notification_settings_dialog_id,
858                                            bool initial_is_silent, bool is_silent, int32 min_delay_ms,
859                                            NotificationId notification_id, unique_ptr<NotificationType> type,
860                                            const char *source) {
861   if (is_disabled() || max_notification_group_count_ == 0) {
862     on_notification_removed(notification_id);
863     return;
864   }
865 
866   CHECK(group_id.is_valid());
867   CHECK(dialog_id.is_valid());
868   CHECK(notification_settings_dialog_id.is_valid());
869   LOG_CHECK(notification_id.is_valid()) << notification_id << " " << source;
870   CHECK(type != nullptr);
871   VLOG(notifications) << "Add " << notification_id << " to " << group_id << " of type " << group_type << " in "
872                       << dialog_id << " with settings from " << notification_settings_dialog_id
873                       << (is_silent ? "   silently" : " with sound") << ": " << *type;
874 
875   if (!type->is_temporary()) {
876     remove_temporary_notifications(group_id, "add_notification");
877   }
878 
879   auto group_it = get_group_force(group_id);
880   if (group_it == groups_.end()) {
881     group_it = add_group(NotificationGroupKey(group_id, dialog_id, 0), NotificationGroup(), "add_notification");
882   }
883   if (group_it->second.notifications.empty() && group_it->second.pending_notifications.empty()) {
884     group_it->second.type = group_type;
885   }
886   CHECK(group_it->second.type == group_type);
887 
888   NotificationGroup &group = group_it->second;
889   if (notification_id.get() <= get_last_notification_id(group).get()) {
890     LOG(ERROR) << "Failed to add " << notification_id << " to " << group_id << " of type " << group_type << " in "
891                << dialog_id << ", because have already added " << get_last_notification_id(group);
892     on_notification_removed(notification_id);
893     return;
894   }
895   auto message_id = type->get_message_id();
896   if (message_id.is_valid() && message_id <= get_last_message_id(group)) {
897     LOG(ERROR) << "Failed to add " << notification_id << " of type " << *type << " to " << group_id << " of type "
898                << group_type << " in " << dialog_id << ", because have already added notification about "
899                << get_last_message_id(group);
900     on_notification_removed(notification_id);
901     return;
902   }
903 
904   PendingNotification notification;
905   notification.date = date;
906   notification.settings_dialog_id = notification_settings_dialog_id;
907   notification.initial_is_silent = initial_is_silent;
908   notification.is_silent = is_silent;
909   notification.notification_id = notification_id;
910   notification.type = std::move(type);
911 
912   auto delay_ms = get_notification_delay_ms(dialog_id, notification, min_delay_ms);
913   VLOG(notifications) << "Delay " << notification_id << " for " << delay_ms << " milliseconds";
914   auto flush_time = delay_ms * 0.001 + Time::now();
915 
916   if (group.pending_notifications_flush_time == 0 || flush_time < group.pending_notifications_flush_time) {
917     group.pending_notifications_flush_time = flush_time;
918     flush_pending_notifications_timeout_.set_timeout_at(group_id.get(), group.pending_notifications_flush_time);
919   }
920   if (group.pending_notifications.empty()) {
921     on_delayed_notification_update_count_changed(1, group_id.get(), source);
922   }
923   group.pending_notifications.push_back(std::move(notification));
924 }
925 
operator <<(StringBuilder & string_builder,const NotificationManager::NotificationUpdate & update)926 StringBuilder &operator<<(StringBuilder &string_builder, const NotificationManager::NotificationUpdate &update) {
927   if (update.update == nullptr) {
928     return string_builder << "null";
929   }
930   switch (update.update->get_id()) {
931     case td_api::updateNotification::ID: {
932       auto p = static_cast<const td_api::updateNotification *>(update.update);
933       return string_builder << "update[" << NotificationId(p->notification_->id_) << " from "
934                             << NotificationGroupId(p->notification_group_id_) << ']';
935     }
936     case td_api::updateNotificationGroup::ID: {
937       auto p = static_cast<const td_api::updateNotificationGroup *>(update.update);
938       vector<int32> added_notification_ids;
939       for (auto &notification : p->added_notifications_) {
940         added_notification_ids.push_back(notification->id_);
941       }
942 
943       return string_builder << "update[" << NotificationGroupId(p->notification_group_id_) << " of type "
944                             << get_notification_group_type(p->type_) << " from " << DialogId(p->chat_id_)
945                             << " with settings from " << DialogId(p->notification_settings_chat_id_)
946                             << (p->is_silent_ ? "   silently" : " with sound") << "; total_count = " << p->total_count_
947                             << ", add " << added_notification_ids << ", remove " << p->removed_notification_ids_;
948     }
949     default:
950       UNREACHABLE();
951       return string_builder << "unknown";
952   }
953 }
954 
as_notification_update(const td_api::Update * update)955 NotificationManager::NotificationUpdate NotificationManager::as_notification_update(const td_api::Update *update) {
956   return NotificationUpdate{update};
957 }
958 
add_update(int32 group_id,td_api::object_ptr<td_api::Update> update)959 void NotificationManager::add_update(int32 group_id, td_api::object_ptr<td_api::Update> update) {
960   if (!is_binlog_processed_ || !is_inited_) {
961     return;
962   }
963   VLOG(notifications) << "Add " << as_notification_update(update.get());
964   auto &updates = pending_updates_[group_id];
965   if (updates.empty()) {
966     on_delayed_notification_update_count_changed(1, group_id, "add_update");
967   }
968   updates.push_back(std::move(update));
969   if (!running_get_difference_ && running_get_chat_difference_.count(group_id) == 0) {
970     flush_pending_updates_timeout_.add_timeout_in(group_id, MIN_UPDATE_DELAY_MS * 1e-3);
971   } else {
972     flush_pending_updates_timeout_.set_timeout_in(group_id, MAX_UPDATE_DELAY_MS * 1e-3);
973   }
974 }
975 
add_update_notification_group(td_api::object_ptr<td_api::updateNotificationGroup> update)976 void NotificationManager::add_update_notification_group(td_api::object_ptr<td_api::updateNotificationGroup> update) {
977   auto group_id = update->notification_group_id_;
978   if (update->notification_settings_chat_id_ == 0) {
979     update->notification_settings_chat_id_ = update->chat_id_;
980   }
981   add_update(group_id, std::move(update));
982 }
983 
add_update_notification(NotificationGroupId notification_group_id,DialogId dialog_id,const Notification & notification)984 void NotificationManager::add_update_notification(NotificationGroupId notification_group_id, DialogId dialog_id,
985                                                   const Notification &notification) {
986   auto notification_object = get_notification_object(dialog_id, notification);
987   if (notification_object->type_ == nullptr) {
988     return;
989   }
990 
991   add_update(notification_group_id.get(), td_api::make_object<td_api::updateNotification>(
992                                               notification_group_id.get(), std::move(notification_object)));
993   if (!notification.type->can_be_delayed()) {
994     force_flush_pending_updates(notification_group_id, "add_update_notification");
995   }
996 }
997 
flush_pending_updates(int32 group_id,const char * source)998 void NotificationManager::flush_pending_updates(int32 group_id, const char *source) {
999   // no check for G()->close_flag() to flush pending notifications even while closing
1000   auto it = pending_updates_.find(group_id);
1001   if (it == pending_updates_.end()) {
1002     return;
1003   }
1004 
1005   auto updates = std::move(it->second);
1006   pending_updates_.erase(it);
1007 
1008   if (is_destroyed_) {
1009     return;
1010   }
1011 
1012   VLOG(notifications) << "Send " << updates.size() << " pending updates in " << NotificationGroupId(group_id)
1013                       << " from " << source;
1014   for (auto &update : updates) {
1015     VLOG(notifications) << "Have " << as_notification_update(update.get());
1016   }
1017 
1018   td::remove_if(updates, [](auto &update) { return update == nullptr; });
1019 
1020   // if a notification was added, then deleted and then re-added we need to keep
1021   // first addition, because it can be with sound,
1022   // deletion, because number of notification should never exceed max_notification_group_size_,
1023   // and second addition, because we has kept the deletion
1024 
1025   // calculate last state of all notifications
1026   std::unordered_set<int32> added_notification_ids;
1027   std::unordered_set<int32> edited_notification_ids;
1028   std::unordered_set<int32> removed_notification_ids;
1029   for (auto &update : updates) {
1030     CHECK(update != nullptr);
1031     if (update->get_id() == td_api::updateNotificationGroup::ID) {
1032       auto update_ptr = static_cast<td_api::updateNotificationGroup *>(update.get());
1033       for (auto &notification : update_ptr->added_notifications_) {
1034         auto notification_id = notification->id_;
1035         bool is_inserted = added_notification_ids.insert(notification_id).second;
1036         CHECK(is_inserted);                                          // there must be no additions after addition
1037         CHECK(edited_notification_ids.count(notification_id) == 0);  // there must be no additions after edit
1038         removed_notification_ids.erase(notification_id);
1039       }
1040       for (auto &notification_id : update_ptr->removed_notification_ids_) {
1041         added_notification_ids.erase(notification_id);
1042         edited_notification_ids.erase(notification_id);
1043         if (!removed_notification_ids.insert(notification_id).second) {
1044           // sometimes there can be deletion of notification without previous addition, because the notification
1045           // has already been deleted at the time of addition and get_notification_object_type was nullptr
1046           VLOG(notifications) << "Remove duplicated deletion of " << notification_id;
1047           notification_id = 0;
1048         }
1049       }
1050       td::remove_if(update_ptr->removed_notification_ids_, [](auto &notification_id) { return notification_id == 0; });
1051     } else {
1052       CHECK(update->get_id() == td_api::updateNotification::ID);
1053       auto update_ptr = static_cast<td_api::updateNotification *>(update.get());
1054       auto notification_id = update_ptr->notification_->id_;
1055       CHECK(removed_notification_ids.count(notification_id) == 0);  // there must be no edits of deleted notifications
1056       added_notification_ids.erase(notification_id);
1057       edited_notification_ids.insert(notification_id);
1058     }
1059   }
1060 
1061   // we need to keep only additions of notifications from added_notification_ids/edited_notification_ids and
1062   // all edits of notifications from edited_notification_ids
1063   // deletions of a notification can be removed, only if the addition of the notification has already been deleted
1064   // deletions of all unkept notifications can be moved to the first updateNotificationGroup
1065   // after that at every moment there are no more active notifications than in the last moment,
1066   // so left deletions after add/edit can be safely removed and following additions can be treated as edits
1067   // we still need to keep deletions coming first, because we can't have 2 consequent additions
1068   // from all additions of the same notification, we need to preserve the first, because it can be with sound,
1069   // all other additions and edits can be merged to the first addition/edit
1070   // i.e. in edit+delete+add chain we want to remove deletion and merge addition to the edit
1071 
1072   auto group_key = group_keys_[NotificationGroupId(group_id)];
1073   bool is_hidden = group_key.last_notification_date == 0 || get_last_updated_group_key() < group_key;
1074   bool is_changed = true;
1075   while (is_changed) {
1076     is_changed = false;
1077 
1078     size_t cur_pos = 0;
1079     std::unordered_map<int32, size_t> first_add_notification_pos;
1080     std::unordered_map<int32, size_t> first_edit_notification_pos;
1081     std::unordered_set<int32> can_be_deleted_notification_ids;
1082     std::vector<int32> moved_deleted_notification_ids;
1083     size_t first_notification_group_pos = 0;
1084 
1085     for (auto &update : updates) {
1086       cur_pos++;
1087 
1088       CHECK(update != nullptr);
1089       if (update->get_id() == td_api::updateNotificationGroup::ID) {
1090         auto update_ptr = static_cast<td_api::updateNotificationGroup *>(update.get());
1091 
1092         for (auto &notification : update_ptr->added_notifications_) {
1093           auto notification_id = notification->id_;
1094           bool is_needed =
1095               added_notification_ids.count(notification_id) != 0 || edited_notification_ids.count(notification_id) != 0;
1096           if (!is_needed) {
1097             VLOG(notifications) << "Remove unneeded addition of " << notification_id << " in update " << cur_pos;
1098             can_be_deleted_notification_ids.insert(notification_id);
1099             notification = nullptr;
1100             is_changed = true;
1101             continue;
1102           }
1103 
1104           auto edit_it = first_edit_notification_pos.find(notification_id);
1105           if (edit_it != first_edit_notification_pos.end()) {
1106             VLOG(notifications) << "Move addition of " << notification_id << " in update " << cur_pos
1107                                 << " to edit in update " << edit_it->second;
1108             CHECK(edit_it->second < cur_pos);
1109             auto previous_update_ptr = static_cast<td_api::updateNotification *>(updates[edit_it->second - 1].get());
1110             CHECK(previous_update_ptr->notification_->id_ == notification_id);
1111             previous_update_ptr->notification_->type_ = std::move(notification->type_);
1112             is_changed = true;
1113             notification = nullptr;
1114             continue;
1115           }
1116           auto add_it = first_add_notification_pos.find(notification_id);
1117           if (add_it != first_add_notification_pos.end()) {
1118             VLOG(notifications) << "Move addition of " << notification_id << " in update " << cur_pos << " to update "
1119                                 << add_it->second;
1120             CHECK(add_it->second < cur_pos);
1121             auto previous_update_ptr =
1122                 static_cast<td_api::updateNotificationGroup *>(updates[add_it->second - 1].get());
1123             bool is_found = false;
1124             for (auto &prev_notification : previous_update_ptr->added_notifications_) {
1125               if (prev_notification->id_ == notification_id) {
1126                 prev_notification->type_ = std::move(notification->type_);
1127                 is_found = true;
1128                 break;
1129               }
1130             }
1131             CHECK(is_found);
1132             is_changed = true;
1133             notification = nullptr;
1134             continue;
1135           }
1136 
1137           // it is a first addition/edit of needed notification
1138           first_add_notification_pos[notification_id] = cur_pos;
1139         }
1140         td::remove_if(update_ptr->added_notifications_, [](auto &notification) { return notification == nullptr; });
1141         if (update_ptr->added_notifications_.empty() && !update_ptr->is_silent_) {
1142           update_ptr->is_silent_ = true;
1143           is_changed = true;
1144         }
1145 
1146         for (auto &notification_id : update_ptr->removed_notification_ids_) {
1147           bool is_needed =
1148               added_notification_ids.count(notification_id) != 0 || edited_notification_ids.count(notification_id) != 0;
1149           if (can_be_deleted_notification_ids.count(notification_id) == 1) {
1150             CHECK(!is_needed);
1151             VLOG(notifications) << "Remove unneeded deletion of " << notification_id << " in update " << cur_pos;
1152             notification_id = 0;
1153             is_changed = true;
1154             continue;
1155           }
1156           if (!is_needed) {
1157             if (first_notification_group_pos != 0) {
1158               VLOG(notifications) << "Need to keep deletion of " << notification_id << " in update " << cur_pos
1159                                   << ", but can move it to the first updateNotificationGroup at pos "
1160                                   << first_notification_group_pos;
1161               moved_deleted_notification_ids.push_back(notification_id);
1162               notification_id = 0;
1163               is_changed = true;
1164             }
1165             continue;
1166           }
1167 
1168           if (first_add_notification_pos.count(notification_id) != 0 ||
1169               first_edit_notification_pos.count(notification_id) != 0) {
1170             // the notification will be re-added, and we will be able to merge the addition with previous update, so we can just remove the deletion
1171             VLOG(notifications) << "Remove unneeded deletion in update " << cur_pos;
1172             notification_id = 0;
1173             is_changed = true;
1174             continue;
1175           }
1176 
1177           // we need to keep the deletion, because otherwise we will have 2 consequent additions
1178         }
1179         td::remove_if(update_ptr->removed_notification_ids_,
1180                       [](auto &notification_id) { return notification_id == 0; });
1181 
1182         if (update_ptr->removed_notification_ids_.empty() && update_ptr->added_notifications_.empty()) {
1183           for (size_t i = cur_pos - 1; i > 0; i--) {
1184             if (updates[i - 1] != nullptr && updates[i - 1]->get_id() == td_api::updateNotificationGroup::ID) {
1185               VLOG(notifications) << "Move total_count from empty update " << cur_pos << " to update " << i;
1186               auto previous_update_ptr = static_cast<td_api::updateNotificationGroup *>(updates[i - 1].get());
1187               previous_update_ptr->type_ = std::move(update_ptr->type_);
1188               previous_update_ptr->total_count_ = update_ptr->total_count_;
1189               is_changed = true;
1190               update = nullptr;
1191               break;
1192             }
1193           }
1194           if (update != nullptr && cur_pos == 1) {
1195             bool is_empty_group =
1196                 added_notification_ids.empty() && edited_notification_ids.empty() && update_ptr->total_count_ == 0;
1197             if (updates.size() > 1 || (is_hidden && !is_empty_group)) {
1198               VLOG(notifications) << "Remove empty update " << cur_pos;
1199               CHECK(moved_deleted_notification_ids.empty());
1200               is_changed = true;
1201               update = nullptr;
1202             }
1203           }
1204         }
1205 
1206         if (first_notification_group_pos == 0 && update != nullptr) {
1207           first_notification_group_pos = cur_pos;
1208         }
1209       } else {
1210         CHECK(update->get_id() == td_api::updateNotification::ID);
1211         auto update_ptr = static_cast<td_api::updateNotification *>(update.get());
1212         auto notification_id = update_ptr->notification_->id_;
1213         bool is_needed =
1214             added_notification_ids.count(notification_id) != 0 || edited_notification_ids.count(notification_id) != 0;
1215         if (!is_needed) {
1216           VLOG(notifications) << "Remove unneeded update " << cur_pos;
1217           is_changed = true;
1218           update = nullptr;
1219           continue;
1220         }
1221         auto edit_it = first_edit_notification_pos.find(notification_id);
1222         if (edit_it != first_edit_notification_pos.end()) {
1223           VLOG(notifications) << "Move edit of " << notification_id << " in update " << cur_pos << " to update "
1224                               << edit_it->second;
1225           CHECK(edit_it->second < cur_pos);
1226           auto previous_update_ptr = static_cast<td_api::updateNotification *>(updates[edit_it->second - 1].get());
1227           CHECK(previous_update_ptr->notification_->id_ == notification_id);
1228           previous_update_ptr->notification_->type_ = std::move(update_ptr->notification_->type_);
1229           is_changed = true;
1230           update = nullptr;
1231           continue;
1232         }
1233         auto add_it = first_add_notification_pos.find(notification_id);
1234         if (add_it != first_add_notification_pos.end()) {
1235           VLOG(notifications) << "Move edit of " << notification_id << " in update " << cur_pos << " to update "
1236                               << add_it->second;
1237           CHECK(add_it->second < cur_pos);
1238           auto previous_update_ptr = static_cast<td_api::updateNotificationGroup *>(updates[add_it->second - 1].get());
1239           bool is_found = false;
1240           for (auto &notification : previous_update_ptr->added_notifications_) {
1241             if (notification->id_ == notification_id) {
1242               notification->type_ = std::move(update_ptr->notification_->type_);
1243               is_found = true;
1244               break;
1245             }
1246           }
1247           CHECK(is_found);
1248           is_changed = true;
1249           update = nullptr;
1250           continue;
1251         }
1252 
1253         // it is a first addition/edit of needed notification
1254         first_edit_notification_pos[notification_id] = cur_pos;
1255       }
1256     }
1257     if (!moved_deleted_notification_ids.empty()) {
1258       CHECK(first_notification_group_pos != 0);
1259       auto &update = updates[first_notification_group_pos - 1];
1260       CHECK(update->get_id() == td_api::updateNotificationGroup::ID);
1261       auto update_ptr = static_cast<td_api::updateNotificationGroup *>(update.get());
1262       append(update_ptr->removed_notification_ids_, std::move(moved_deleted_notification_ids));
1263       auto old_size = update_ptr->removed_notification_ids_.size();
1264       td::unique(update_ptr->removed_notification_ids_);
1265       CHECK(old_size == update_ptr->removed_notification_ids_.size());
1266     }
1267 
1268     td::remove_if(updates, [](auto &update) { return update == nullptr; });
1269     if (updates.empty()) {
1270       VLOG(notifications) << "There are no updates to send in " << NotificationGroupId(group_id);
1271       break;
1272     }
1273 
1274     auto has_common_notifications = [](const vector<td_api::object_ptr<td_api::notification>> &notifications,
1275                                        const vector<int32> &notification_ids) {
1276       for (auto &notification : notifications) {
1277         if (td::contains(notification_ids, notification->id_)) {
1278           return true;
1279         }
1280       }
1281       return false;
1282     };
1283 
1284     size_t last_update_pos = 0;
1285     for (size_t i = 1; i < updates.size(); i++) {
1286       if (updates[last_update_pos]->get_id() == td_api::updateNotificationGroup::ID &&
1287           updates[i]->get_id() == td_api::updateNotificationGroup::ID) {
1288         auto last_update_ptr = static_cast<td_api::updateNotificationGroup *>(updates[last_update_pos].get());
1289         auto update_ptr = static_cast<td_api::updateNotificationGroup *>(updates[i].get());
1290         if ((last_update_ptr->notification_settings_chat_id_ == update_ptr->notification_settings_chat_id_ ||
1291              last_update_ptr->added_notifications_.empty()) &&
1292             !has_common_notifications(last_update_ptr->added_notifications_, update_ptr->removed_notification_ids_) &&
1293             !has_common_notifications(update_ptr->added_notifications_, last_update_ptr->removed_notification_ids_)) {
1294           // combine updates
1295           VLOG(notifications) << "Combine " << as_notification_update(last_update_ptr) << " and "
1296                               << as_notification_update(update_ptr);
1297           CHECK(last_update_ptr->notification_group_id_ == update_ptr->notification_group_id_);
1298           CHECK(last_update_ptr->chat_id_ == update_ptr->chat_id_);
1299           if (last_update_ptr->is_silent_ && !update_ptr->is_silent_) {
1300             last_update_ptr->is_silent_ = false;
1301           }
1302           last_update_ptr->notification_settings_chat_id_ = update_ptr->notification_settings_chat_id_;
1303           last_update_ptr->type_ = std::move(update_ptr->type_);
1304           last_update_ptr->total_count_ = update_ptr->total_count_;
1305           append(last_update_ptr->added_notifications_, std::move(update_ptr->added_notifications_));
1306           append(last_update_ptr->removed_notification_ids_, std::move(update_ptr->removed_notification_ids_));
1307           updates[i] = nullptr;
1308           is_changed = true;
1309           continue;
1310         }
1311       }
1312       last_update_pos++;
1313       if (last_update_pos != i) {
1314         updates[last_update_pos] = std::move(updates[i]);
1315       }
1316     }
1317     updates.resize(last_update_pos + 1);
1318   }
1319 
1320   for (auto &update : updates) {
1321     CHECK(update != nullptr);
1322     if (update->get_id() == td_api::updateNotificationGroup::ID) {
1323       auto update_ptr = static_cast<td_api::updateNotificationGroup *>(update.get());
1324       std::sort(update_ptr->added_notifications_.begin(), update_ptr->added_notifications_.end(),
1325                 [](const auto &lhs, const auto &rhs) { return lhs->id_ < rhs->id_; });
1326       std::sort(update_ptr->removed_notification_ids_.begin(), update_ptr->removed_notification_ids_.end());
1327     }
1328     VLOG(notifications) << "Send " << as_notification_update(update.get());
1329     send_closure(G()->td(), &Td::send_update, std::move(update));
1330   }
1331   on_delayed_notification_update_count_changed(-1, group_id, "flush_pending_updates");
1332 
1333   auto group_it = get_group_force(NotificationGroupId(group_id));
1334   CHECK(group_it != groups_.end());
1335   NotificationGroup &group = group_it->second;
1336   for (auto &notification : group.notifications) {
1337     on_notification_processed(notification.notification_id);
1338   }
1339 }
1340 
force_flush_pending_updates(NotificationGroupId group_id,const char * source)1341 void NotificationManager::force_flush_pending_updates(NotificationGroupId group_id, const char *source) {
1342   flush_pending_updates_timeout_.cancel_timeout(group_id.get());
1343   flush_pending_updates(group_id.get(), source);
1344 }
1345 
flush_all_pending_updates(bool include_delayed_chats,const char * source)1346 void NotificationManager::flush_all_pending_updates(bool include_delayed_chats, const char *source) {
1347   VLOG(notifications) << "Flush all pending notification updates "
1348                       << (include_delayed_chats ? "with delayed chats " : "") << "from " << source;
1349   if (!include_delayed_chats && running_get_difference_) {
1350     return;
1351   }
1352 
1353   vector<NotificationGroupKey> ready_group_keys;
1354   for (const auto &it : pending_updates_) {
1355     if (include_delayed_chats || running_get_chat_difference_.count(it.first) == 0) {
1356       auto group_it = get_group(NotificationGroupId(it.first));
1357       CHECK(group_it != groups_.end());
1358       ready_group_keys.push_back(group_it->first);
1359     }
1360   }
1361 
1362   // flush groups in reverse order to not exceed max_notification_group_count_
1363   VLOG(notifications) << "Flush pending updates in " << ready_group_keys.size() << " notification groups";
1364   std::sort(ready_group_keys.begin(), ready_group_keys.end());
1365   for (const auto &group_key : reversed(ready_group_keys)) {
1366     force_flush_pending_updates(group_key.group_id, "flush_all_pending_updates");
1367   }
1368   if (include_delayed_chats) {
1369     CHECK(pending_updates_.empty());
1370   }
1371 }
1372 
do_flush_pending_notifications(NotificationGroupKey & group_key,NotificationGroup & group,vector<PendingNotification> & pending_notifications)1373 bool NotificationManager::do_flush_pending_notifications(NotificationGroupKey &group_key, NotificationGroup &group,
1374                                                          vector<PendingNotification> &pending_notifications) {
1375   // no check for G()->close_flag() to flush pending notifications even while closing
1376   if (pending_notifications.empty()) {
1377     return false;
1378   }
1379 
1380   VLOG(notifications) << "Do flush " << pending_notifications.size() << " pending notifications in " << group_key
1381                       << " with known " << group.notifications.size() << " from total of " << group.total_count
1382                       << " notifications";
1383 
1384   size_t old_notification_count = group.notifications.size();
1385   size_t shown_notification_count = min(old_notification_count, max_notification_group_size_);
1386 
1387   bool force_update = false;
1388   vector<td_api::object_ptr<td_api::notification>> added_notifications;
1389   added_notifications.reserve(pending_notifications.size());
1390   for (auto &pending_notification : pending_notifications) {
1391     Notification notification(pending_notification.notification_id, pending_notification.date,
1392                               pending_notification.initial_is_silent, std::move(pending_notification.type));
1393     added_notifications.push_back(get_notification_object(group_key.dialog_id, notification));
1394     CHECK(added_notifications.back()->type_ != nullptr);
1395 
1396     if (!notification.type->can_be_delayed()) {
1397       force_update = true;
1398     }
1399     group.notifications.push_back(std::move(notification));
1400   }
1401   group.total_count += narrow_cast<int32>(added_notifications.size());
1402   if (added_notifications.size() > max_notification_group_size_) {
1403     added_notifications.erase(added_notifications.begin(), added_notifications.end() - max_notification_group_size_);
1404   }
1405 
1406   vector<int32> removed_notification_ids;
1407   if (shown_notification_count + added_notifications.size() > max_notification_group_size_) {
1408     auto removed_notification_count =
1409         shown_notification_count + added_notifications.size() - max_notification_group_size_;
1410     removed_notification_ids.reserve(removed_notification_count);
1411     for (size_t i = 0; i < removed_notification_count; i++) {
1412       removed_notification_ids.push_back(
1413           group.notifications[old_notification_count - shown_notification_count + i].notification_id.get());
1414     }
1415   }
1416 
1417   if (!added_notifications.empty()) {
1418     add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
1419         group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(),
1420         pending_notifications[0].settings_dialog_id.get(), pending_notifications[0].is_silent, group.total_count,
1421         std::move(added_notifications), std::move(removed_notification_ids)));
1422   } else {
1423     CHECK(removed_notification_ids.empty());
1424   }
1425   pending_notifications.clear();
1426   return force_update;
1427 }
1428 
get_remove_group_update(const NotificationGroupKey & group_key,const NotificationGroup & group,vector<int32> && removed_notification_ids) const1429 td_api::object_ptr<td_api::updateNotificationGroup> NotificationManager::get_remove_group_update(
1430     const NotificationGroupKey &group_key, const NotificationGroup &group,
1431     vector<int32> &&removed_notification_ids) const {
1432   auto total_size = group.notifications.size();
1433   CHECK(removed_notification_ids.size() <= max_notification_group_size_);
1434   auto removed_size = min(total_size, max_notification_group_size_ - removed_notification_ids.size());
1435   removed_notification_ids.reserve(removed_size + removed_notification_ids.size());
1436   for (size_t i = total_size - removed_size; i < total_size; i++) {
1437     removed_notification_ids.push_back(group.notifications[i].notification_id.get());
1438   }
1439 
1440   if (removed_notification_ids.empty()) {
1441     return nullptr;
1442   }
1443   return td_api::make_object<td_api::updateNotificationGroup>(
1444       group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(),
1445       group_key.dialog_id.get(), true, group.total_count, vector<td_api::object_ptr<td_api::notification>>(),
1446       std::move(removed_notification_ids));
1447 }
1448 
send_remove_group_update(const NotificationGroupKey & group_key,const NotificationGroup & group,vector<int32> && removed_notification_ids)1449 void NotificationManager::send_remove_group_update(const NotificationGroupKey &group_key,
1450                                                    const NotificationGroup &group,
1451                                                    vector<int32> &&removed_notification_ids) {
1452   VLOG(notifications) << "Remove " << group_key.group_id;
1453   auto update = get_remove_group_update(group_key, group, std::move(removed_notification_ids));
1454   if (update != nullptr) {
1455     add_update_notification_group(std::move(update));
1456   }
1457 }
1458 
send_add_group_update(const NotificationGroupKey & group_key,const NotificationGroup & group)1459 void NotificationManager::send_add_group_update(const NotificationGroupKey &group_key, const NotificationGroup &group) {
1460   VLOG(notifications) << "Add " << group_key.group_id;
1461   auto total_size = group.notifications.size();
1462   auto added_size = min(total_size, max_notification_group_size_);
1463   vector<td_api::object_ptr<td_api::notification>> added_notifications;
1464   added_notifications.reserve(added_size);
1465   for (size_t i = total_size - added_size; i < total_size; i++) {
1466     added_notifications.push_back(get_notification_object(group_key.dialog_id, group.notifications[i]));
1467     if (added_notifications.back()->type_ == nullptr) {
1468       added_notifications.pop_back();
1469     }
1470   }
1471 
1472   if (!added_notifications.empty()) {
1473     add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
1474         group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(), 0, true,
1475         group.total_count, std::move(added_notifications), vector<int32>()));
1476   }
1477 }
1478 
flush_pending_notifications(NotificationGroupId group_id)1479 void NotificationManager::flush_pending_notifications(NotificationGroupId group_id) {
1480   auto group_it = get_group(group_id);
1481   if (group_it == groups_.end()) {
1482     return;
1483   }
1484 
1485   td::remove_if(group_it->second.pending_notifications,
1486                 [dialog_id = group_it->first.dialog_id](const PendingNotification &pending_notification) {
1487                   return pending_notification.type->get_notification_type_object(dialog_id) == nullptr;
1488                 });
1489 
1490   if (group_it->second.pending_notifications.empty()) {
1491     return;
1492   }
1493 
1494   auto group_key = group_it->first;
1495   auto group = std::move(group_it->second);
1496 
1497   delete_group(std::move(group_it));
1498 
1499   auto final_group_key = group_key;
1500   for (auto &pending_notification : group.pending_notifications) {
1501     if (pending_notification.date >= final_group_key.last_notification_date) {
1502       final_group_key.last_notification_date = pending_notification.date;
1503     }
1504   }
1505   CHECK(final_group_key.last_notification_date != 0);
1506 
1507   VLOG(notifications) << "Flush pending notifications in " << group_key << " up to "
1508                       << final_group_key.last_notification_date;
1509 
1510   auto last_group_key = get_last_updated_group_key();
1511   bool was_updated = group_key.last_notification_date != 0 && group_key < last_group_key;
1512   bool is_updated = final_group_key < last_group_key;
1513   bool force_update = false;
1514 
1515   NotificationGroupId removed_group_id;
1516   if (!is_updated) {
1517     CHECK(!was_updated);
1518     VLOG(notifications) << "There is no need to send updateNotificationGroup in " << group_key
1519                         << ", because of newer notification groups";
1520     group.total_count += narrow_cast<int32>(group.pending_notifications.size());
1521     for (auto &pending_notification : group.pending_notifications) {
1522       group.notifications.emplace_back(pending_notification.notification_id, pending_notification.date,
1523                                        pending_notification.initial_is_silent, std::move(pending_notification.type));
1524     }
1525   } else {
1526     if (!was_updated) {
1527       if (last_group_key.last_notification_date != 0) {
1528         // need to remove last notification group to not exceed max_notification_group_count_
1529         removed_group_id = last_group_key.group_id;
1530         send_remove_group_update(last_group_key, groups_[last_group_key], vector<int32>());
1531       }
1532       send_add_group_update(group_key, group);
1533     }
1534 
1535     DialogId notification_settings_dialog_id;
1536     bool is_silent = false;
1537 
1538     // split notifications by groups with common settings
1539     vector<PendingNotification> grouped_notifications;
1540     for (auto &pending_notification : group.pending_notifications) {
1541       if (notification_settings_dialog_id != pending_notification.settings_dialog_id ||
1542           is_silent != pending_notification.is_silent) {
1543         if (do_flush_pending_notifications(group_key, group, grouped_notifications)) {
1544           force_update = true;
1545         }
1546         notification_settings_dialog_id = pending_notification.settings_dialog_id;
1547         is_silent = pending_notification.is_silent;
1548       }
1549       grouped_notifications.push_back(std::move(pending_notification));
1550     }
1551     if (do_flush_pending_notifications(group_key, group, grouped_notifications)) {
1552       force_update = true;
1553     }
1554   }
1555 
1556   group.pending_notifications_flush_time = 0;
1557   group.pending_notifications.clear();
1558   on_delayed_notification_update_count_changed(-1, group_id.get(), "flush_pending_notifications");
1559   // if we can delete a lot of notifications simultaneously
1560   if (group.notifications.size() > keep_notification_group_size_ + EXTRA_GROUP_SIZE &&
1561       group.type != NotificationGroupType::Calls) {
1562     // keep only keep_notification_group_size_ last notifications in memory
1563     for (auto it = group.notifications.begin(); it != group.notifications.end() - keep_notification_group_size_; ++it) {
1564       on_notification_removed(it->notification_id);
1565     }
1566     group.notifications.erase(group.notifications.begin(), group.notifications.end() - keep_notification_group_size_);
1567     group.is_loaded_from_database = false;
1568   }
1569 
1570   add_group(std::move(final_group_key), std::move(group), "flush_pending_notifications");
1571 
1572   if (force_update) {
1573     if (removed_group_id.is_valid()) {
1574       force_flush_pending_updates(removed_group_id, "flush_pending_notifications 1");
1575     }
1576     force_flush_pending_updates(group_key.group_id, "flush_pending_notifications 2");
1577   }
1578 }
1579 
flush_all_pending_notifications()1580 void NotificationManager::flush_all_pending_notifications() {
1581   std::multimap<int32, NotificationGroupId> group_ids;
1582   for (auto &group_it : groups_) {
1583     if (!group_it.second.pending_notifications.empty()) {
1584       group_ids.emplace(group_it.second.pending_notifications.back().date, group_it.first.group_id);
1585     }
1586   }
1587 
1588   // flush groups in order of last notification date
1589   VLOG(notifications) << "Flush pending notifications in " << group_ids.size() << " notification groups";
1590   for (auto &it : group_ids) {
1591     flush_pending_notifications_timeout_.cancel_timeout(it.second.get());
1592     flush_pending_notifications(it.second);
1593   }
1594 }
1595 
edit_notification(NotificationGroupId group_id,NotificationId notification_id,unique_ptr<NotificationType> type)1596 void NotificationManager::edit_notification(NotificationGroupId group_id, NotificationId notification_id,
1597                                             unique_ptr<NotificationType> type) {
1598   if (is_disabled() || max_notification_group_count_ == 0) {
1599     return;
1600   }
1601   if (!group_id.is_valid()) {
1602     return;
1603   }
1604 
1605   CHECK(notification_id.is_valid());
1606   CHECK(type != nullptr);
1607   VLOG(notifications) << "Edit " << notification_id << ": " << *type;
1608 
1609   auto group_it = get_group(group_id);
1610   if (group_it == groups_.end()) {
1611     return;
1612   }
1613   auto &group = group_it->second;
1614   for (size_t i = 0; i < group.notifications.size(); i++) {
1615     auto &notification = group.notifications[i];
1616     if (notification.notification_id == notification_id) {
1617       if (notification.type->get_message_id() != type->get_message_id() ||
1618           notification.type->is_temporary() != type->is_temporary()) {
1619         LOG(ERROR) << "Ignore edit of " << notification_id << " with " << *type << ", because previous type is "
1620                    << *notification.type;
1621         return;
1622       }
1623 
1624       notification.type = std::move(type);
1625       if (i + max_notification_group_size_ >= group.notifications.size() &&
1626           !(get_last_updated_group_key() < group_it->first)) {
1627         CHECK(group_it->first.last_notification_date != 0);
1628         add_update_notification(group_it->first.group_id, group_it->first.dialog_id, notification);
1629       }
1630       return;
1631     }
1632   }
1633   for (auto &notification : group.pending_notifications) {
1634     if (notification.notification_id == notification_id) {
1635       if (notification.type->get_message_id() != type->get_message_id() ||
1636           notification.type->is_temporary() != type->is_temporary()) {
1637         LOG(ERROR) << "Ignore edit of " << notification_id << " with " << *type << ", because previous type is "
1638                    << *notification.type;
1639         return;
1640       }
1641 
1642       notification.type = std::move(type);
1643       return;
1644     }
1645   }
1646 }
1647 
on_notification_processed(NotificationId notification_id)1648 void NotificationManager::on_notification_processed(NotificationId notification_id) {
1649   auto promise_it = push_notification_promises_.find(notification_id);
1650   if (promise_it != push_notification_promises_.end()) {
1651     auto promises = std::move(promise_it->second);
1652     push_notification_promises_.erase(promise_it);
1653 
1654     for (auto &promise : promises) {
1655       promise.set_value(Unit());
1656     }
1657   }
1658 }
1659 
on_notification_removed(NotificationId notification_id)1660 void NotificationManager::on_notification_removed(NotificationId notification_id) {
1661   VLOG(notifications) << "In on_notification_removed with " << notification_id;
1662 
1663   auto add_it = temporary_notification_log_event_ids_.find(notification_id);
1664   if (add_it == temporary_notification_log_event_ids_.end()) {
1665     return;
1666   }
1667 
1668   auto edit_it = temporary_edit_notification_log_event_ids_.find(notification_id);
1669   if (edit_it != temporary_edit_notification_log_event_ids_.end()) {
1670     VLOG(notifications) << "Remove from binlog edit of " << notification_id << " with log event " << edit_it->second;
1671     if (!is_being_destroyed_) {
1672       binlog_erase(G()->td_db()->get_binlog(), edit_it->second);
1673     }
1674     temporary_edit_notification_log_event_ids_.erase(edit_it);
1675   }
1676 
1677   VLOG(notifications) << "Remove from binlog " << notification_id << " with log event " << add_it->second;
1678   if (!is_being_destroyed_) {
1679     binlog_erase(G()->td_db()->get_binlog(), add_it->second);
1680   }
1681   temporary_notification_log_event_ids_.erase(add_it);
1682 
1683   auto erased_notification_count = temporary_notifications_.erase(temporary_notification_message_ids_[notification_id]);
1684   auto erased_message_id_count = temporary_notification_message_ids_.erase(notification_id);
1685   CHECK(erased_notification_count > 0);
1686   CHECK(erased_message_id_count > 0);
1687 
1688   on_notification_processed(notification_id);
1689 }
1690 
on_notifications_removed(NotificationGroups::iterator && group_it,vector<td_api::object_ptr<td_api::notification>> && added_notifications,vector<int32> && removed_notification_ids,bool force_update)1691 void NotificationManager::on_notifications_removed(
1692     NotificationGroups::iterator &&group_it, vector<td_api::object_ptr<td_api::notification>> &&added_notifications,
1693     vector<int32> &&removed_notification_ids, bool force_update) {
1694   VLOG(notifications) << "In on_notifications_removed for " << group_it->first.group_id << " with "
1695                       << added_notifications.size() << " added notifications and " << removed_notification_ids.size()
1696                       << " removed notifications, new total_count = " << group_it->second.total_count;
1697   auto group_key = group_it->first;
1698   auto final_group_key = group_key;
1699   final_group_key.last_notification_date = 0;
1700   for (auto &notification : group_it->second.notifications) {
1701     if (notification.date > final_group_key.last_notification_date) {
1702       final_group_key.last_notification_date = notification.date;
1703     }
1704   }
1705 
1706   bool is_position_changed = final_group_key.last_notification_date != group_key.last_notification_date;
1707 
1708   NotificationGroup group = std::move(group_it->second);
1709   if (is_position_changed) {
1710     VLOG(notifications) << "Position of notification group is changed from " << group_key << " to " << final_group_key;
1711     delete_group(std::move(group_it));
1712   }
1713 
1714   auto last_group_key = get_last_updated_group_key();
1715   bool was_updated = false;
1716   bool is_updated = false;
1717   if (is_position_changed) {
1718     was_updated = group_key.last_notification_date != 0 && group_key < last_group_key;
1719     is_updated = final_group_key.last_notification_date != 0 && final_group_key < last_group_key;
1720   } else {
1721     was_updated = is_updated = group_key.last_notification_date != 0 && !(last_group_key < group_key);
1722   }
1723 
1724   if (!was_updated) {
1725     CHECK(!is_updated);
1726     if (final_group_key.last_notification_date == 0 && group.total_count == 0) {
1727       // send update about empty invisible group anyway
1728       add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
1729           group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(), 0, true,
1730           0, vector<td_api::object_ptr<td_api::notification>>(), vector<int32>()));
1731     } else {
1732       VLOG(notifications) << "There is no need to send updateNotificationGroup about " << group_key.group_id;
1733     }
1734   } else {
1735     if (is_updated) {
1736       // group is still visible
1737       add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
1738           group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(), 0, true,
1739           group.total_count, std::move(added_notifications), std::move(removed_notification_ids)));
1740     } else {
1741       // group needs to be removed
1742       send_remove_group_update(group_key, group, std::move(removed_notification_ids));
1743       if (last_group_key.last_notification_date != 0) {
1744         // need to add new last notification group
1745         send_add_group_update(last_group_key, groups_[last_group_key]);
1746       }
1747     }
1748   }
1749 
1750   if (is_position_changed) {
1751     add_group(std::move(final_group_key), std::move(group), "on_notifications_removed");
1752 
1753     last_group_key = get_last_updated_group_key();
1754   } else {
1755     CHECK(group_it->first.last_notification_date == 0 || !group.notifications.empty());
1756     group_it->second = std::move(group);
1757   }
1758 
1759   if (force_update) {
1760     force_flush_pending_updates(group_key.group_id, "on_notifications_removed");
1761   }
1762 
1763   if (last_loaded_notification_group_key_ < last_group_key) {
1764     load_message_notification_groups_from_database(td::max(static_cast<int32>(max_notification_group_count_), 10) / 2,
1765                                                    true);
1766   }
1767 }
1768 
remove_added_notifications_from_pending_updates(NotificationGroupId group_id,const std::function<bool (const td_api::object_ptr<td_api::notification> & notification)> & is_removed)1769 void NotificationManager::remove_added_notifications_from_pending_updates(
1770     NotificationGroupId group_id,
1771     const std::function<bool(const td_api::object_ptr<td_api::notification> &notification)> &is_removed) {
1772   auto it = pending_updates_.find(group_id.get());
1773   if (it == pending_updates_.end()) {
1774     return;
1775   }
1776 
1777   std::unordered_set<int32> removed_notification_ids;
1778   for (auto &update : it->second) {
1779     if (update == nullptr) {
1780       continue;
1781     }
1782     if (update->get_id() == td_api::updateNotificationGroup::ID) {
1783       auto update_ptr = static_cast<td_api::updateNotificationGroup *>(update.get());
1784       if (!removed_notification_ids.empty() && !update_ptr->removed_notification_ids_.empty()) {
1785         td::remove_if(update_ptr->removed_notification_ids_, [&removed_notification_ids](auto &notification_id) {
1786           return removed_notification_ids.count(notification_id) == 1;
1787         });
1788       }
1789       for (auto &notification : update_ptr->added_notifications_) {
1790         if (is_removed(notification)) {
1791           removed_notification_ids.insert(notification->id_);
1792           VLOG(notifications) << "Remove " << NotificationId(notification->id_) << " in " << group_id;
1793           notification = nullptr;
1794         }
1795       }
1796       td::remove_if(update_ptr->added_notifications_, [](auto &notification) { return notification == nullptr; });
1797     } else {
1798       CHECK(update->get_id() == td_api::updateNotification::ID);
1799       auto update_ptr = static_cast<td_api::updateNotification *>(update.get());
1800       if (is_removed(update_ptr->notification_)) {
1801         removed_notification_ids.insert(update_ptr->notification_->id_);
1802         VLOG(notifications) << "Remove " << NotificationId(update_ptr->notification_->id_) << " in " << group_id;
1803         update = nullptr;
1804       }
1805     }
1806   }
1807 }
1808 
remove_notification(NotificationGroupId group_id,NotificationId notification_id,bool is_permanent,bool force_update,Promise<Unit> && promise,const char * source)1809 void NotificationManager::remove_notification(NotificationGroupId group_id, NotificationId notification_id,
1810                                               bool is_permanent, bool force_update, Promise<Unit> &&promise,
1811                                               const char *source) {
1812   if (!group_id.is_valid()) {
1813     return promise.set_error(Status::Error(400, "Notification group identifier is invalid"));
1814   }
1815   if (!notification_id.is_valid()) {
1816     return promise.set_error(Status::Error(400, "Notification identifier is invalid"));
1817   }
1818 
1819   if (is_disabled() || max_notification_group_count_ == 0) {
1820     return promise.set_value(Unit());
1821   }
1822 
1823   VLOG(notifications) << "Remove " << notification_id << " from " << group_id << " with is_permanent = " << is_permanent
1824                       << ", force_update = " << force_update << " from " << source;
1825 
1826   auto group_it = get_group_force(group_id);
1827   if (group_it == groups_.end()) {
1828     return promise.set_value(Unit());
1829   }
1830 
1831   if (!is_permanent && group_it->second.type != NotificationGroupType::Calls) {
1832     td_->messages_manager_->remove_message_notification(group_it->first.dialog_id, group_id, notification_id);
1833   }
1834 
1835   for (auto it = group_it->second.pending_notifications.begin(); it != group_it->second.pending_notifications.end();
1836        ++it) {
1837     if (it->notification_id == notification_id) {
1838       // notification is still pending, just delete it
1839       on_notification_removed(notification_id);
1840       group_it->second.pending_notifications.erase(it);
1841       if (group_it->second.pending_notifications.empty()) {
1842         group_it->second.pending_notifications_flush_time = 0;
1843         flush_pending_notifications_timeout_.cancel_timeout(group_id.get());
1844         on_delayed_notification_update_count_changed(-1, group_id.get(), "remove_notification");
1845       }
1846       return promise.set_value(Unit());
1847     }
1848   }
1849 
1850   bool is_found = false;
1851   auto old_group_size = group_it->second.notifications.size();
1852   size_t notification_pos = old_group_size;
1853   for (size_t pos = 0; pos < notification_pos; pos++) {
1854     if (group_it->second.notifications[pos].notification_id == notification_id) {
1855       on_notification_removed(notification_id);
1856       notification_pos = pos;
1857       is_found = true;
1858     }
1859   }
1860 
1861   bool have_all_notifications = group_it->second.type == NotificationGroupType::Calls ||
1862                                 group_it->second.type == NotificationGroupType::SecretChat;
1863   bool is_total_count_changed = false;
1864   if ((!have_all_notifications && is_permanent) || (have_all_notifications && is_found)) {
1865     if (group_it->second.total_count == 0) {
1866       LOG(ERROR) << "Total notification count became negative in " << group_it->second << " after removing "
1867                  << notification_id << " with is_permanent = " << is_permanent << ", is_found = " << is_found
1868                  << ", force_update = " << force_update << " from " << source;
1869     } else {
1870       group_it->second.total_count--;
1871       is_total_count_changed = true;
1872     }
1873   }
1874   if (is_found) {
1875     group_it->second.notifications.erase(group_it->second.notifications.begin() + notification_pos);
1876   }
1877 
1878   vector<td_api::object_ptr<td_api::notification>> added_notifications;
1879   vector<int32> removed_notification_ids;
1880   CHECK(max_notification_group_size_ > 0);
1881   if (is_found && notification_pos + max_notification_group_size_ >= old_group_size) {
1882     removed_notification_ids.push_back(notification_id.get());
1883     if (old_group_size >= max_notification_group_size_ + 1) {
1884       added_notifications.push_back(
1885           get_notification_object(group_it->first.dialog_id,
1886                                   group_it->second.notifications[old_group_size - max_notification_group_size_ - 1]));
1887       if (added_notifications.back()->type_ == nullptr) {
1888         added_notifications.pop_back();
1889       }
1890     }
1891     if (added_notifications.empty() && max_notification_group_size_ > group_it->second.notifications.size()) {
1892       load_message_notifications_from_database(group_it->first, group_it->second, keep_notification_group_size_);
1893     }
1894   }
1895 
1896   if (is_total_count_changed || !removed_notification_ids.empty()) {
1897     on_notifications_removed(std::move(group_it), std::move(added_notifications), std::move(removed_notification_ids),
1898                              force_update);
1899   }
1900 
1901   remove_added_notifications_from_pending_updates(
1902       group_id, [notification_id](const td_api::object_ptr<td_api::notification> &notification) {
1903         return notification->id_ == notification_id.get();
1904       });
1905 
1906   promise.set_value(Unit());
1907 }
1908 
remove_temporary_notification_by_message_id(NotificationGroupId group_id,MessageId message_id,bool force_update,const char * source)1909 void NotificationManager::remove_temporary_notification_by_message_id(NotificationGroupId group_id,
1910                                                                       MessageId message_id, bool force_update,
1911                                                                       const char *source) {
1912   if (!group_id.is_valid()) {
1913     return;
1914   }
1915 
1916   VLOG(notifications) << "Remove notification for " << message_id << " in " << group_id << " from " << source;
1917   CHECK(message_id.is_valid());
1918 
1919   auto group_it = get_group(group_id);
1920   if (group_it == groups_.end()) {
1921     return;
1922   }
1923 
1924   auto remove_notification_by_message_id = [&](auto &notifications) {
1925     for (auto &notification : notifications) {
1926       if (notification.type->get_message_id() == message_id) {
1927         for (auto file_id : notification.type->get_file_ids(td_)) {
1928           this->td_->file_manager_->delete_file(file_id, Promise<>(), "remove_temporary_notification_by_message_id");
1929         }
1930         return this->remove_notification(group_id, notification.notification_id, true, force_update, Auto(),
1931                                          "remove_temporary_notification_by_message_id");
1932       }
1933     }
1934   };
1935 
1936   remove_notification_by_message_id(group_it->second.pending_notifications);
1937   remove_notification_by_message_id(group_it->second.notifications);
1938 }
1939 
remove_notification_group(NotificationGroupId group_id,NotificationId max_notification_id,MessageId max_message_id,int32 new_total_count,bool force_update,Promise<Unit> && promise)1940 void NotificationManager::remove_notification_group(NotificationGroupId group_id, NotificationId max_notification_id,
1941                                                     MessageId max_message_id, int32 new_total_count, bool force_update,
1942                                                     Promise<Unit> &&promise) {
1943   if (!group_id.is_valid()) {
1944     return promise.set_error(Status::Error(400, "Group identifier is invalid"));
1945   }
1946   if (!max_notification_id.is_valid() && !max_message_id.is_valid()) {
1947     return promise.set_error(Status::Error(400, "Notification identifier is invalid"));
1948   }
1949 
1950   if (is_disabled() || max_notification_group_count_ == 0) {
1951     return promise.set_value(Unit());
1952   }
1953 
1954   if (new_total_count == 0) {
1955     remove_temporary_notifications(group_id, "remove_notification_group");
1956   }
1957 
1958   VLOG(notifications) << "Remove " << group_id << " up to " << max_notification_id << " or " << max_message_id
1959                       << " with new_total_count = " << new_total_count << " and force_update = " << force_update;
1960 
1961   auto group_it = get_group_force(group_id);
1962   if (group_it == groups_.end()) {
1963     VLOG(notifications) << "Can't find " << group_id;
1964     return promise.set_value(Unit());
1965   }
1966 
1967   if (max_notification_id.is_valid()) {
1968     if (max_notification_id.get() > current_notification_id_.get()) {
1969       max_notification_id = current_notification_id_;
1970     }
1971     if (group_it->second.type != NotificationGroupType::Calls) {
1972       td_->messages_manager_->remove_message_notifications(
1973           group_it->first.dialog_id, group_id, max_notification_id,
1974           get_last_message_id_by_notification_id(group_it->second, max_notification_id));
1975     }
1976   }
1977 
1978   auto pending_delete_end = group_it->second.pending_notifications.begin();
1979   for (auto it = group_it->second.pending_notifications.begin(); it != group_it->second.pending_notifications.end();
1980        ++it) {
1981     if (it->notification_id.get() <= max_notification_id.get() ||
1982         (max_message_id.is_valid() && it->type->get_message_id() <= max_message_id)) {
1983       pending_delete_end = it + 1;
1984       on_notification_removed(it->notification_id);
1985     }
1986   }
1987   if (pending_delete_end != group_it->second.pending_notifications.begin()) {
1988     group_it->second.pending_notifications.erase(group_it->second.pending_notifications.begin(), pending_delete_end);
1989     if (group_it->second.pending_notifications.empty()) {
1990       group_it->second.pending_notifications_flush_time = 0;
1991       flush_pending_notifications_timeout_.cancel_timeout(group_id.get());
1992       on_delayed_notification_update_count_changed(-1, group_id.get(), "remove_notification_group");
1993     }
1994   }
1995   if (new_total_count != -1) {
1996     new_total_count += get_temporary_notification_total_count(group_it->second);
1997     new_total_count -= static_cast<int32>(group_it->second.pending_notifications.size());
1998     if (new_total_count < 0) {
1999       LOG(ERROR) << "Have wrong new_total_count " << new_total_count << " + "
2000                  << group_it->second.pending_notifications.size();
2001     }
2002   }
2003 
2004   auto old_group_size = group_it->second.notifications.size();
2005   auto notification_delete_end = old_group_size;
2006   for (size_t pos = 0; pos < notification_delete_end; pos++) {
2007     auto &notification = group_it->second.notifications[pos];
2008     if (notification.notification_id.get() > max_notification_id.get() &&
2009         (!max_message_id.is_valid() || notification.type->get_message_id() > max_message_id)) {
2010       notification_delete_end = pos;
2011     } else {
2012       on_notification_removed(notification.notification_id);
2013     }
2014   }
2015 
2016   bool is_found = notification_delete_end != 0;
2017 
2018   vector<int32> removed_notification_ids;
2019   if (is_found && notification_delete_end + max_notification_group_size_ > old_group_size) {
2020     for (size_t i = old_group_size >= max_notification_group_size_ ? old_group_size - max_notification_group_size_ : 0;
2021          i < notification_delete_end; i++) {
2022       removed_notification_ids.push_back(group_it->second.notifications[i].notification_id.get());
2023     }
2024   }
2025 
2026   VLOG(notifications) << "Need to delete " << notification_delete_end << " from "
2027                       << group_it->second.notifications.size() << " notifications";
2028   if (is_found) {
2029     group_it->second.notifications.erase(group_it->second.notifications.begin(),
2030                                          group_it->second.notifications.begin() + notification_delete_end);
2031   }
2032   if (group_it->second.type == NotificationGroupType::Calls ||
2033       group_it->second.type == NotificationGroupType::SecretChat) {
2034     new_total_count = static_cast<int32>(group_it->second.notifications.size());
2035   }
2036   if (group_it->second.total_count == new_total_count) {
2037     new_total_count = -1;
2038   }
2039   if (new_total_count != -1) {
2040     group_it->second.total_count = new_total_count;
2041   }
2042 
2043   if (new_total_count != -1 || !removed_notification_ids.empty()) {
2044     on_notifications_removed(std::move(group_it), vector<td_api::object_ptr<td_api::notification>>(),
2045                              std::move(removed_notification_ids), force_update);
2046   } else {
2047     VLOG(notifications) << "Have new_total_count = " << new_total_count << ", " << removed_notification_ids.size()
2048                         << " removed notifications and force_update = " << force_update;
2049     if (force_update) {
2050       force_flush_pending_updates(group_id, "remove_notification_group");
2051     }
2052   }
2053 
2054   if (max_notification_id.is_valid()) {
2055     remove_added_notifications_from_pending_updates(
2056         group_id, [max_notification_id](const td_api::object_ptr<td_api::notification> &notification) {
2057           return notification->id_ <= max_notification_id.get();
2058         });
2059   } else {
2060     remove_added_notifications_from_pending_updates(
2061         group_id, [max_message_id](const td_api::object_ptr<td_api::notification> &notification) {
2062           return notification->type_->get_id() == td_api::notificationTypeNewMessage::ID &&
2063                  static_cast<const td_api::notificationTypeNewMessage *>(notification->type_.get())->message_->id_ <=
2064                      max_message_id.get();
2065         });
2066   }
2067 
2068   promise.set_value(Unit());
2069 }
2070 
remove_temporary_notifications(NotificationGroupId group_id,const char * source)2071 void NotificationManager::remove_temporary_notifications(NotificationGroupId group_id, const char *source) {
2072   CHECK(group_id.is_valid());
2073 
2074   if (is_disabled() || max_notification_group_count_ == 0) {
2075     return;
2076   }
2077 
2078   auto group_it = get_group(group_id);
2079   if (group_it == groups_.end()) {
2080     return;
2081   }
2082 
2083   if (get_temporary_notification_total_count(group_it->second) == 0) {
2084     return;
2085   }
2086 
2087   VLOG(notifications) << "Remove temporary notifications in " << group_id << " from " << source;
2088 
2089   auto &group = group_it->second;
2090   while (!group.pending_notifications.empty() && group.pending_notifications.back().type->is_temporary()) {
2091     VLOG(notifications) << "Remove temporary " << group.pending_notifications.back() << " from " << group_id;
2092     // notification is still pending, just delete it
2093     on_notification_removed(group.pending_notifications.back().notification_id);
2094     group.pending_notifications.pop_back();
2095     if (group.pending_notifications.empty()) {
2096       group.pending_notifications_flush_time = 0;
2097       flush_pending_notifications_timeout_.cancel_timeout(group_id.get());
2098       on_delayed_notification_update_count_changed(-1, group_id.get(), "remove_temporary_notifications");
2099     }
2100   }
2101 
2102   auto old_group_size = group.notifications.size();
2103   size_t notification_pos = old_group_size;
2104   for (size_t pos = 0; pos < notification_pos; pos++) {
2105     if (group.notifications[pos].type->is_temporary()) {
2106       notification_pos = pos;
2107     }
2108   }
2109   auto removed_notification_count = narrow_cast<int32>(old_group_size - notification_pos);
2110   if (removed_notification_count == 0) {
2111     CHECK(get_temporary_notification_total_count(group_it->second) == 0);
2112     return;
2113   }
2114 
2115   if (group.total_count < removed_notification_count) {
2116     LOG(ERROR) << "Total notification count became negative in " << group_id << " after removing "
2117                << removed_notification_count << " temporary notificaitions";
2118     group.total_count = 0;
2119   } else {
2120     group.total_count -= removed_notification_count;
2121   }
2122 
2123   vector<int32> removed_notification_ids;
2124   for (auto i = notification_pos; i < old_group_size; i++) {
2125     LOG_CHECK(group.notifications[i].type->is_temporary())
2126         << notification_pos << ' ' << i << ' ' << old_group_size << ' ' << removed_notification_count << ' '
2127         << group.notifications[i] << ' ' << group << ' ' << group_it->first;
2128     VLOG(notifications) << "Remove temporary " << group.notifications[i] << " from " << group_id;
2129     auto notification_id = group.notifications[i].notification_id;
2130     on_notification_removed(notification_id);
2131     if (i + max_notification_group_size_ >= old_group_size) {
2132       removed_notification_ids.push_back(notification_id.get());
2133     }
2134   }
2135   group.notifications.erase(group.notifications.begin() + notification_pos, group.notifications.end());
2136   CHECK(!removed_notification_ids.empty());
2137 
2138   vector<td_api::object_ptr<td_api::notification>> added_notifications;
2139   if (old_group_size >= max_notification_group_size_) {
2140     size_t added_notification_count = 0;
2141     for (size_t i = min(old_group_size - max_notification_group_size_, notification_pos);
2142          i-- > 0 && added_notification_count++ < removed_notification_ids.size();) {
2143       added_notifications.push_back(get_notification_object(group_it->first.dialog_id, group.notifications[i]));
2144       if (added_notifications.back()->type_ == nullptr) {
2145         added_notifications.pop_back();
2146       }
2147     }
2148     if (added_notification_count < removed_notification_ids.size() &&
2149         max_notification_group_size_ > group.notifications.size()) {
2150       load_message_notifications_from_database(group_it->first, group, keep_notification_group_size_);
2151     }
2152     std::reverse(added_notifications.begin(), added_notifications.end());
2153   }
2154   CHECK(get_temporary_notification_total_count(group_it->second) == 0);
2155 
2156   on_notifications_removed(std::move(group_it), std::move(added_notifications), std::move(removed_notification_ids),
2157                            false);
2158 
2159   remove_added_notifications_from_pending_updates(
2160       group_id, [](const td_api::object_ptr<td_api::notification> &notification) {
2161         return notification->get_id() == td_api::notificationTypeNewPushMessage::ID;
2162       });
2163 }
2164 
get_temporary_notification_total_count(const NotificationGroup & group)2165 int32 NotificationManager::get_temporary_notification_total_count(const NotificationGroup &group) {
2166   int32 result = 0;
2167   for (auto &notification : reversed(group.notifications)) {
2168     if (!notification.type->is_temporary()) {
2169       break;
2170     }
2171     result++;
2172   }
2173   for (auto &pending_notification : reversed(group.pending_notifications)) {
2174     if (!pending_notification.type->is_temporary()) {
2175       break;
2176     }
2177     result++;
2178   }
2179   return result;
2180 }
2181 
set_notification_total_count(NotificationGroupId group_id,int32 new_total_count)2182 void NotificationManager::set_notification_total_count(NotificationGroupId group_id, int32 new_total_count) {
2183   if (!group_id.is_valid()) {
2184     return;
2185   }
2186   if (is_disabled() || max_notification_group_count_ == 0) {
2187     return;
2188   }
2189 
2190   auto group_it = get_group_force(group_id);
2191   if (group_it == groups_.end()) {
2192     VLOG(notifications) << "Can't find " << group_id;
2193     return;
2194   }
2195 
2196   new_total_count += get_temporary_notification_total_count(group_it->second);
2197   new_total_count -= static_cast<int32>(group_it->second.pending_notifications.size());
2198   if (new_total_count < 0) {
2199     LOG(ERROR) << "Have wrong new_total_count " << new_total_count << " after removing "
2200                << group_it->second.pending_notifications.size() << " pending notifications";
2201     return;
2202   }
2203   if (new_total_count < static_cast<int32>(group_it->second.notifications.size())) {
2204     LOG(ERROR) << "Have wrong new_total_count " << new_total_count << " less than number of known notifications "
2205                << group_it->second.notifications.size();
2206     return;
2207   }
2208 
2209   CHECK(group_it->second.type != NotificationGroupType::Calls);
2210   if (group_it->second.total_count == new_total_count) {
2211     return;
2212   }
2213 
2214   VLOG(notifications) << "Set total_count in " << group_id << " to " << new_total_count;
2215   group_it->second.total_count = new_total_count;
2216 
2217   on_notifications_removed(std::move(group_it), vector<td_api::object_ptr<td_api::notification>>(), vector<int32>(),
2218                            false);
2219 }
2220 
get_notification_group_message_ids(NotificationGroupId group_id)2221 vector<MessageId> NotificationManager::get_notification_group_message_ids(NotificationGroupId group_id) {
2222   CHECK(group_id.is_valid());
2223   if (is_disabled() || max_notification_group_count_ == 0) {
2224     return {};
2225   }
2226 
2227   auto group_it = get_group_force(group_id);
2228   if (group_it == groups_.end()) {
2229     return {};
2230   }
2231 
2232   vector<MessageId> message_ids;
2233   for (auto &notification : group_it->second.notifications) {
2234     auto message_id = notification.type->get_message_id();
2235     if (message_id.is_valid()) {
2236       message_ids.push_back(message_id);
2237     }
2238   }
2239   for (auto &notification : group_it->second.pending_notifications) {
2240     auto message_id = notification.type->get_message_id();
2241     if (message_id.is_valid()) {
2242       message_ids.push_back(message_id);
2243     }
2244   }
2245 
2246   return message_ids;
2247 }
2248 
get_call_notification_group_id(DialogId dialog_id)2249 NotificationGroupId NotificationManager::get_call_notification_group_id(DialogId dialog_id) {
2250   auto it = dialog_id_to_call_notification_group_id_.find(dialog_id);
2251   if (it != dialog_id_to_call_notification_group_id_.end()) {
2252     return it->second;
2253   }
2254 
2255   if (available_call_notification_group_ids_.empty()) {
2256     // need to reserve new group_id for calls
2257     if (call_notification_group_ids_.size() >= MAX_CALL_NOTIFICATION_GROUPS) {
2258       return {};
2259     }
2260     NotificationGroupId last_group_id;
2261     if (!call_notification_group_ids_.empty()) {
2262       last_group_id = call_notification_group_ids_.back();
2263     }
2264     NotificationGroupId next_notification_group_id;
2265     do {
2266       next_notification_group_id = get_next_notification_group_id();
2267       if (!next_notification_group_id.is_valid()) {
2268         return {};
2269       }
2270     } while (last_group_id.get() >= next_notification_group_id.get());  // just in case
2271     VLOG(notifications) << "Add call " << next_notification_group_id;
2272 
2273     call_notification_group_ids_.push_back(next_notification_group_id);
2274     auto call_notification_group_ids_string = implode(
2275         transform(call_notification_group_ids_, [](NotificationGroupId group_id) { return to_string(group_id.get()); }),
2276         ',');
2277     G()->td_db()->get_binlog_pmc()->set("notification_call_group_ids", call_notification_group_ids_string);
2278     available_call_notification_group_ids_.insert(next_notification_group_id);
2279   }
2280 
2281   auto available_it = available_call_notification_group_ids_.begin();
2282   auto group_id = *available_it;
2283   available_call_notification_group_ids_.erase(available_it);
2284   dialog_id_to_call_notification_group_id_[dialog_id] = group_id;
2285   return group_id;
2286 }
2287 
add_call_notification(DialogId dialog_id,CallId call_id)2288 void NotificationManager::add_call_notification(DialogId dialog_id, CallId call_id) {
2289   CHECK(dialog_id.is_valid());
2290   CHECK(call_id.is_valid());
2291   if (is_disabled() || max_notification_group_count_ == 0) {
2292     return;
2293   }
2294 
2295   auto group_id = get_call_notification_group_id(dialog_id);
2296   if (!group_id.is_valid()) {
2297     VLOG(notifications) << "Ignore notification about " << call_id << " in " << dialog_id;
2298     return;
2299   }
2300 
2301   G()->td().get_actor_unsafe()->messages_manager_->force_create_dialog(dialog_id, "add_call_notification");
2302 
2303   auto &active_notifications = active_call_notifications_[dialog_id];
2304   if (active_notifications.size() >= MAX_CALL_NOTIFICATIONS) {
2305     VLOG(notifications) << "Ignore notification about " << call_id << " in " << dialog_id << " and " << group_id;
2306     return;
2307   }
2308 
2309   auto notification_id = get_next_notification_id();
2310   if (!notification_id.is_valid()) {
2311     return;
2312   }
2313   active_notifications.push_back(ActiveCallNotification{call_id, notification_id});
2314 
2315   add_notification(group_id, NotificationGroupType::Calls, dialog_id, G()->unix_time() + 120, dialog_id, false, false,
2316                    0, notification_id, create_new_call_notification(call_id), "add_call_notification");
2317 }
2318 
remove_call_notification(DialogId dialog_id,CallId call_id)2319 void NotificationManager::remove_call_notification(DialogId dialog_id, CallId call_id) {
2320   CHECK(dialog_id.is_valid());
2321   CHECK(call_id.is_valid());
2322   if (is_disabled() || max_notification_group_count_ == 0) {
2323     return;
2324   }
2325 
2326   auto group_id_it = dialog_id_to_call_notification_group_id_.find(dialog_id);
2327   if (group_id_it == dialog_id_to_call_notification_group_id_.end()) {
2328     VLOG(notifications) << "Ignore removing notification about " << call_id << " in " << dialog_id;
2329     return;
2330   }
2331   auto group_id = group_id_it->second;
2332   CHECK(group_id.is_valid());
2333 
2334   auto &active_notifications = active_call_notifications_[dialog_id];
2335   for (auto it = active_notifications.begin(); it != active_notifications.end(); ++it) {
2336     if (it->call_id == call_id) {
2337       remove_notification(group_id, it->notification_id, true, true, Promise<Unit>(), "remove_call_notification");
2338       active_notifications.erase(it);
2339       if (active_notifications.empty()) {
2340         VLOG(notifications) << "Reuse call " << group_id;
2341         active_call_notifications_.erase(dialog_id);
2342         available_call_notification_group_ids_.insert(group_id);
2343         dialog_id_to_call_notification_group_id_.erase(dialog_id);
2344 
2345         flush_pending_notifications_timeout_.cancel_timeout(group_id.get());
2346         flush_pending_notifications(group_id);
2347         force_flush_pending_updates(group_id, "reuse call group_id");
2348 
2349         auto group_it = get_group(group_id);
2350         LOG_CHECK(group_it->first.dialog_id == dialog_id)
2351             << group_id << ' ' << dialog_id << ' ' << group_it->first << ' ' << group_it->second;
2352         CHECK(group_it->first.last_notification_date == 0);
2353         CHECK(group_it->second.total_count == 0);
2354         CHECK(group_it->second.notifications.empty());
2355         CHECK(group_it->second.pending_notifications.empty());
2356         CHECK(group_it->second.type == NotificationGroupType::Calls);
2357         CHECK(!group_it->second.is_being_loaded_from_database);
2358         CHECK(pending_updates_.count(group_id.get()) == 0);
2359         delete_group(std::move(group_it));
2360       }
2361       return;
2362     }
2363   }
2364 
2365   VLOG(notifications) << "Failed to find " << call_id << " in " << dialog_id << " and " << group_id;
2366 }
2367 
on_notification_group_count_max_changed(bool send_updates)2368 void NotificationManager::on_notification_group_count_max_changed(bool send_updates) {
2369   if (is_disabled()) {
2370     return;
2371   }
2372 
2373   auto new_max_notification_group_count = narrow_cast<int32>(
2374       G()->shared_config().get_option_integer("notification_group_count_max", DEFAULT_GROUP_COUNT_MAX));
2375   CHECK(MIN_NOTIFICATION_GROUP_COUNT_MAX <= new_max_notification_group_count &&
2376         new_max_notification_group_count <= MAX_NOTIFICATION_GROUP_COUNT_MAX);
2377 
2378   auto new_max_notification_group_count_size_t = static_cast<size_t>(new_max_notification_group_count);
2379   if (new_max_notification_group_count_size_t == max_notification_group_count_) {
2380     return;
2381   }
2382 
2383   VLOG(notifications) << "Change max notification group count from " << max_notification_group_count_ << " to "
2384                       << new_max_notification_group_count;
2385 
2386   bool is_increased = new_max_notification_group_count_size_t > max_notification_group_count_;
2387   if (send_updates) {
2388     flush_all_notifications();
2389 
2390     size_t cur_pos = 0;
2391     size_t min_group_count = min(new_max_notification_group_count_size_t, max_notification_group_count_);
2392     size_t max_group_count = max(new_max_notification_group_count_size_t, max_notification_group_count_);
2393     for (auto it = groups_.begin(); it != groups_.end() && cur_pos < max_group_count; ++it, cur_pos++) {
2394       if (cur_pos < min_group_count) {
2395         continue;
2396       }
2397 
2398       auto &group_key = it->first;
2399       auto &group = it->second;
2400       CHECK(group.pending_notifications.empty());
2401       CHECK(pending_updates_.count(group_key.group_id.get()) == 0);
2402 
2403       if (group_key.last_notification_date == 0) {
2404         break;
2405       }
2406 
2407       if (is_increased) {
2408         send_add_group_update(group_key, group);
2409       } else {
2410         send_remove_group_update(group_key, group, vector<int32>());
2411       }
2412     }
2413 
2414     flush_all_pending_updates(true, "on_notification_group_size_max_changed end");
2415 
2416     if (new_max_notification_group_count == 0) {
2417       last_loaded_notification_group_key_ = NotificationGroupKey();
2418       last_loaded_notification_group_key_.last_notification_date = std::numeric_limits<int32>::max();
2419       CHECK(pending_updates_.empty());
2420       groups_.clear();
2421       group_keys_.clear();
2422     }
2423   }
2424 
2425   max_notification_group_count_ = new_max_notification_group_count_size_t;
2426   if (is_increased && last_loaded_notification_group_key_ < get_last_updated_group_key()) {
2427     load_message_notification_groups_from_database(td::max(new_max_notification_group_count, 5), true);
2428   }
2429 }
2430 
on_notification_group_size_max_changed()2431 void NotificationManager::on_notification_group_size_max_changed() {
2432   if (is_disabled()) {
2433     return;
2434   }
2435 
2436   auto new_max_notification_group_size = narrow_cast<int32>(
2437       G()->shared_config().get_option_integer("notification_group_size_max", DEFAULT_GROUP_SIZE_MAX));
2438   CHECK(MIN_NOTIFICATION_GROUP_SIZE_MAX <= new_max_notification_group_size &&
2439         new_max_notification_group_size <= MAX_NOTIFICATION_GROUP_SIZE_MAX);
2440 
2441   auto new_max_notification_group_size_size_t = static_cast<size_t>(new_max_notification_group_size);
2442   if (new_max_notification_group_size_size_t == max_notification_group_size_) {
2443     return;
2444   }
2445 
2446   auto new_keep_notification_group_size =
2447       new_max_notification_group_size_size_t +
2448       clamp(new_max_notification_group_size_size_t, EXTRA_GROUP_SIZE / 2, EXTRA_GROUP_SIZE);
2449 
2450   VLOG(notifications) << "Change max notification group size from " << max_notification_group_size_ << " to "
2451                       << new_max_notification_group_size;
2452 
2453   if (max_notification_group_size_ != 0) {
2454     flush_all_notifications();
2455 
2456     size_t left = max_notification_group_count_;
2457     for (auto it = groups_.begin(); it != groups_.end() && left > 0; ++it, left--) {
2458       auto &group_key = it->first;
2459       auto &group = it->second;
2460       CHECK(group.pending_notifications.empty());
2461       CHECK(pending_updates_.count(group_key.group_id.get()) == 0);
2462 
2463       if (group_key.last_notification_date == 0) {
2464         break;
2465       }
2466 
2467       vector<td_api::object_ptr<td_api::notification>> added_notifications;
2468       vector<int32> removed_notification_ids;
2469       auto notification_count = group.notifications.size();
2470       if (new_max_notification_group_size_size_t < max_notification_group_size_) {
2471         if (notification_count <= new_max_notification_group_size_size_t) {
2472           VLOG(notifications) << "There is no need to update " << group_key.group_id;
2473           continue;
2474         }
2475         for (size_t i = notification_count - min(notification_count, max_notification_group_size_);
2476              i < notification_count - new_max_notification_group_size_size_t; i++) {
2477           removed_notification_ids.push_back(group.notifications[i].notification_id.get());
2478         }
2479         CHECK(!removed_notification_ids.empty());
2480       } else {
2481         if (new_max_notification_group_size_size_t > notification_count) {
2482           load_message_notifications_from_database(group_key, group, new_keep_notification_group_size);
2483         }
2484         if (notification_count <= max_notification_group_size_) {
2485           VLOG(notifications) << "There is no need to update " << group_key.group_id;
2486           continue;
2487         }
2488         for (size_t i = notification_count - min(notification_count, new_max_notification_group_size_size_t);
2489              i < notification_count - max_notification_group_size_; i++) {
2490           added_notifications.push_back(get_notification_object(group_key.dialog_id, group.notifications[i]));
2491           if (added_notifications.back()->type_ == nullptr) {
2492             added_notifications.pop_back();
2493           }
2494         }
2495         if (added_notifications.empty()) {
2496           continue;
2497         }
2498       }
2499       if (!is_destroyed_) {
2500         auto update = td_api::make_object<td_api::updateNotificationGroup>(
2501             group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(),
2502             group_key.dialog_id.get(), true, group.total_count, std::move(added_notifications),
2503             std::move(removed_notification_ids));
2504         VLOG(notifications) << "Send " << as_notification_update(update.get());
2505         send_closure(G()->td(), &Td::send_update, std::move(update));
2506       }
2507     }
2508   }
2509 
2510   max_notification_group_size_ = new_max_notification_group_size_size_t;
2511   keep_notification_group_size_ = new_keep_notification_group_size;
2512 }
2513 
on_online_cloud_timeout_changed()2514 void NotificationManager::on_online_cloud_timeout_changed() {
2515   if (is_disabled()) {
2516     return;
2517   }
2518 
2519   online_cloud_timeout_ms_ = narrow_cast<int32>(
2520       G()->shared_config().get_option_integer("online_cloud_timeout_ms", DEFAULT_ONLINE_CLOUD_TIMEOUT_MS));
2521   VLOG(notifications) << "Set online_cloud_timeout_ms to " << online_cloud_timeout_ms_;
2522 }
2523 
on_notification_cloud_delay_changed()2524 void NotificationManager::on_notification_cloud_delay_changed() {
2525   if (is_disabled()) {
2526     return;
2527   }
2528 
2529   notification_cloud_delay_ms_ = narrow_cast<int32>(
2530       G()->shared_config().get_option_integer("notification_cloud_delay_ms", DEFAULT_ONLINE_CLOUD_DELAY_MS));
2531   VLOG(notifications) << "Set notification_cloud_delay_ms to " << notification_cloud_delay_ms_;
2532 }
2533 
on_notification_default_delay_changed()2534 void NotificationManager::on_notification_default_delay_changed() {
2535   if (is_disabled()) {
2536     return;
2537   }
2538 
2539   notification_default_delay_ms_ = narrow_cast<int32>(
2540       G()->shared_config().get_option_integer("notification_default_delay_ms", DEFAULT_DEFAULT_DELAY_MS));
2541   VLOG(notifications) << "Set notification_default_delay_ms to " << notification_default_delay_ms_;
2542 }
2543 
on_disable_contact_registered_notifications_changed()2544 void NotificationManager::on_disable_contact_registered_notifications_changed() {
2545   if (is_disabled()) {
2546     return;
2547   }
2548 
2549   auto is_disabled = G()->shared_config().get_option_boolean("disable_contact_registered_notifications");
2550 
2551   if (is_disabled == disable_contact_registered_notifications_) {
2552     return;
2553   }
2554 
2555   disable_contact_registered_notifications_ = is_disabled;
2556   if (contact_registered_notifications_sync_state_ == SyncState::Completed) {
2557     run_contact_registered_notifications_sync();
2558   }
2559 }
2560 
on_get_disable_contact_registered_notifications(bool is_disabled)2561 void NotificationManager::on_get_disable_contact_registered_notifications(bool is_disabled) {
2562   if (disable_contact_registered_notifications_ == is_disabled) {
2563     return;
2564   }
2565   disable_contact_registered_notifications_ = is_disabled;
2566 
2567   if (is_disabled) {
2568     G()->shared_config().set_option_boolean("disable_contact_registered_notifications", is_disabled);
2569   } else {
2570     G()->shared_config().set_option_empty("disable_contact_registered_notifications");
2571   }
2572 }
2573 
set_contact_registered_notifications_sync_state(SyncState new_state)2574 void NotificationManager::set_contact_registered_notifications_sync_state(SyncState new_state) {
2575   if (is_disabled()) {
2576     return;
2577   }
2578 
2579   contact_registered_notifications_sync_state_ = new_state;
2580   string value;
2581   value += static_cast<char>(static_cast<int32>(new_state) + '0');
2582   value += static_cast<char>(static_cast<int32>(disable_contact_registered_notifications_) + '0');
2583   G()->td_db()->get_binlog_pmc()->set(get_is_contact_registered_notifications_synchronized_key(), value);
2584 }
2585 
run_contact_registered_notifications_sync()2586 void NotificationManager::run_contact_registered_notifications_sync() {
2587   if (is_disabled()) {
2588     return;
2589   }
2590 
2591   auto is_disabled = disable_contact_registered_notifications_;
2592   if (contact_registered_notifications_sync_state_ == SyncState::NotSynced && !is_disabled) {
2593     set_contact_registered_notifications_sync_state(SyncState::Completed);
2594     return;
2595   }
2596   if (contact_registered_notifications_sync_state_ != SyncState::Pending) {
2597     set_contact_registered_notifications_sync_state(SyncState::Pending);
2598   }
2599 
2600   VLOG(notifications) << "Send SetContactSignUpNotificationQuery with " << is_disabled;
2601   auto promise = PromiseCreator::lambda([actor_id = actor_id(this), is_disabled](Result<Unit> result) {
2602     send_closure(actor_id, &NotificationManager::on_contact_registered_notifications_sync, is_disabled,
2603                  std::move(result));
2604   });
2605   td_->create_handler<SetContactSignUpNotificationQuery>(std::move(promise))->send(is_disabled);
2606 }
2607 
on_contact_registered_notifications_sync(bool is_disabled,Result<Unit> result)2608 void NotificationManager::on_contact_registered_notifications_sync(bool is_disabled, Result<Unit> result) {
2609   CHECK(contact_registered_notifications_sync_state_ == SyncState::Pending);
2610   if (is_disabled != disable_contact_registered_notifications_) {
2611     return run_contact_registered_notifications_sync();
2612   }
2613   if (result.is_ok()) {
2614     // everything is synchronized
2615     set_contact_registered_notifications_sync_state(SyncState::Completed);
2616   } else {
2617     // let's resend the query forever
2618     run_contact_registered_notifications_sync();
2619   }
2620 }
2621 
get_disable_contact_registered_notifications(Promise<Unit> && promise)2622 void NotificationManager::get_disable_contact_registered_notifications(Promise<Unit> &&promise) {
2623   if (is_disabled()) {
2624     promise.set_value(Unit());
2625     return;
2626   }
2627 
2628   td_->create_handler<GetContactSignUpNotificationQuery>(std::move(promise))->send();
2629 }
2630 
process_push_notification(string payload,Promise<Unit> && user_promise)2631 void NotificationManager::process_push_notification(string payload, Promise<Unit> &&user_promise) {
2632   auto promise = PromiseCreator::lambda([user_promise = std::move(user_promise)](Result<Unit> &&result) mutable {
2633     if (result.is_error()) {
2634       if (result.error().code() == 200) {
2635         user_promise.set_value(Unit());
2636       } else {
2637         user_promise.set_error(result.move_as_error());
2638       }
2639     } else {
2640       create_actor<SleepActor>("FinishProcessPushNotificationActor", 0.01, std::move(user_promise)).release();
2641     }
2642   });
2643 
2644   if (is_disabled() || payload == "{}") {
2645     return promise.set_error(Status::Error(200, "Immediate success"));
2646   }
2647 
2648   auto r_receiver_id = get_push_receiver_id(payload);
2649   if (r_receiver_id.is_error()) {
2650     VLOG(notifications) << "Failed to get push notification receiver from \"" << format::escaped(payload)
2651                         << "\":" << r_receiver_id.is_error();
2652     return promise.set_error(r_receiver_id.move_as_error());
2653   }
2654 
2655   auto receiver_id = r_receiver_id.move_as_ok();
2656   auto encryption_keys = td_->device_token_manager_->get_actor_unsafe()->get_encryption_keys();
2657   VLOG(notifications) << "Process push notification \"" << format::escaped(payload)
2658                       << "\" with receiver_id = " << receiver_id << " and " << encryption_keys.size()
2659                       << " encryption keys";
2660   bool was_encrypted = false;
2661   for (auto &key : encryption_keys) {
2662     VLOG(notifications) << "Have key " << key.first;
2663     // VLOG(notifications) << "Have key " << key.first << ": \"" << format::escaped(key.second) << '"';
2664     if (key.first == receiver_id) {
2665       if (!key.second.empty()) {
2666         auto r_payload = decrypt_push(key.first, key.second.str(), std::move(payload));
2667         if (r_payload.is_error()) {
2668           LOG(ERROR) << "Failed to decrypt push: " << r_payload.error();
2669           return promise.set_error(Status::Error(400, "Failed to decrypt push payload"));
2670         }
2671         payload = r_payload.move_as_ok();
2672         was_encrypted = true;
2673       }
2674       receiver_id = 0;
2675       break;
2676     }
2677   }
2678 
2679   if (!td_->is_online()) {
2680     // reset online flag to false to immediately check all connections aliveness
2681     send_closure(G()->state_manager(), &StateManager::on_online, false);
2682   }
2683 
2684   if (receiver_id == 0 || receiver_id == G()->get_my_id()) {
2685     auto status = process_push_notification_payload(payload, was_encrypted, promise);
2686     if (status.is_error()) {
2687       if (status.code() == 406 || status.code() == 200) {
2688         return promise.set_error(std::move(status));
2689       }
2690 
2691       LOG(ERROR) << "Receive error " << status << ", while parsing push payload " << payload;
2692       return promise.set_error(Status::Error(400, status.message()));
2693     }
2694     // promise will be set after updateNotificationGroup is sent to the client
2695     return;
2696   }
2697 
2698   VLOG(notifications) << "Failed to process push notification";
2699   promise.set_error(Status::Error(200, "Immediate success"));
2700 }
2701 
convert_loc_key(const string & loc_key)2702 string NotificationManager::convert_loc_key(const string &loc_key) {
2703   if (loc_key.size() <= 8) {
2704     if (loc_key == "MESSAGES" || loc_key == "ALBUM") {
2705       return "MESSAGES";
2706     }
2707     return string();
2708   }
2709   switch (loc_key[8]) {
2710     case 'A':
2711       if (loc_key == "PINNED_GAME") {
2712         return "PINNED_MESSAGE_GAME";
2713       }
2714       if (loc_key == "PINNED_GAME_SCORE") {
2715         return "PINNED_MESSAGE_GAME_SCORE";
2716       }
2717       if (loc_key == "CHAT_CREATED") {
2718         return "MESSAGE_BASIC_GROUP_CHAT_CREATE";
2719       }
2720       if (loc_key == "MESSAGE_AUDIO") {
2721         return "MESSAGE_VOICE_NOTE";
2722       }
2723       break;
2724     case 'C':
2725       if (loc_key == "MESSAGE_CONTACT") {
2726         return "MESSAGE_CONTACT";
2727       }
2728       break;
2729     case 'D':
2730       if (loc_key == "MESSAGE_DOC") {
2731         return "MESSAGE_DOCUMENT";
2732       }
2733       if (loc_key == "MESSAGE_DOCS") {
2734         return "MESSAGE_DOCUMENTS";
2735       }
2736       if (loc_key == "ENCRYPTED_MESSAGE") {
2737         return "MESSAGE";
2738       }
2739       break;
2740     case 'E':
2741       if (loc_key == "PINNED_GEO") {
2742         return "PINNED_MESSAGE_LOCATION";
2743       }
2744       if (loc_key == "PINNED_GEOLIVE") {
2745         return "PINNED_MESSAGE_LIVE_LOCATION";
2746       }
2747       if (loc_key == "CHAT_DELETE_MEMBER") {
2748         return "MESSAGE_CHAT_DELETE_MEMBER";
2749       }
2750       if (loc_key == "CHAT_DELETE_YOU") {
2751         return "MESSAGE_CHAT_DELETE_MEMBER_YOU";
2752       }
2753       if (loc_key == "PINNED_TEXT") {
2754         return "PINNED_MESSAGE_TEXT";
2755       }
2756       break;
2757     case 'F':
2758       if (loc_key == "MESSAGE_FWDS") {
2759         return "MESSAGE_FORWARDS";
2760       }
2761       break;
2762     case 'G':
2763       if (loc_key == "MESSAGE_GAME") {
2764         return "MESSAGE_GAME";
2765       }
2766       if (loc_key == "MESSAGE_GAME_SCORE") {
2767         return "MESSAGE_GAME_SCORE";
2768       }
2769       if (loc_key == "MESSAGE_GEO") {
2770         return "MESSAGE_LOCATION";
2771       }
2772       if (loc_key == "MESSAGE_GEOLIVE") {
2773         return "MESSAGE_LIVE_LOCATION";
2774       }
2775       if (loc_key == "MESSAGE_GIF") {
2776         return "MESSAGE_ANIMATION";
2777       }
2778       break;
2779     case 'H':
2780       if (loc_key == "PINNED_PHOTO") {
2781         return "PINNED_MESSAGE_PHOTO";
2782       }
2783       break;
2784     case 'I':
2785       if (loc_key == "PINNED_VIDEO") {
2786         return "PINNED_MESSAGE_VIDEO";
2787       }
2788       if (loc_key == "PINNED_GIF") {
2789         return "PINNED_MESSAGE_ANIMATION";
2790       }
2791       if (loc_key == "MESSAGE_INVOICE") {
2792         return "MESSAGE_INVOICE";
2793       }
2794       break;
2795     case 'J':
2796       if (loc_key == "CONTACT_JOINED") {
2797         return "MESSAGE_CONTACT_REGISTERED";
2798       }
2799       break;
2800     case 'L':
2801       if (loc_key == "CHAT_TITLE_EDITED") {
2802         return "MESSAGE_CHAT_CHANGE_TITLE";
2803       }
2804       break;
2805     case 'N':
2806       if (loc_key == "CHAT_JOINED") {
2807         return "MESSAGE_CHAT_JOIN_BY_LINK";
2808       }
2809       if (loc_key == "MESSAGE_NOTEXT") {
2810         return "MESSAGE";
2811       }
2812       if (loc_key == "MESSAGE_NOTHEME") {
2813         return "MESSAGE_CHAT_CHANGE_THEME";
2814       }
2815       if (loc_key == "PINNED_INVOICE") {
2816         return "PINNED_MESSAGE_INVOICE";
2817       }
2818       break;
2819     case 'O':
2820       if (loc_key == "PINNED_DOC") {
2821         return "PINNED_MESSAGE_DOCUMENT";
2822       }
2823       if (loc_key == "PINNED_POLL") {
2824         return "PINNED_MESSAGE_POLL";
2825       }
2826       if (loc_key == "PINNED_CONTACT") {
2827         return "PINNED_MESSAGE_CONTACT";
2828       }
2829       if (loc_key == "PINNED_NOTEXT") {
2830         return "PINNED_MESSAGE";
2831       }
2832       if (loc_key == "PINNED_ROUND") {
2833         return "PINNED_MESSAGE_VIDEO_NOTE";
2834       }
2835       break;
2836     case 'P':
2837       if (loc_key == "MESSAGE_PHOTO") {
2838         return "MESSAGE_PHOTO";
2839       }
2840       if (loc_key == "MESSAGE_PHOTOS") {
2841         return "MESSAGE_PHOTOS";
2842       }
2843       if (loc_key == "MESSAGE_PHOTO_SECRET") {
2844         return "MESSAGE_SECRET_PHOTO";
2845       }
2846       if (loc_key == "MESSAGE_PLAYLIST") {
2847         return "MESSAGE_AUDIOS";
2848       }
2849       if (loc_key == "MESSAGE_POLL") {
2850         return "MESSAGE_POLL";
2851       }
2852       break;
2853     case 'Q':
2854       if (loc_key == "MESSAGE_QUIZ") {
2855         return "MESSAGE_QUIZ";
2856       }
2857       break;
2858     case 'R':
2859       if (loc_key == "MESSAGE_ROUND") {
2860         return "MESSAGE_VIDEO_NOTE";
2861       }
2862       break;
2863     case 'S':
2864       if (loc_key == "MESSAGE_SCREENSHOT") {
2865         return "MESSAGE_SCREENSHOT_TAKEN";
2866       }
2867       if (loc_key == "MESSAGE_STICKER") {
2868         return "MESSAGE_STICKER";
2869       }
2870       break;
2871     case 'T':
2872       if (loc_key == "CHAT_LEFT") {
2873         return "MESSAGE_CHAT_DELETE_MEMBER_LEFT";
2874       }
2875       if (loc_key == "MESSAGE_TEXT") {
2876         return "MESSAGE_TEXT";
2877       }
2878       if (loc_key == "PINNED_STICKER") {
2879         return "PINNED_MESSAGE_STICKER";
2880       }
2881       if (loc_key == "CHAT_PHOTO_EDITED") {
2882         return "MESSAGE_CHAT_CHANGE_PHOTO";
2883       }
2884       if (loc_key == "MESSAGE_THEME") {
2885         return "MESSAGE_CHAT_CHANGE_THEME";
2886       }
2887       break;
2888     case 'U':
2889       if (loc_key == "PINNED_AUDIO") {
2890         return "PINNED_MESSAGE_VOICE_NOTE";
2891       }
2892       if (loc_key == "PINNED_QUIZ") {
2893         return "PINNED_MESSAGE_QUIZ";
2894       }
2895       if (loc_key == "CHAT_RETURNED") {
2896         return "MESSAGE_CHAT_ADD_MEMBERS_RETURNED";
2897       }
2898       break;
2899     case 'V':
2900       if (loc_key == "MESSAGE_VIDEO") {
2901         return "MESSAGE_VIDEO";
2902       }
2903       if (loc_key == "MESSAGE_VIDEOS") {
2904         return "MESSAGE_VIDEOS";
2905       }
2906       if (loc_key == "MESSAGE_VIDEO_SECRET") {
2907         return "MESSAGE_SECRET_VIDEO";
2908       }
2909       break;
2910     case '_':
2911       if (loc_key == "CHAT_ADD_MEMBER") {
2912         return "MESSAGE_CHAT_ADD_MEMBERS";
2913       }
2914       if (loc_key == "CHAT_ADD_YOU") {
2915         return "MESSAGE_CHAT_ADD_MEMBERS_YOU";
2916       }
2917       if (loc_key == "CHAT_REQ_JOINED") {
2918         return "MESSAGE_CHAT_JOIN_BY_REQUEST";
2919       }
2920       break;
2921   }
2922   return string();
2923 }
2924 
process_push_notification_payload(string payload,bool was_encrypted,Promise<Unit> & promise)2925 Status NotificationManager::process_push_notification_payload(string payload, bool was_encrypted,
2926                                                               Promise<Unit> &promise) {
2927   VLOG(notifications) << "Process push notification payload " << payload;
2928   auto r_json_value = json_decode(payload);
2929   if (r_json_value.is_error()) {
2930     return Status::Error("Failed to parse payload as JSON object");
2931   }
2932 
2933   auto json_value = r_json_value.move_as_ok();
2934   if (json_value.type() != JsonValue::Type::Object) {
2935     return Status::Error("Expected a JSON object as push payload");
2936   }
2937 
2938   auto data = std::move(json_value.get_object());
2939   int32 sent_date = G()->unix_time();
2940   if (has_json_object_field(data, "data")) {
2941     TRY_RESULT(date, get_json_object_int_field(data, "date", true, sent_date));
2942     if (sent_date - 28 * 86400 <= date && date <= sent_date + 5) {
2943       sent_date = date;
2944     }
2945     TRY_RESULT(data_data, get_json_object_field(data, "data", JsonValue::Type::Object, false));
2946     data = std::move(data_data.get_object());
2947   }
2948 
2949   string loc_key;
2950   JsonObject custom;
2951   string announcement_message_text;
2952   vector<string> loc_args;
2953   string sender_name;
2954   for (auto &field_value : data) {
2955     if (field_value.first == "loc_key") {
2956       if (field_value.second.type() != JsonValue::Type::String) {
2957         return Status::Error("Expected loc_key as a String");
2958       }
2959       loc_key = field_value.second.get_string().str();
2960     } else if (field_value.first == "loc_args") {
2961       if (field_value.second.type() != JsonValue::Type::Array) {
2962         return Status::Error("Expected loc_args as an Array");
2963       }
2964       loc_args.reserve(field_value.second.get_array().size());
2965       for (auto &arg : field_value.second.get_array()) {
2966         if (arg.type() != JsonValue::Type::String) {
2967           return Status::Error("Expected loc_arg as a String");
2968         }
2969         loc_args.push_back(arg.get_string().str());
2970       }
2971     } else if (field_value.first == "custom") {
2972       if (field_value.second.type() != JsonValue::Type::Object) {
2973         return Status::Error("Expected custom as an Object");
2974       }
2975       custom = std::move(field_value.second.get_object());
2976     } else if (field_value.first == "message") {
2977       if (field_value.second.type() != JsonValue::Type::String) {
2978         return Status::Error("Expected announcement message text as a String");
2979       }
2980       announcement_message_text = field_value.second.get_string().str();
2981     } else if (field_value.first == "google.sent_time") {
2982       TRY_RESULT(google_sent_time, get_json_object_long_field(data, "google.sent_time"));
2983       google_sent_time /= 1000;
2984       if (sent_date - 28 * 86400 <= google_sent_time && google_sent_time <= sent_date + 5) {
2985         sent_date = narrow_cast<int32>(google_sent_time);
2986       }
2987     }
2988   }
2989 
2990   if (!clean_input_string(loc_key)) {
2991     return Status::Error(PSLICE() << "Receive invalid loc_key " << format::escaped(loc_key));
2992   }
2993   if (loc_key.empty()) {
2994     return Status::Error("Receive empty loc_key");
2995   }
2996   for (auto &loc_arg : loc_args) {
2997     if (!clean_input_string(loc_arg)) {
2998       return Status::Error(PSLICE() << "Receive invalid loc_arg " << format::escaped(loc_arg));
2999     }
3000   }
3001 
3002   if (loc_key == "MESSAGE_ANNOUNCEMENT") {
3003     if (announcement_message_text.empty()) {
3004       return Status::Error("Have empty announcement message text");
3005     }
3006     TRY_RESULT(announcement_id, get_json_object_int_field(custom, "announcement"));
3007     auto &date = announcement_id_date_[announcement_id];
3008     auto now = G()->unix_time();
3009     if (date >= now - ANNOUNCEMENT_ID_CACHE_TIME) {
3010       VLOG(notifications) << "Ignore duplicate announcement " << announcement_id;
3011       return Status::Error(200, "Immediate success");
3012     }
3013     date = now;
3014 
3015     auto update = telegram_api::make_object<telegram_api::updateServiceNotification>(
3016         telegram_api::updateServiceNotification::INBOX_DATE_MASK, false, G()->unix_time(), string(),
3017         announcement_message_text, nullptr, vector<telegram_api::object_ptr<telegram_api::MessageEntity>>());
3018     send_closure(G()->messages_manager(), &MessagesManager::on_update_service_notification, std::move(update), false,
3019                  std::move(promise));
3020     save_announcement_ids();
3021     return Status::OK();
3022   }
3023   if (!announcement_message_text.empty()) {
3024     LOG(ERROR) << "Have non-empty announcement message text with loc_key = " << loc_key;
3025   }
3026 
3027   if (loc_key == "DC_UPDATE") {
3028     TRY_RESULT(dc_id, get_json_object_int_field(custom, "dc", false));
3029     TRY_RESULT(addr, get_json_object_string_field(custom, "addr", false));
3030     if (!DcId::is_valid(dc_id)) {
3031       return Status::Error("Invalid datacenter ID");
3032     }
3033     if (!clean_input_string(addr)) {
3034       return Status::Error(PSLICE() << "Receive invalid addr " << format::escaped(addr));
3035     }
3036     send_closure(G()->connection_creator(), &ConnectionCreator::on_dc_update, DcId::internal(dc_id), std::move(addr),
3037                  std::move(promise));
3038     return Status::OK();
3039   }
3040 
3041   if (loc_key == "SESSION_REVOKE") {
3042     if (was_encrypted) {
3043       send_closure(td_->auth_manager_actor_, &AuthManager::on_authorization_lost, "SESSION_REVOKE");
3044     } else {
3045       LOG(ERROR) << "Receive unencrypted SESSION_REVOKE push notification";
3046     }
3047     promise.set_value(Unit());
3048     return Status::OK();
3049   }
3050 
3051   if (loc_key == "LOCKED_MESSAGE") {
3052     return Status::Error(200, "Immediate success");
3053   }
3054 
3055   if (loc_key == "GEO_LIVE_PENDING") {
3056     td_->messages_manager_->on_update_some_live_location_viewed(std::move(promise));
3057     return Status::OK();
3058   }
3059 
3060   if (loc_key == "AUTH_REGION" || loc_key == "AUTH_UNKNOWN") {
3061     // TODO
3062     return Status::Error(200, "Immediate success");
3063   }
3064 
3065   DialogId dialog_id;
3066   if (has_json_object_field(custom, "from_id")) {
3067     TRY_RESULT(user_id_int, get_json_object_long_field(custom, "from_id"));
3068     UserId user_id(user_id_int);
3069     if (!user_id.is_valid()) {
3070       return Status::Error("Receive invalid user_id");
3071     }
3072     dialog_id = DialogId(user_id);
3073   }
3074   if (has_json_object_field(custom, "chat_id")) {
3075     TRY_RESULT(chat_id_int, get_json_object_long_field(custom, "chat_id"));
3076     ChatId chat_id(chat_id_int);
3077     if (!chat_id.is_valid()) {
3078       return Status::Error("Receive invalid chat_id");
3079     }
3080     dialog_id = DialogId(chat_id);
3081   }
3082   if (has_json_object_field(custom, "channel_id")) {
3083     TRY_RESULT(channel_id_int, get_json_object_long_field(custom, "channel_id"));
3084     ChannelId channel_id(channel_id_int);
3085     if (!channel_id.is_valid()) {
3086       return Status::Error("Receive invalid channel_id");
3087     }
3088     dialog_id = DialogId(channel_id);
3089   }
3090   if (has_json_object_field(custom, "encryption_id")) {
3091     TRY_RESULT(secret_chat_id_int, get_json_object_int_field(custom, "encryption_id"));
3092     SecretChatId secret_chat_id(secret_chat_id_int);
3093     if (!secret_chat_id.is_valid()) {
3094       return Status::Error("Receive invalid secret_chat_id");
3095     }
3096     dialog_id = DialogId(secret_chat_id);
3097   }
3098   if (!dialog_id.is_valid()) {
3099     if (loc_key == "ENCRYPTED_MESSAGE" || loc_key == "MESSAGE_MUTED") {
3100       return Status::Error(406, "Force loading data from the server");
3101     }
3102     return Status::Error("Can't find dialog_id");
3103   }
3104 
3105   if (loc_key == "READ_HISTORY") {
3106     if (dialog_id.get_type() == DialogType::SecretChat) {
3107       return Status::Error("Receive read history in a secret chat");
3108     }
3109 
3110     TRY_RESULT(max_id, get_json_object_int_field(custom, "max_id"));
3111     ServerMessageId max_server_message_id(max_id);
3112     if (!max_server_message_id.is_valid()) {
3113       return Status::Error("Receive invalid max_id");
3114     }
3115 
3116     td_->messages_manager_->read_history_inbox(dialog_id, MessageId(max_server_message_id), -1,
3117                                                "process_push_notification_payload");
3118     promise.set_value(Unit());
3119     return Status::OK();
3120   }
3121 
3122   if (loc_key == "MESSAGE_DELETED") {
3123     if (dialog_id.get_type() == DialogType::SecretChat) {
3124       return Status::Error("Receive MESSAGE_DELETED in a secret chat");
3125     }
3126     TRY_RESULT(server_message_ids_str, get_json_object_string_field(custom, "messages", false));
3127     auto server_message_ids = full_split(server_message_ids_str, ',');
3128     vector<MessageId> message_ids;
3129     for (const auto &server_message_id_str : server_message_ids) {
3130       TRY_RESULT(server_message_id_int, to_integer_safe<int32>(server_message_id_str));
3131       ServerMessageId server_message_id(server_message_id_int);
3132       if (!server_message_id.is_valid()) {
3133         return Status::Error("Receive invalid message_id");
3134       }
3135       message_ids.push_back(MessageId(server_message_id));
3136     }
3137     td_->messages_manager_->remove_message_notifications_by_message_ids(dialog_id, message_ids);
3138     promise.set_value(Unit());
3139     return Status::OK();
3140   }
3141 
3142   if (loc_key == "MESSAGE_MUTED") {
3143     return Status::Error(406, "Notifications about muted messages force loading data from the server");
3144   }
3145 
3146   TRY_RESULT(msg_id, get_json_object_int_field(custom, "msg_id"));
3147   ServerMessageId server_message_id(msg_id);
3148   if (server_message_id != ServerMessageId() && !server_message_id.is_valid()) {
3149     return Status::Error("Receive invalid msg_id");
3150   }
3151 
3152   TRY_RESULT(random_id, get_json_object_long_field(custom, "random_id"));
3153 
3154   UserId sender_user_id;
3155   DialogId sender_dialog_id;
3156   if (has_json_object_field(custom, "chat_from_broadcast_id")) {
3157     TRY_RESULT(sender_channel_id_int, get_json_object_long_field(custom, "chat_from_broadcast_id"));
3158     sender_dialog_id = DialogId(ChannelId(sender_channel_id_int));
3159     if (!sender_dialog_id.is_valid()) {
3160       return Status::Error("Receive invalid chat_from_broadcast_id");
3161     }
3162   } else if (has_json_object_field(custom, "chat_from_group_id")) {
3163     TRY_RESULT(sender_channel_id_int, get_json_object_long_field(custom, "chat_from_group_id"));
3164     sender_dialog_id = DialogId(ChannelId(sender_channel_id_int));
3165     if (!sender_dialog_id.is_valid()) {
3166       return Status::Error("Receive invalid chat_from_group_id");
3167     }
3168   } else if (has_json_object_field(custom, "chat_from_id")) {
3169     TRY_RESULT(sender_user_id_int, get_json_object_long_field(custom, "chat_from_id"));
3170     sender_user_id = UserId(sender_user_id_int);
3171     if (!sender_user_id.is_valid()) {
3172       return Status::Error("Receive invalid chat_from_id");
3173     }
3174   } else if (dialog_id.get_type() == DialogType::User) {
3175     sender_user_id = dialog_id.get_user_id();
3176   } else if (dialog_id.get_type() == DialogType::Channel) {
3177     sender_dialog_id = dialog_id;
3178   }
3179 
3180   TRY_RESULT(contains_mention_int, get_json_object_int_field(custom, "mention"));
3181   bool contains_mention = contains_mention_int != 0;
3182 
3183   if (begins_with(loc_key, "CHANNEL_MESSAGE") || loc_key == "CHANNEL_ALBUM") {
3184     if (dialog_id.get_type() != DialogType::Channel) {
3185       return Status::Error("Receive wrong chat type");
3186     }
3187     loc_key = loc_key.substr(8);
3188   }
3189   if (begins_with(loc_key, "CHAT_")) {
3190     auto dialog_type = dialog_id.get_type();
3191     if (dialog_type != DialogType::Chat && dialog_type != DialogType::Channel) {
3192       return Status::Error("Receive wrong chat type");
3193     }
3194 
3195     if (begins_with(loc_key, "CHAT_MESSAGE") || loc_key == "CHAT_ALBUM") {
3196       loc_key = loc_key.substr(5);
3197     }
3198     if (loc_args.empty()) {
3199       return Status::Error("Expect sender name as first argument");
3200     }
3201     sender_name = std::move(loc_args[0]);
3202     loc_args.erase(loc_args.begin());
3203   }
3204   if (begins_with(loc_key, "MESSAGE") && !server_message_id.is_valid()) {
3205     return Status::Error("Receive no message ID");
3206   }
3207   if (begins_with(loc_key, "ENCRYPT") || random_id != 0) {
3208     if (dialog_id.get_type() != DialogType::SecretChat) {
3209       return Status::Error("Receive wrong chat type");
3210     }
3211   }
3212   if (server_message_id.is_valid() && dialog_id.get_type() == DialogType::SecretChat) {
3213     return Status::Error("Receive message ID in secret chat push");
3214   }
3215 
3216   if (begins_with(loc_key, "ENCRYPTION_")) {
3217     // TODO ENCRYPTION_REQUEST/ENCRYPTION_ACCEPT notifications
3218     return Status::Error(406, "New secret chat notification is not supported");
3219   }
3220 
3221   if (begins_with(loc_key, "PHONE_CALL_") || begins_with(loc_key, "VIDEO_CALL_")) {
3222     // TODO PHONE_CALL_REQUEST/PHONE_CALL_DECLINE/PHONE_CALL_MISSED/VIDEO_CALL_REQUEST/VIDEO_CALL_MISSED notifications
3223     return Status::Error(406, "Phone call notification is not supported");
3224   }
3225 
3226   loc_key = convert_loc_key(loc_key);
3227   if (loc_key.empty()) {
3228     return Status::Error("Push type is unknown");
3229   }
3230 
3231   if (loc_args.empty()) {
3232     return Status::Error("Expected chat name as next argument");
3233   }
3234   if (dialog_id.get_type() == DialogType::User) {
3235     sender_name = std::move(loc_args[0]);
3236   } else if ((sender_user_id.is_valid() || sender_dialog_id.is_valid()) && begins_with(loc_key, "PINNED_")) {
3237     if (loc_args.size() < 2) {
3238       return Status::Error("Expected chat title as the last argument");
3239     }
3240     loc_args.pop_back();
3241   }
3242   // chat title for CHAT_*, CHANNEL_* and ENCRYPTED_MESSAGE, sender name for MESSAGE_* and CONTACT_JOINED
3243   // chat title or sender name for PINNED_*
3244   loc_args.erase(loc_args.begin());
3245 
3246   string arg;
3247   if (loc_key == "MESSAGE_GAME_SCORE") {
3248     if (loc_args.size() != 2) {
3249       return Status::Error("Expected 2 arguments for MESSAGE_GAME_SCORE");
3250     }
3251     TRY_RESULT(score, to_integer_safe<int32>(loc_args[1]));
3252     if (score < 0) {
3253       return Status::Error("Expected score to be non-negative");
3254     }
3255     arg = PSTRING() << loc_args[1] << ' ' << loc_args[0];
3256     loc_args.clear();
3257   }
3258   if (loc_args.size() > 1) {
3259     return Status::Error("Receive too much arguments");
3260   }
3261 
3262   if (loc_args.size() == 1) {
3263     arg = std::move(loc_args[0]);
3264   }
3265 
3266   if (sender_user_id.is_valid() && !td_->contacts_manager_->have_user_force(sender_user_id)) {
3267     int64 sender_access_hash = -1;
3268     telegram_api::object_ptr<telegram_api::UserProfilePhoto> sender_photo;
3269     TRY_RESULT(mtpeer, get_json_object_field(custom, "mtpeer", JsonValue::Type::Object));
3270     if (mtpeer.type() != JsonValue::Type::Null) {
3271       TRY_RESULT(ah, get_json_object_string_field(mtpeer.get_object(), "ah"));
3272       if (!ah.empty()) {
3273         TRY_RESULT_ASSIGN(sender_access_hash, to_integer_safe<int64>(ah));
3274       }
3275       TRY_RESULT(ph, get_json_object_field(mtpeer.get_object(), "ph", JsonValue::Type::Object));
3276       if (ph.type() != JsonValue::Type::Null) {
3277         // TODO parse sender photo
3278       }
3279     }
3280 
3281     int32 flags = USER_FLAG_IS_INACCESSIBLE;
3282     if (sender_access_hash != -1) {
3283       // set phone number flag to show that this is a full access hash
3284       flags |= USER_FLAG_HAS_ACCESS_HASH | USER_FLAG_HAS_PHONE_NUMBER;
3285     }
3286     auto user_name = sender_user_id.get() == 136817688 ? "Channel" : sender_name;
3287     auto user = telegram_api::make_object<telegram_api::user>(
3288         flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
3289         false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
3290         false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
3291         sender_user_id.get(), sender_access_hash, user_name, string(), string(), string(), std::move(sender_photo),
3292         nullptr, 0, Auto(), string(), string());
3293     td_->contacts_manager_->on_get_user(std::move(user), "process_push_notification_payload");
3294   }
3295 
3296   Photo attached_photo;
3297   Document attached_document;
3298   if (has_json_object_field(custom, "attachb64")) {
3299     TRY_RESULT(attachb64, get_json_object_string_field(custom, "attachb64", false));
3300     TRY_RESULT(attach, base64url_decode(attachb64));
3301 
3302     TlParser gzip_parser(attach);
3303     int32 id = gzip_parser.fetch_int();
3304     if (gzip_parser.get_error()) {
3305       return Status::Error(PSLICE() << "Failed to parse attach: " << gzip_parser.get_error());
3306     }
3307     BufferSlice buffer;
3308     if (id == mtproto_api::gzip_packed::ID) {
3309       mtproto_api::gzip_packed gzip(gzip_parser);
3310       gzip_parser.fetch_end();
3311       if (gzip_parser.get_error()) {
3312         return Status::Error(PSLICE() << "Failed to parse mtproto_api::gzip_packed in attach: "
3313                                       << gzip_parser.get_error());
3314       }
3315       buffer = gzdecode(gzip.packed_data_);
3316       if (buffer.empty()) {
3317         return Status::Error("Failed to uncompress attach");
3318       }
3319     } else {
3320       buffer = BufferSlice(attach);
3321     }
3322 
3323     TlBufferParser parser(&buffer);
3324     auto result = telegram_api::Object::fetch(parser);
3325     parser.fetch_end();
3326     const char *error = parser.get_error();
3327     if (error != nullptr) {
3328       LOG(ERROR) << "Can't parse attach: " << Slice(error) << " at " << parser.get_error_pos() << ": "
3329                  << format::as_hex_dump<4>(Slice(attach));
3330     } else {
3331       switch (result->get_id()) {
3332         case telegram_api::photo::ID:
3333           if (ends_with(loc_key, "MESSAGE_PHOTO") || ends_with(loc_key, "MESSAGE_TEXT")) {
3334             VLOG(notifications) << "Have attached photo";
3335             loc_key.resize(loc_key.rfind('_') + 1);
3336             loc_key += "PHOTO";
3337             attached_photo = get_photo(td_->file_manager_.get(),
3338                                        telegram_api::move_object_as<telegram_api::photo>(result), dialog_id);
3339           } else {
3340             LOG(ERROR) << "Receive attached photo for " << loc_key;
3341           }
3342           break;
3343         case telegram_api::document::ID: {
3344           if (ends_with(loc_key, "MESSAGE_ANIMATION") || ends_with(loc_key, "MESSAGE_AUDIO") ||
3345               ends_with(loc_key, "MESSAGE_DOCUMENT") || ends_with(loc_key, "MESSAGE_STICKER") ||
3346               ends_with(loc_key, "MESSAGE_VIDEO") || ends_with(loc_key, "MESSAGE_VIDEO_NOTE") ||
3347               ends_with(loc_key, "MESSAGE_VOICE_NOTE") || ends_with(loc_key, "MESSAGE_TEXT")) {
3348             VLOG(notifications) << "Have attached document";
3349             attached_document = td_->documents_manager_->on_get_document(
3350                 telegram_api::move_object_as<telegram_api::document>(result), dialog_id);
3351             if (!attached_document.empty()) {
3352               if (ends_with(loc_key, "_NOTE")) {
3353                 loc_key.resize(loc_key.rfind('_'));
3354               }
3355               loc_key.resize(loc_key.rfind('_') + 1);
3356 
3357               auto type = [attached_document] {
3358                 switch (attached_document.type) {
3359                   case Document::Type::Animation:
3360                     return "ANIMATION";
3361                   case Document::Type::Audio:
3362                     return "AUDIO";
3363                   case Document::Type::General:
3364                     return "DOCUMENT";
3365                   case Document::Type::Sticker:
3366                     return "STICKER";
3367                   case Document::Type::Video:
3368                     return "VIDEO";
3369                   case Document::Type::VideoNote:
3370                     return "VIDEO_NOTE";
3371                   case Document::Type::VoiceNote:
3372                     return "VOICE_NOTE";
3373                   case Document::Type::Unknown:
3374                   default:
3375                     UNREACHABLE();
3376                     return "UNREACHABLE";
3377                 }
3378               }();
3379 
3380               loc_key += type;
3381             }
3382           } else {
3383             LOG(ERROR) << "Receive attached document for " << loc_key;
3384           }
3385           break;
3386         }
3387         default:
3388           LOG(ERROR) << "Receive unexpected attached " << to_string(result);
3389       }
3390     }
3391   }
3392   if (!arg.empty()) {
3393     uint32 emoji = [&] {
3394       if (ends_with(loc_key, "PHOTO")) {
3395         return 0x1F5BC;
3396       }
3397       if (ends_with(loc_key, "ANIMATION")) {
3398         return 0x1F3AC;
3399       }
3400       if (ends_with(loc_key, "DOCUMENT")) {
3401         return 0x1F4CE;
3402       }
3403       if (ends_with(loc_key, "VIDEO")) {
3404         return 0x1F4F9;
3405       }
3406       return 0;
3407     }();
3408     if (emoji != 0) {
3409       string prefix;
3410       append_utf8_character(prefix, emoji);
3411       prefix += ' ';
3412       if (begins_with(arg, prefix)) {
3413         arg = arg.substr(prefix.size());
3414       }
3415     }
3416   }
3417 
3418   if (has_json_object_field(custom, "edit_date")) {
3419     if (random_id != 0) {
3420       return Status::Error("Receive edit of secret message");
3421     }
3422     TRY_RESULT(edit_date, get_json_object_int_field(custom, "edit_date"));
3423     if (edit_date <= 0) {
3424       return Status::Error("Receive wrong edit date");
3425     }
3426     edit_message_push_notification(dialog_id, MessageId(server_message_id), edit_date, std::move(loc_key),
3427                                    std::move(arg), std::move(attached_photo), std::move(attached_document), 0,
3428                                    std::move(promise));
3429   } else {
3430     bool is_from_scheduled = has_json_object_field(custom, "schedule");
3431     bool is_silent = has_json_object_field(custom, "silent");
3432     add_message_push_notification(dialog_id, MessageId(server_message_id), random_id, sender_user_id, sender_dialog_id,
3433                                   std::move(sender_name), sent_date, is_from_scheduled, contains_mention, is_silent,
3434                                   is_silent, std::move(loc_key), std::move(arg), std::move(attached_photo),
3435                                   std::move(attached_document), NotificationId(), 0, std::move(promise));
3436   }
3437   return Status::OK();
3438 }
3439 
3440 class NotificationManager::AddMessagePushNotificationLogEvent {
3441  public:
3442   DialogId dialog_id_;
3443   MessageId message_id_;
3444   int64 random_id_;
3445   UserId sender_user_id_;
3446   DialogId sender_dialog_id_;
3447   string sender_name_;
3448   int32 date_;
3449   bool is_from_scheduled_;
3450   bool contains_mention_;
3451   bool is_silent_;
3452   string loc_key_;
3453   string arg_;
3454   Photo photo_;
3455   Document document_;
3456   NotificationId notification_id_;
3457 
3458   template <class StorerT>
store(StorerT & storer) const3459   void store(StorerT &storer) const {
3460     bool has_message_id = message_id_.is_valid();
3461     bool has_random_id = random_id_ != 0;
3462     bool has_sender = sender_user_id_.is_valid();
3463     bool has_sender_name = !sender_name_.empty();
3464     bool has_arg = !arg_.empty();
3465     bool has_photo = !photo_.is_empty();
3466     bool has_document = !document_.empty();
3467     bool has_sender_dialog_id = sender_dialog_id_.is_valid();
3468     BEGIN_STORE_FLAGS();
3469     STORE_FLAG(contains_mention_);
3470     STORE_FLAG(is_silent_);
3471     STORE_FLAG(has_message_id);
3472     STORE_FLAG(has_random_id);
3473     STORE_FLAG(has_sender);
3474     STORE_FLAG(has_sender_name);
3475     STORE_FLAG(has_arg);
3476     STORE_FLAG(has_photo);
3477     STORE_FLAG(has_document);
3478     STORE_FLAG(is_from_scheduled_);
3479     STORE_FLAG(has_sender_dialog_id);
3480     END_STORE_FLAGS();
3481     td::store(dialog_id_, storer);
3482     if (has_message_id) {
3483       td::store(message_id_, storer);
3484     }
3485     if (has_random_id) {
3486       td::store(random_id_, storer);
3487     }
3488     if (has_sender) {
3489       td::store(sender_user_id_, storer);
3490     }
3491     if (has_sender_name) {
3492       td::store(sender_name_, storer);
3493     }
3494     td::store(date_, storer);
3495     td::store(loc_key_, storer);
3496     if (has_arg) {
3497       td::store(arg_, storer);
3498     }
3499     if (has_photo) {
3500       td::store(photo_, storer);
3501     }
3502     if (has_document) {
3503       td::store(document_, storer);
3504     }
3505     td::store(notification_id_, storer);
3506     if (has_sender_dialog_id) {
3507       td::store(sender_dialog_id_, storer);
3508     }
3509   }
3510 
3511   template <class ParserT>
parse(ParserT & parser)3512   void parse(ParserT &parser) {
3513     bool has_message_id;
3514     bool has_random_id;
3515     bool has_sender;
3516     bool has_sender_name;
3517     bool has_arg;
3518     bool has_photo;
3519     bool has_document;
3520     bool has_sender_dialog_id;
3521     BEGIN_PARSE_FLAGS();
3522     PARSE_FLAG(contains_mention_);
3523     PARSE_FLAG(is_silent_);
3524     PARSE_FLAG(has_message_id);
3525     PARSE_FLAG(has_random_id);
3526     PARSE_FLAG(has_sender);
3527     PARSE_FLAG(has_sender_name);
3528     PARSE_FLAG(has_arg);
3529     PARSE_FLAG(has_photo);
3530     PARSE_FLAG(has_document);
3531     PARSE_FLAG(is_from_scheduled_);
3532     PARSE_FLAG(has_sender_dialog_id);
3533     END_PARSE_FLAGS();
3534     td::parse(dialog_id_, parser);
3535     if (has_message_id) {
3536       td::parse(message_id_, parser);
3537     }
3538     if (has_random_id) {
3539       td::parse(random_id_, parser);
3540     } else {
3541       random_id_ = 0;
3542     }
3543     if (has_sender) {
3544       td::parse(sender_user_id_, parser);
3545     }
3546     if (has_sender_name) {
3547       td::parse(sender_name_, parser);
3548     }
3549     td::parse(date_, parser);
3550     td::parse(loc_key_, parser);
3551     if (has_arg) {
3552       td::parse(arg_, parser);
3553     }
3554     if (has_photo) {
3555       td::parse(photo_, parser);
3556     }
3557     if (has_document) {
3558       td::parse(document_, parser);
3559     }
3560     td::parse(notification_id_, parser);
3561     if (has_sender_dialog_id) {
3562       td::parse(sender_dialog_id_, parser);
3563     }
3564   }
3565 };
3566 
add_message_push_notification(DialogId dialog_id,MessageId message_id,int64 random_id,UserId sender_user_id,DialogId sender_dialog_id,string sender_name,int32 date,bool is_from_scheduled,bool contains_mention,bool initial_is_silent,bool is_silent,string loc_key,string arg,Photo photo,Document document,NotificationId notification_id,uint64 log_event_id,Promise<Unit> promise)3567 void NotificationManager::add_message_push_notification(DialogId dialog_id, MessageId message_id, int64 random_id,
3568                                                         UserId sender_user_id, DialogId sender_dialog_id,
3569                                                         string sender_name, int32 date, bool is_from_scheduled,
3570                                                         bool contains_mention, bool initial_is_silent, bool is_silent,
3571                                                         string loc_key, string arg, Photo photo, Document document,
3572                                                         NotificationId notification_id, uint64 log_event_id,
3573                                                         Promise<Unit> promise) {
3574   auto is_pinned = begins_with(loc_key, "PINNED_");
3575   auto r_info = td_->messages_manager_->get_message_push_notification_info(
3576       dialog_id, message_id, random_id, sender_user_id, sender_dialog_id, date, is_from_scheduled, contains_mention,
3577       is_pinned, log_event_id != 0);
3578   if (r_info.is_error()) {
3579     VLOG(notifications) << "Don't need message push notification for " << message_id << "/" << random_id << " from "
3580                         << dialog_id << " sent by " << sender_user_id << "/" << sender_dialog_id << " at " << date
3581                         << ": " << r_info.error();
3582     if (log_event_id != 0) {
3583       binlog_erase(G()->td_db()->get_binlog(), log_event_id);
3584     }
3585     if (r_info.error().code() == 406) {
3586       promise.set_error(r_info.move_as_error());
3587     } else {
3588       promise.set_error(Status::Error(200, "Immediate success"));
3589     }
3590     return;
3591   }
3592 
3593   auto info = r_info.move_as_ok();
3594   CHECK(info.group_id.is_valid());
3595 
3596   if (dialog_id.get_type() == DialogType::SecretChat) {
3597     VLOG(notifications) << "Skip notification in secret " << dialog_id;
3598     // TODO support secret chat notifications
3599     // main problem: there is no message_id yet
3600     // also don't forget to delete newSecretChat notification
3601     CHECK(log_event_id == 0);
3602     return promise.set_error(Status::Error(406, "Secret chat push notifications are unsupported"));
3603   }
3604   CHECK(random_id == 0);
3605 
3606   if (is_disabled() || max_notification_group_count_ == 0) {
3607     CHECK(log_event_id == 0);
3608     return promise.set_error(Status::Error(200, "Immediate success"));
3609   }
3610 
3611   if (!notification_id.is_valid()) {
3612     CHECK(log_event_id == 0);
3613     notification_id = get_next_notification_id();
3614     if (!notification_id.is_valid()) {
3615       return promise.set_value(Unit());
3616     }
3617   } else {
3618     CHECK(log_event_id != 0);
3619   }
3620 
3621   if (sender_user_id.is_valid() && !td_->contacts_manager_->have_user_force(sender_user_id)) {
3622     int32 flags = USER_FLAG_IS_INACCESSIBLE;
3623     auto user_name = sender_user_id.get() == 136817688 ? "Channel" : sender_name;
3624     auto user = telegram_api::make_object<telegram_api::user>(
3625         flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
3626         false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
3627         false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
3628         sender_user_id.get(), 0, user_name, string(), string(), string(), nullptr, nullptr, 0, Auto(), string(),
3629         string());
3630     td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification");
3631   }
3632 
3633   if (log_event_id == 0 && G()->parameters().use_message_db) {
3634     AddMessagePushNotificationLogEvent log_event{
3635         dialog_id, message_id,        random_id,        sender_user_id,    sender_dialog_id, sender_name,
3636         date,      is_from_scheduled, contains_mention, initial_is_silent, loc_key,          arg,
3637         photo,     document,          notification_id};
3638     log_event_id = binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::AddMessagePushNotification,
3639                               get_log_event_storer(log_event));
3640   }
3641 
3642   auto group_id = info.group_id;
3643   CHECK(group_id.is_valid());
3644 
3645   bool is_outgoing =
3646       sender_user_id.is_valid() ? td_->contacts_manager_->get_my_id() == sender_user_id : is_from_scheduled;
3647   if (log_event_id != 0) {
3648     VLOG(notifications) << "Register temporary " << notification_id << " with log event " << log_event_id;
3649     temporary_notification_log_event_ids_[notification_id] = log_event_id;
3650     temporary_notifications_[FullMessageId(dialog_id, message_id)] = {group_id,         notification_id, sender_user_id,
3651                                                                       sender_dialog_id, sender_name,     is_outgoing};
3652     temporary_notification_message_ids_[notification_id] = FullMessageId(dialog_id, message_id);
3653   }
3654   push_notification_promises_[notification_id].push_back(std::move(promise));
3655 
3656   auto group_type = info.group_type;
3657   auto settings_dialog_id = info.settings_dialog_id;
3658   VLOG(notifications) << "Add message push " << notification_id << " of type " << loc_key << " for " << message_id
3659                       << "/" << random_id << " in " << dialog_id << ", sent by " << sender_user_id << "/"
3660                       << sender_dialog_id << "/\"" << sender_name << "\" at " << date << " with arg " << arg
3661                       << ", photo " << photo << " and document " << document << " to " << group_id << " of type "
3662                       << group_type << " with settings from " << settings_dialog_id;
3663 
3664   add_notification(
3665       group_id, group_type, dialog_id, date, settings_dialog_id, initial_is_silent, is_silent, 0, notification_id,
3666       create_new_push_message_notification(sender_user_id, sender_dialog_id, sender_name, is_outgoing, message_id,
3667                                            std::move(loc_key), std::move(arg), std::move(photo), std::move(document)),
3668       "add_message_push_notification");
3669 }
3670 
3671 class NotificationManager::EditMessagePushNotificationLogEvent {
3672  public:
3673   DialogId dialog_id_;
3674   MessageId message_id_;
3675   int32 edit_date_;
3676   string loc_key_;
3677   string arg_;
3678   Photo photo_;
3679   Document document_;
3680 
3681   template <class StorerT>
store(StorerT & storer) const3682   void store(StorerT &storer) const {
3683     bool has_message_id = message_id_.is_valid();
3684     bool has_arg = !arg_.empty();
3685     bool has_photo = !photo_.is_empty();
3686     bool has_document = !document_.empty();
3687     BEGIN_STORE_FLAGS();
3688     STORE_FLAG(has_message_id);
3689     STORE_FLAG(has_arg);
3690     STORE_FLAG(has_photo);
3691     STORE_FLAG(has_document);
3692     END_STORE_FLAGS();
3693     td::store(dialog_id_, storer);
3694     if (has_message_id) {
3695       td::store(message_id_, storer);
3696     }
3697     td::store(edit_date_, storer);
3698     td::store(loc_key_, storer);
3699     if (has_arg) {
3700       td::store(arg_, storer);
3701     }
3702     if (has_photo) {
3703       td::store(photo_, storer);
3704     }
3705     if (has_document) {
3706       td::store(document_, storer);
3707     }
3708   }
3709 
3710   template <class ParserT>
parse(ParserT & parser)3711   void parse(ParserT &parser) {
3712     bool has_message_id;
3713     bool has_arg;
3714     bool has_photo;
3715     bool has_document;
3716     BEGIN_PARSE_FLAGS();
3717     PARSE_FLAG(has_message_id);
3718     PARSE_FLAG(has_arg);
3719     PARSE_FLAG(has_photo);
3720     PARSE_FLAG(has_document);
3721     END_PARSE_FLAGS();
3722     td::parse(dialog_id_, parser);
3723     if (has_message_id) {
3724       td::parse(message_id_, parser);
3725     }
3726     td::parse(edit_date_, parser);
3727     td::parse(loc_key_, parser);
3728     if (has_arg) {
3729       td::parse(arg_, parser);
3730     }
3731     if (has_photo) {
3732       td::parse(photo_, parser);
3733     }
3734     if (has_document) {
3735       td::parse(document_, parser);
3736     }
3737   }
3738 };
3739 
edit_message_push_notification(DialogId dialog_id,MessageId message_id,int32 edit_date,string loc_key,string arg,Photo photo,Document document,uint64 log_event_id,Promise<Unit> promise)3740 void NotificationManager::edit_message_push_notification(DialogId dialog_id, MessageId message_id, int32 edit_date,
3741                                                          string loc_key, string arg, Photo photo, Document document,
3742                                                          uint64 log_event_id, Promise<Unit> promise) {
3743   if (is_disabled() || max_notification_group_count_ == 0) {
3744     CHECK(log_event_id == 0);
3745     return promise.set_error(Status::Error(200, "Immediate success"));
3746   }
3747 
3748   auto it = temporary_notifications_.find(FullMessageId(dialog_id, message_id));
3749   if (it == temporary_notifications_.end()) {
3750     VLOG(notifications) << "Ignore edit of message push notification for " << message_id << " in " << dialog_id
3751                         << " edited at " << edit_date;
3752     return promise.set_error(Status::Error(200, "Immediate success"));
3753   }
3754 
3755   auto group_id = it->second.group_id;
3756   auto notification_id = it->second.notification_id;
3757   auto sender_user_id = it->second.sender_user_id;
3758   auto sender_dialog_id = it->second.sender_dialog_id;
3759   auto sender_name = it->second.sender_name;
3760   auto is_outgoing = it->second.is_outgoing;
3761   CHECK(group_id.is_valid());
3762   CHECK(notification_id.is_valid());
3763 
3764   if (log_event_id == 0 && G()->parameters().use_message_db) {
3765     EditMessagePushNotificationLogEvent log_event{dialog_id, message_id, edit_date, loc_key, arg, photo, document};
3766     auto storer = get_log_event_storer(log_event);
3767     auto &cur_log_event_id = temporary_edit_notification_log_event_ids_[notification_id];
3768     if (cur_log_event_id == 0) {
3769       log_event_id = binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::EditMessagePushNotification, storer);
3770       cur_log_event_id = log_event_id;
3771       VLOG(notifications) << "Add edit message push notification log event " << log_event_id;
3772     } else {
3773       auto new_log_event_id = binlog_rewrite(G()->td_db()->get_binlog(), cur_log_event_id,
3774                                              LogEvent::HandlerType::EditMessagePushNotification, storer);
3775       VLOG(notifications) << "Rewrite edit message push notification log event " << cur_log_event_id << " with "
3776                           << new_log_event_id;
3777     }
3778   } else if (log_event_id != 0) {
3779     VLOG(notifications) << "Register edit of temporary " << notification_id << " with log event " << log_event_id;
3780     temporary_edit_notification_log_event_ids_[notification_id] = log_event_id;
3781   }
3782 
3783   push_notification_promises_[notification_id].push_back(std::move(promise));
3784 
3785   edit_notification(group_id, notification_id,
3786                     create_new_push_message_notification(sender_user_id, sender_dialog_id, std::move(sender_name),
3787                                                          is_outgoing, message_id, std::move(loc_key), std::move(arg),
3788                                                          std::move(photo), std::move(document)));
3789 }
3790 
get_push_receiver_id(string payload)3791 Result<int64> NotificationManager::get_push_receiver_id(string payload) {
3792   if (payload == "{}") {
3793     return static_cast<int64>(0);
3794   }
3795 
3796   auto r_json_value = json_decode(payload);
3797   if (r_json_value.is_error()) {
3798     return Status::Error(400, "Failed to parse payload as JSON object");
3799   }
3800 
3801   auto json_value = r_json_value.move_as_ok();
3802   if (json_value.type() != JsonValue::Type::Object) {
3803     return Status::Error(400, "Expected JSON object");
3804   }
3805 
3806   auto data = std::move(json_value.get_object());
3807   if (has_json_object_field(data, "data")) {
3808     auto r_data_data = get_json_object_field(data, "data", JsonValue::Type::Object, false);
3809     if (r_data_data.is_error()) {
3810       return Status::Error(400, r_data_data.error().message());
3811     }
3812     auto data_data = r_data_data.move_as_ok();
3813     data = std::move(data_data.get_object());
3814   }
3815 
3816   for (auto &field_value : data) {
3817     if (field_value.first == "p") {
3818       auto encrypted_payload = std::move(field_value.second);
3819       if (encrypted_payload.type() != JsonValue::Type::String) {
3820         return Status::Error(400, "Expected encrypted payload as a String");
3821       }
3822       Slice encrypted_data = encrypted_payload.get_string();
3823       if (encrypted_data.size() < 12) {
3824         return Status::Error(400, "Encrypted payload is too small");
3825       }
3826       auto r_decoded = base64url_decode(encrypted_data.substr(0, 12));
3827       if (r_decoded.is_error()) {
3828         return Status::Error(400, "Failed to base64url-decode payload");
3829       }
3830       CHECK(r_decoded.ok().size() == 9);
3831       return as<int64>(r_decoded.ok().c_str());
3832     }
3833     if (field_value.first == "user_id") {
3834       auto user_id = std::move(field_value.second);
3835       if (user_id.type() != JsonValue::Type::String && user_id.type() != JsonValue::Type::Number) {
3836         return Status::Error(400, "Expected user_id as a String or a Number");
3837       }
3838       Slice user_id_str = user_id.type() == JsonValue::Type::String ? user_id.get_string() : user_id.get_number();
3839       auto r_user_id = to_integer_safe<int64>(user_id_str);
3840       if (r_user_id.is_error()) {
3841         return Status::Error(400, PSLICE() << "Failed to get user_id from " << user_id_str);
3842       }
3843       if (r_user_id.ok() <= 0) {
3844         return Status::Error(400, PSLICE() << "Receive wrong user_id " << user_id_str);
3845       }
3846       return r_user_id.ok();
3847     }
3848   }
3849 
3850   return static_cast<int64>(0);
3851 }
3852 
decrypt_push(int64 encryption_key_id,string encryption_key,string push)3853 Result<string> NotificationManager::decrypt_push(int64 encryption_key_id, string encryption_key, string push) {
3854   auto r_json_value = json_decode(push);
3855   if (r_json_value.is_error()) {
3856     return Status::Error(400, "Failed to parse payload as JSON object");
3857   }
3858 
3859   auto json_value = r_json_value.move_as_ok();
3860   if (json_value.type() != JsonValue::Type::Object) {
3861     return Status::Error(400, "Expected JSON object");
3862   }
3863 
3864   for (auto &field_value : json_value.get_object()) {
3865     if (field_value.first == "p") {
3866       auto encrypted_payload = std::move(field_value.second);
3867       if (encrypted_payload.type() != JsonValue::Type::String) {
3868         return Status::Error(400, "Expected encrypted payload as a String");
3869       }
3870       Slice encrypted_data = encrypted_payload.get_string();
3871       if (encrypted_data.size() < 12) {
3872         return Status::Error(400, "Encrypted payload is too small");
3873       }
3874       auto r_decoded = base64url_decode(encrypted_data);
3875       if (r_decoded.is_error()) {
3876         return Status::Error(400, "Failed to base64url-decode payload");
3877       }
3878       return decrypt_push_payload(encryption_key_id, std::move(encryption_key), r_decoded.move_as_ok());
3879     }
3880   }
3881   return Status::Error(400, "No 'p'(payload) field found in push");
3882 }
3883 
decrypt_push_payload(int64 encryption_key_id,string encryption_key,string payload)3884 Result<string> NotificationManager::decrypt_push_payload(int64 encryption_key_id, string encryption_key,
3885                                                          string payload) {
3886   mtproto::AuthKey auth_key(encryption_key_id, std::move(encryption_key));
3887   mtproto::PacketInfo packet_info;
3888   packet_info.version = 2;
3889   packet_info.type = mtproto::PacketInfo::EndToEnd;
3890   packet_info.is_creator = true;
3891   packet_info.check_mod4 = false;
3892 
3893   TRY_RESULT(result, mtproto::Transport::read(payload, auth_key, &packet_info));
3894   if (result.type() != mtproto::Transport::ReadResult::Packet) {
3895     return Status::Error(400, "Wrong packet type");
3896   }
3897   if (result.packet().size() < 4) {
3898     return Status::Error(400, "Packet is too small");
3899   }
3900   return result.packet().substr(4).str();
3901 }
3902 
before_get_difference()3903 void NotificationManager::before_get_difference() {
3904   if (is_disabled()) {
3905     return;
3906   }
3907   if (running_get_difference_) {
3908     return;
3909   }
3910 
3911   running_get_difference_ = true;
3912   on_unreceived_notification_update_count_changed(1, 0, "before_get_difference");
3913 }
3914 
after_get_difference()3915 void NotificationManager::after_get_difference() {
3916   if (is_disabled()) {
3917     return;
3918   }
3919 
3920   CHECK(running_get_difference_);
3921   running_get_difference_ = false;
3922   on_unreceived_notification_update_count_changed(-1, 0, "after_get_difference");
3923   flush_pending_notifications_timeout_.set_timeout_in(0, MIN_NOTIFICATION_DELAY_MS * 1e-3);
3924 }
3925 
after_get_difference_impl()3926 void NotificationManager::after_get_difference_impl() {
3927   if (running_get_difference_) {
3928     return;
3929   }
3930 
3931   VLOG(notifications) << "After get difference";
3932 
3933   vector<NotificationGroupId> to_remove_temporary_notifications_group_ids;
3934   for (auto &group_it : groups_) {
3935     const auto &group_key = group_it.first;
3936     const auto &group = group_it.second;
3937     if (running_get_chat_difference_.count(group_key.group_id.get()) == 0 &&
3938         get_temporary_notification_total_count(group) > 0) {
3939       to_remove_temporary_notifications_group_ids.push_back(group_key.group_id);
3940     }
3941   }
3942   for (auto group_id : reversed(to_remove_temporary_notifications_group_ids)) {
3943     remove_temporary_notifications(group_id, "after_get_difference");
3944   }
3945 
3946   flush_all_pending_updates(false, "after_get_difference");
3947 }
3948 
before_get_chat_difference(NotificationGroupId group_id)3949 void NotificationManager::before_get_chat_difference(NotificationGroupId group_id) {
3950   if (is_disabled()) {
3951     return;
3952   }
3953 
3954   VLOG(notifications) << "Before get chat difference in " << group_id;
3955   CHECK(group_id.is_valid());
3956   if (running_get_chat_difference_.insert(group_id.get()).second) {
3957     on_unreceived_notification_update_count_changed(1, group_id.get(), "before_get_chat_difference");
3958   }
3959 }
3960 
after_get_chat_difference(NotificationGroupId group_id)3961 void NotificationManager::after_get_chat_difference(NotificationGroupId group_id) {
3962   if (is_disabled()) {
3963     return;
3964   }
3965 
3966   VLOG(notifications) << "After get chat difference in " << group_id;
3967   CHECK(group_id.is_valid());
3968   auto erased_count = running_get_chat_difference_.erase(group_id.get());
3969   if (erased_count == 1) {
3970     flush_pending_notifications_timeout_.set_timeout_in(-group_id.get(), MIN_NOTIFICATION_DELAY_MS * 1e-3);
3971     on_unreceived_notification_update_count_changed(-1, group_id.get(), "after_get_chat_difference");
3972   }
3973 }
3974 
after_get_chat_difference_impl(NotificationGroupId group_id)3975 void NotificationManager::after_get_chat_difference_impl(NotificationGroupId group_id) {
3976   if (running_get_chat_difference_.count(group_id.get()) == 1) {
3977     return;
3978   }
3979 
3980   VLOG(notifications) << "Flush updates after get chat difference in " << group_id;
3981   CHECK(group_id.is_valid());
3982   if (!running_get_difference_ && pending_updates_.count(group_id.get()) == 1) {
3983     remove_temporary_notifications(group_id, "after_get_chat_difference");
3984     force_flush_pending_updates(group_id, "after_get_chat_difference");
3985   }
3986 }
3987 
get_current_state(vector<td_api::object_ptr<td_api::Update>> & updates) const3988 void NotificationManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
3989   if (is_disabled() || max_notification_group_count_ == 0 || is_destroyed_) {
3990     return;
3991   }
3992 
3993   updates.push_back(get_update_active_notifications());
3994   updates.push_back(get_update_have_pending_notifications());
3995 }
3996 
flush_all_notifications()3997 void NotificationManager::flush_all_notifications() {
3998   flush_all_pending_notifications();
3999   flush_all_pending_updates(true, "flush_all_notifications");
4000 }
4001 
destroy_all_notifications()4002 void NotificationManager::destroy_all_notifications() {
4003   if (is_destroyed_) {
4004     return;
4005   }
4006   is_being_destroyed_ = true;
4007 
4008   size_t cur_pos = 0;
4009   for (auto it = groups_.begin(); it != groups_.end() && cur_pos < max_notification_group_count_; ++it, cur_pos++) {
4010     auto &group_key = it->first;
4011     auto &group = it->second;
4012 
4013     if (group_key.last_notification_date == 0) {
4014       break;
4015     }
4016 
4017     VLOG(notifications) << "Destroy " << group_key.group_id;
4018     send_remove_group_update(group_key, group, vector<int32>());
4019   }
4020 
4021   flush_all_pending_updates(true, "destroy_all_notifications");
4022   if (delayed_notification_update_count_ != 0) {
4023     on_delayed_notification_update_count_changed(-delayed_notification_update_count_, 0, "destroy_all_notifications");
4024   }
4025   if (unreceived_notification_update_count_ != 0) {
4026     on_unreceived_notification_update_count_changed(-unreceived_notification_update_count_, 0,
4027                                                     "destroy_all_notifications");
4028   }
4029 
4030   while (!push_notification_promises_.empty()) {
4031     on_notification_processed(push_notification_promises_.begin()->first);
4032   }
4033 
4034   is_destroyed_ = true;
4035 }
4036 
get_update_have_pending_notifications() const4037 td_api::object_ptr<td_api::updateHavePendingNotifications> NotificationManager::get_update_have_pending_notifications()
4038     const {
4039   return td_api::make_object<td_api::updateHavePendingNotifications>(delayed_notification_update_count_ != 0,
4040                                                                      unreceived_notification_update_count_ != 0);
4041 }
4042 
send_update_have_pending_notifications() const4043 void NotificationManager::send_update_have_pending_notifications() const {
4044   if (is_destroyed_ || !is_inited_ || !is_binlog_processed_) {
4045     return;
4046   }
4047 
4048   auto update = get_update_have_pending_notifications();
4049   VLOG(notifications) << "Send " << oneline(to_string(update));
4050   send_closure(G()->td(), &Td::send_update, std::move(update));
4051 }
4052 
on_delayed_notification_update_count_changed(int32 diff,int32 notification_group_id,const char * source)4053 void NotificationManager::on_delayed_notification_update_count_changed(int32 diff, int32 notification_group_id,
4054                                                                        const char *source) {
4055   bool had_delayed = delayed_notification_update_count_ != 0;
4056   delayed_notification_update_count_ += diff;
4057   CHECK(delayed_notification_update_count_ >= 0);
4058   VLOG(notifications) << "Update delayed notification count with diff " << diff << " to "
4059                       << delayed_notification_update_count_ << " from group " << notification_group_id << " and "
4060                       << source;
4061   bool have_delayed = delayed_notification_update_count_ != 0;
4062   if (had_delayed != have_delayed) {
4063     send_update_have_pending_notifications();
4064   }
4065 }
4066 
on_unreceived_notification_update_count_changed(int32 diff,int32 notification_group_id,const char * source)4067 void NotificationManager::on_unreceived_notification_update_count_changed(int32 diff, int32 notification_group_id,
4068                                                                           const char *source) {
4069   bool had_unreceived = unreceived_notification_update_count_ != 0;
4070   unreceived_notification_update_count_ += diff;
4071   CHECK(unreceived_notification_update_count_ >= 0);
4072   VLOG(notifications) << "Update unreceived notification count with diff " << diff << " to "
4073                       << unreceived_notification_update_count_ << " from group " << notification_group_id << " and "
4074                       << source;
4075   bool have_unreceived = unreceived_notification_update_count_ != 0;
4076   if (had_unreceived != have_unreceived) {
4077     send_update_have_pending_notifications();
4078   }
4079 }
4080 
try_send_update_active_notifications()4081 void NotificationManager::try_send_update_active_notifications() {
4082   if (max_notification_group_count_ == 0) {
4083     return;
4084   }
4085   if (!is_binlog_processed_ || !is_inited_) {
4086     return;
4087   }
4088 
4089   auto update = get_update_active_notifications();
4090   VLOG(notifications) << "Send " << as_active_notifications_update(update.get());
4091   send_closure(G()->td(), &Td::send_update, std::move(update));
4092 
4093   while (!push_notification_promises_.empty()) {
4094     on_notification_processed(push_notification_promises_.begin()->first);
4095   }
4096 }
4097 
on_binlog_events(vector<BinlogEvent> && events)4098 void NotificationManager::on_binlog_events(vector<BinlogEvent> &&events) {
4099   VLOG(notifications) << "Begin to process " << events.size() << " binlog events";
4100   for (auto &event : events) {
4101     if (!G()->parameters().use_message_db || is_disabled() || max_notification_group_count_ == 0) {
4102       binlog_erase(G()->td_db()->get_binlog(), event.id_);
4103       break;
4104     }
4105 
4106     switch (event.type_) {
4107       case LogEvent::HandlerType::AddMessagePushNotification: {
4108         CHECK(is_inited_);
4109         AddMessagePushNotificationLogEvent log_event;
4110         log_event_parse(log_event, event.data_).ensure();
4111 
4112         add_message_push_notification(
4113             log_event.dialog_id_, log_event.message_id_, log_event.random_id_, log_event.sender_user_id_,
4114             log_event.sender_dialog_id_, log_event.sender_name_, log_event.date_, log_event.is_from_scheduled_,
4115             log_event.contains_mention_, log_event.is_silent_, true, log_event.loc_key_, log_event.arg_,
4116             log_event.photo_, log_event.document_, log_event.notification_id_, event.id_,
4117             PromiseCreator::lambda([](Result<Unit> result) {
4118               if (result.is_error() && result.error().code() != 200 && result.error().code() != 406) {
4119                 LOG(ERROR) << "Receive error " << result.error() << ", while processing message push notification";
4120               }
4121             }));
4122         break;
4123       }
4124       case LogEvent::HandlerType::EditMessagePushNotification: {
4125         CHECK(is_inited_);
4126         EditMessagePushNotificationLogEvent log_event;
4127         log_event_parse(log_event, event.data_).ensure();
4128 
4129         edit_message_push_notification(
4130             log_event.dialog_id_, log_event.message_id_, log_event.edit_date_, log_event.loc_key_, log_event.arg_,
4131             log_event.photo_, log_event.document_, event.id_, PromiseCreator::lambda([](Result<Unit> result) {
4132               if (result.is_error() && result.error().code() != 200 && result.error().code() != 406) {
4133                 LOG(ERROR) << "Receive error " << result.error() << ", while processing edit message push notification";
4134               }
4135             }));
4136         break;
4137       }
4138       default:
4139         LOG(FATAL) << "Unsupported log event type " << event.type_;
4140     }
4141   }
4142   if (is_inited_) {
4143     flush_all_pending_notifications();
4144   }
4145   is_binlog_processed_ = true;
4146   try_send_update_active_notifications();
4147   VLOG(notifications) << "Finish processing binlog events";
4148 }
4149 
4150 }  // namespace td
4151