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 ¬ification : 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 ¬ification : 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 ¬ification : 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 ¬ification : 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 ¬ification : 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 ¬ification) {
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 ¬ification : 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 ¬ification : 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 ¬ification,
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 ¬ification : 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 ¬ification) {
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 ¬ification : 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 ¬ification_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 ¬ification_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 ¬ification : 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 ¬ification) { 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 ¬ification_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 ¬ification_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 ¬ification : 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>> ¬ifications,
1275 const vector<int32> ¬ification_ids) {
1276 for (auto ¬ification : 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 ¬ification : 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 ¬ification = 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 ¬ification : 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 ¬ification : 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> ¬ification)> &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 ¬ification_id) {
1786 return removed_notification_ids.count(notification_id) == 1;
1787 });
1788 }
1789 for (auto ¬ification : 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 ¬ification) { 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> ¬ification) {
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 ¬ifications) {
1925 for (auto ¬ification : 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 ¬ification = 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> ¬ification) {
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> ¬ification) {
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> ¬ification) {
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 ¬ification : 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 ¬ification : 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 ¬ification : 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