From a86a9d2a0044985ecf0ac835321e132b017b7c31 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 15 Nov 2018 18:58:33 +0300 Subject: [PATCH] Add support for updateNotificationGroup and delaying of updates. GitOrigin-RevId: 28974259dcaf97d2ddc7b303c7609bd99b846089 --- td/generate/scheme/td_api.tl | 13 +- td/generate/scheme/td_api.tlo | Bin 137408 -> 137492 bytes td/telegram/ContactsManager.cpp | 14 ++ td/telegram/ContactsManager.h | 9 + td/telegram/MessagesManager.cpp | 2 +- td/telegram/NotificationManager.cpp | 349 +++++++++++++++++++++++++++- td/telegram/NotificationManager.h | 97 +++++++- td/telegram/NotificationType.cpp | 19 ++ td/telegram/NotificationType.h | 3 + td/telegram/Td.cpp | 12 +- td/telegram/td_emscripten.cpp | 3 - 11 files changed, 493 insertions(+), 28 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index ecaf21f70..638fe5dec 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2216,12 +2216,15 @@ updateChatReplyMarkup chat_id:int53 reply_markup_message_id:int53 = Update; //@description A chat draft has changed. Be aware that the update may come in the currently opened chat but with old content of the draft. If the user has changed the content of the draft, this update shouldn't be applied @chat_id Chat identifier @draft_message The new draft message; may be null @order New value of the chat order updateChatDraftMessage chat_id:int53 draft_message:draftMessage order:int64 = Update; -//@description A list of active notifications in a notification group has changed @notification_group_id Unique notification group identifier @chat_id Identifier of a chat to which all notifications in the group belong -//@notification_settings_chat_id Chat identifier, which notification settings must be applied @silent True, if the notifications should be shown without sound -//@total_count Total number of active notifications in the group @new_notifications List of new group notifications @edited_notifications List of edited group notifications @removed_notification_ids Identifiers of removed group notifications -updateNotificationGroup notification_group_id:int32 chat_id:int53 notification_settings_chat_id:int53 silent:Bool total_count:int32 new_notifications:vector edited_notifications:vector removed_notification_ids:vector = Update; +//@description A notification was changed @notification_group_id Unique notification group identifier @notification Changed notification +updateNotification notification_group_id:int32 notification:notification = Update; -//@description Contains active notifications that was shown on previous application launches. This update is sent only if a message database is used. In that case it comes once before any updateNotificationGroup update @groups Lists of active notification groups +//@description A list of active notifications in a notification group has changed @notification_group_id Unique notification group identifier @chat_id Identifier of a chat to which all notifications in the group belong +//@notification_settings_chat_id Chat identifier, which notification settings must be applied @is_silent True, if the notifications should be shown without sound +//@total_count Total number of active notifications in the group @new_notifications List of new group notifications @removed_notification_ids Identifiers of removed group notifications +updateNotificationGroup notification_group_id:int32 chat_id:int53 notification_settings_chat_id:int53 is_silent:Bool total_count:int32 new_notifications:vector removed_notification_ids:vector = Update; + +//@description Contains active notifications that was shown on previous application launches. This update is sent only if a message database is used. In that case it comes once before any updateNotification and updateNotificationGroup update @groups Lists of active notification groups updateActiveNotifications groups:vector = Update; //@description Some messages were deleted @chat_id Chat identifier @message_ids Identifiers of the deleted messages diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index f3805abcee7c6e0726571cf78de0e34b80052cca..cec55473ec3e2cc5c3705b3d7859e219f3ffedcf 100644 GIT binary patch delta 1708 zcmZWqUr1A76!)yD%NAY5n2^~hi3?qEk`F>FbenV2HLp7LI;U>Bse`3MCums+K?q4K z?(pkj#vTG+d{}>A1`-MqJqQh2VBmxG5ClCa_@E@zw(p+nsy&_ke&_tozwd18FIACO zs`$3{?9z4NQ$c6Y>k0Yt>cxG~ygt?jPQm0ue`Ug736IN_84q$;4|;0VH9%$762sqg z6pA!}WQ9}UpqSPG9JP1@2-I3J0DC$k_vL+wO)0`1jmQ|)i$<8G{G1V3DP|dXax8yk zO;S$6n;=GA4ok}`vqT8CzAf@?yIZvvjOFLJRe zacTboVJx7cWVr?F)ChW zl^3^euq0~1_|g`rqi%c)j8bc@1=*}RV*@uOLN-*2*}zLN)otuM-7S|hn%G~&q8&$8 zXUBkm9fgy26wZpUt?2A-MS_;{3o6M@8mq}XgFNZ;xuVFGdhq>8$yD|o+Q&owVgHaP z%*wFd*f?8LK-HtL;QA zAY&03+mf+#GLC4wupM5u=DP3={XD^*K`r6CQF*=_IQfVb;#XEf){B4kxe zjj08!ofEKjw}1)50>r7GsV7GbNY!t+CTg3dyNdUqNU{e-luop?XB5(LE;vzm&58B1 zy}7?NWWx!)>G>;M3$6TnCbU{!mu@A9OAKqHL54XG1hkCYt#HRc4 zoHu1`uOFXL{Q&9(2OvswFm8<1yRpBZj78npmC7UgdDFjR!XQ}4mIv2=;K>Wqt&<8V-l6?bRKh_(V4VjF^0T7b%nY4u+CAYncXU<@pA`CyLXyFRIb1Tzd}KtXN< qD-%XgY5_5kpDiQF5&UmpN2OULrbpq;av2_C=9Y60IbmWHv#LMN#b9^< delta 1686 zcmZ8hPe>GD6!)3RBnE>CuC&yEdst|#Erb?QGI!lZU3E17Y`VIu&Xx;XNNtB`4@MC| z9hSbu=Rsi)f*tmt85q&QLdt`Jhl&Ct?c||@Mu!dyF3h& zf;9hGIkNuq)J{pyxIUbS7F=L^P`Q{>;)meo{g`7Z=77icA|nESH$WY?JQu-{w^-`C zn?vb~U|QNGaN%wfIJrY<1Q)loMhJ2%+X&9XM$Rwq$^%6Vu2(YITQ=#1B==8vP4=X% z(1jaw;J_R~==7PzQS(8D6MhkPK!`U{Sh`6l_*a_UZ#oWG(|)MsDL?o@;nr_IwdT4+ z*nX25_s&|e8aA_c3}qE0?zF4cMiMFnIKHA<64l_03f0_E*KDfJOG-6^%JI$Se|l&r zFEpneZ?RNhYv-Lfty1F^t>EEwtrf<&^}5wsKn_5JRclr*2f*+IjNbXZj5%MJ)s80S!6ejksQhFS)lkI-x2tis6B?w7s2)p1^)S1)eU!aFQ^m5|Ww_Nz`>O4t zN$OqTMa6N63aWw{InCYeq$aaJI zeAc?n0ZsN0+u0)qBCtmSTNc=kz?5F%8uc*IOU=v*E0Ts)hLm!2V_t6n-3e3F% zc3dt6naOvHu|5bV_(UQ>l06Zmy}k&7ODv-Z@BINcR$YO+oH2F9;swKdk|`~cy`BHrc!%Thg8Z+=(N9H!!g|tA3)t$X7UThHAvr)Rdo}rsTq;^cO$j&0+um diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 0f90c6b16..ec7abdde9 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3110,6 +3110,20 @@ void ContactsManager::set_my_online_status(bool is_online, bool send_update, boo } } +ContactsManager::MyOnlineStatusInfo ContactsManager::get_my_online_status() const { + auto my_id = get_my_id(); + const User *u = get_user(my_id); + int32 was_online = my_was_online_local_ != 0 ? my_was_online_local_ : (u == nullptr ? 0 : u->was_online); + + MyOnlineStatusInfo status_info; + status_info.is_online_local = td_->is_online(); + status_info.is_online_remote = false; // TODO + status_info.was_online_local = was_online; // TODO + status_info.was_online_remote = was_online; // TODO + + return status_info; +} + UserId ContactsManager::get_service_notifications_user_id() { UserId user_id(777000); if (!have_user_force(user_id)) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 80929f957..afbd42740 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -213,6 +213,15 @@ class ContactsManager : public Actor { void set_my_online_status(bool is_online, bool send_update, bool is_local); + struct MyOnlineStatusInfo { + bool is_online_local = false; + bool is_online_remote = false; + int32 was_online_local = 0; + int32 was_online_remote = 0; + }; + + MyOnlineStatusInfo get_my_online_status() const; + UserId get_service_notifications_user_id(); void on_update_online_status_privacy(); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index fd60762bd..f24822982 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -17462,7 +17462,7 @@ void MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool f set_dialog_last_notification_date(d, m->date); VLOG(notifications) << "Assign " << m->notification_id << " to " << m->message_id << " in " << d->dialog_id; send_closure_later(G()->notification_manager(), &NotificationManager::add_notification, - get_dialog_message_notification_group_id(d), d->dialog_id, settings_dialog_id, + get_dialog_message_notification_group_id(d), d->dialog_id, m->date, settings_dialog_id, m->disable_notification, m->notification_id, create_new_message_notification(m->message_id)); } diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index e7dd38b7f..00a517517 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -8,19 +8,35 @@ #include "td/telegram/AuthManager.h" #include "td/telegram/ConfigShared.h" +#include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" +#include "td/utils/misc.h" + +#include + namespace td { int VERBOSITY_NAME(notifications) = VERBOSITY_NAME(WARNING); NotificationManager::NotificationManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { + flush_pending_notifications_timeout_.set_callback(on_flush_pending_notifications_timeout_callback); + flush_pending_notifications_timeout_.set_callback_data(static_cast(this)); +} + +void NotificationManager::on_flush_pending_notifications_timeout_callback(void *notification_manager_ptr, + int64 group_id_int) { + if (G()->close_flag()) { + return; + } + + auto notification_manager = static_cast(notification_manager_ptr); + notification_manager->flush_pending_notifications(NotificationGroupId(narrow_cast(group_id_int))); } bool NotificationManager::is_disabled() const { - LOG(ERROR) << "IS DISABLED"; return td_->auth_manager_->is_bot(); } @@ -34,10 +50,15 @@ void NotificationManager::start_up() { current_notification_group_id_ = NotificationGroupId(to_integer(G()->td_db()->get_binlog_pmc()->get("notification_group_id_current"))); - max_notification_group_count_ = - G()->shared_config().get_option_integer("notification_group_count_max", DEFAULT_NOTIFICATION_GROUP_COUNT_MAX); - max_notification_group_size_ = - G()->shared_config().get_option_integer("notification_group_size_max", DEFAULT_NOTIFICATION_GROUP_SIZE_MAX); + on_notification_group_count_max_changed(); + on_notification_group_size_max_changed(); + + online_cloud_timeout_ms_ = + G()->shared_config().get_option_integer("online_cloud_timeout_ms", DEFAULT_ONLINE_CLOUD_TIMEOUT_MS); + notification_cloud_delay_ms_ = + G()->shared_config().get_option_integer("notification_cloud_delay_ms", DEFAULT_ONLINE_CLOUD_DELAY_MS); + notification_default_delay_ms_ = + G()->shared_config().get_option_integer("notification_default_delay_ms", DEFAULT_DEFAULT_DELAY_MS); // TODO load groups } @@ -46,6 +67,16 @@ void NotificationManager::tear_down() { parent_.reset(); } +NotificationManager::NotificationGroups::iterator NotificationManager::get_group(NotificationGroupId group_id) { + // TODO optimize + for (auto it = groups_.begin(); it != groups_.end(); ++it) { + if (it->first.group_id == group_id) { + return it; + } + } + return groups_.end(); +} + NotificationId NotificationManager::get_next_notification_id() { if (is_disabled()) { return NotificationId(); @@ -66,18 +97,270 @@ NotificationGroupId NotificationManager::get_next_notification_group_id() { return current_notification_group_id_; } -void NotificationManager::add_notification(NotificationGroupId group_id, DialogId dialog_id, - DialogId notification_settings_dialog_id, bool silent, +NotificationManager::NotificationGroupKey NotificationManager::get_last_updated_group_key() const { + int left = max_notification_group_count_; + auto it = groups_.begin(); + while (it != groups_.end() && left > 1) { + ++it; + left--; + } + if (it == groups_.end()) { + return NotificationGroupKey(); + } + return it->first; +} + +int32 NotificationManager::get_notification_delay_ms(DialogId dialog_id, + const PendingNotification ¬ification) const { + auto delay_ms = [&]() { + if (dialog_id.get_type() == DialogType::SecretChat) { + return 0; // there is no reason to delay notifications in secret chats + } + + auto online_info = td_->contacts_manager_->get_my_online_status(); + if (!online_info.is_online_local && online_info.is_online_remote) { + // If we are offline, but online from some other client then delay notification + // for 'notification_cloud_delay' seconds. + return notification_cloud_delay_ms_; + } + + if (!online_info.is_online_local && + online_info.was_online_remote > max(static_cast(online_info.was_online_local), + G()->server_time_cached() - online_cloud_timeout_ms_ * 1e-3)) { + // If we are offline, but was online from some other client in last 'online_cloud_timeout' seconds + // after we had gone offline, then delay notification for 'notification_cloud_delay' seconds. + return notification_cloud_delay_ms_; + } + + if (online_info.is_online_remote) { + // If some other client is online, then delay notification for 'notification_default_delay' seconds. + return notification_default_delay_ms_; + } + + // otherwise send update without additional delay + return 0; + }(); + + auto passed_time_ms = max(0, static_cast((G()->server_time_cached() - notification.date - 1) * 1000)); + return max(delay_ms - passed_time_ms, MIN_NOTIFICATION_DELAY_MS); +} + +void NotificationManager::add_notification(NotificationGroupId group_id, DialogId dialog_id, int32 date, + DialogId notification_settings_dialog_id, bool is_silent, NotificationId notification_id, unique_ptr type) { if (is_disabled()) { return; } + CHECK(group_id.is_valid()); + CHECK(dialog_id.is_valid()); + CHECK(notification_settings_dialog_id.is_valid()); + CHECK(notification_id.is_valid()); CHECK(type != nullptr); VLOG(notifications) << "Add " << notification_id << " to " << group_id << " in " << dialog_id << " with settings from " << notification_settings_dialog_id - << (silent ? " silent" : " with sound") << ": " << *type; - // TODO total_count++; + << (is_silent ? " silent" : " with sound") << ": " << *type; + + auto group_it = get_group(group_id); + if (group_it == groups_.end()) { + NotificationGroupKey group_key; + group_key.group_id = group_id; + group_key.dialog_id = dialog_id; + group_key.last_notification_date = 0; + group_it = std::move(groups_.emplace(group_key, NotificationGroup()).first); + } + + PendingNotification notification; + notification.date = date; + notification.settings_dialog_id = notification_settings_dialog_id; + notification.is_silent = is_silent; + notification.notification_id = notification_id; + notification.type = std::move(type); + + auto delay_ms = get_notification_delay_ms(dialog_id, notification); + VLOG(notifications) << "Delay " << notification_id << " for " << delay_ms << " milliseconds"; + auto flush_time = delay_ms * 0.001 + Time::now_cached(); + + NotificationGroup &group = group_it->second; + if (group.pending_notifications_flush_time == 0 || flush_time < group.pending_notifications_flush_time) { + group.pending_notifications_flush_time = flush_time; + flush_pending_notifications_timeout_.set_timeout_at(group_id.get(), group.pending_notifications_flush_time); + } + group.pending_notifications.push_back(std::move(notification)); +} + +td_api::object_ptr NotificationManager::get_notification_object( + DialogId dialog_id, const Notification ¬ification) { + return td_api::make_object(notification.notification_id.get(), + notification.type->get_notification_type_object(dialog_id)); +} + +void NotificationManager::send_update_notification_group(td_api::object_ptr update) { + // TODO delay and combine updates while getDifference is running + VLOG(notifications) << "Send " << to_string(update); + send_closure(G()->td(), &Td::send_update, std::move(update)); +} + +void NotificationManager::flush_pending_notifications(NotificationGroupKey &group_key, NotificationGroup &group, + vector &pending_notifications) { + if (pending_notifications.empty()) { + return; + } + + VLOG(notifications) << "Flush " << pending_notifications.size() << " pending notifications in " << group_key + << " with available " << group.notifications.size() << " from " << group.total_count + << " notifications"; + + size_t old_notification_count = group.notifications.size(); + size_t shown_notification_count = min(old_notification_count, max_notification_group_size_); + + vector> added_notifications; + added_notifications.reserve(pending_notifications.size()); + for (auto &pending_notification : pending_notifications) { + Notification notification{pending_notification.notification_id, std::move(pending_notification.type)}; + added_notifications.push_back(get_notification_object(group_key.dialog_id, notification)); + if (added_notifications.back()->type_ == nullptr) { + added_notifications.pop_back(); + } else { + group.notifications.push_back(std::move(notification)); + } + } + if (added_notifications.size() > max_notification_group_size_) { + added_notifications.erase( + added_notifications.begin(), + added_notifications.begin() + (added_notifications.size() - max_notification_group_size_)); + } + + vector removed_notification_ids; + if (shown_notification_count + added_notifications.size() > max_notification_group_size_) { + auto removed_notification_count = + shown_notification_count + added_notifications.size() - max_notification_group_size_; + removed_notification_ids.reserve(removed_notification_count); + for (size_t i = 0; i < removed_notification_count; i++) { + removed_notification_ids.push_back( + group.notifications[old_notification_count - shown_notification_count + i].notification_id.get()); + } + } + + group.total_count += narrow_cast(added_notifications.size()); + if (!added_notifications.empty()) { + send_update_notification_group(td_api::make_object( + group_key.group_id.get(), group_key.dialog_id.get(), pending_notifications[0].settings_dialog_id.get(), + pending_notifications[0].is_silent, group.total_count, std::move(added_notifications), + std::move(removed_notification_ids))); + } else { + CHECK(removed_notification_ids.empty()); + } + pending_notifications.clear(); +} + +void NotificationManager::send_remove_group_update(NotificationGroupId group_id) { + CHECK(group_id.is_valid()); + auto group_it = get_group(group_id); + CHECK(group_it != groups_.end()); + + auto total_size = group_it->second.notifications.size(); + auto removed_size = min(total_size, max_notification_group_size_); + vector removed_notification_ids; + removed_notification_ids.reserve(removed_size); + for (size_t i = total_size - removed_size; i < total_size; i++) { + removed_notification_ids.push_back(group_it->second.notifications[i].notification_id.get()); + } + + if (!removed_notification_ids.empty()) { + send_update_notification_group(td_api::make_object( + group_id.get(), group_it->first.dialog_id.get(), group_it->first.dialog_id.get(), true, 0, + vector>(), std::move(removed_notification_ids))); + } +} + +void NotificationManager::send_add_group_update(const NotificationGroupKey &group_key, const NotificationGroup &group) { + auto total_size = group.notifications.size(); + auto added_size = min(total_size, max_notification_group_size_); + vector> added_notifications; + added_notifications.reserve(added_size); + for (size_t i = total_size - added_size; i < total_size; i++) { + added_notifications.push_back(get_notification_object(group_key.dialog_id, group.notifications[i])); + if (added_notifications.back()->type_ == nullptr) { + added_notifications.pop_back(); + } + } + + if (!added_notifications.empty()) { + send_update_notification_group( + td_api::make_object(group_key.group_id.get(), group_key.dialog_id.get(), 0, + true, 0, std::move(added_notifications), vector())); + } +} + +void NotificationManager::flush_pending_notifications(NotificationGroupId group_id) { + auto group_it = get_group(group_id); + CHECK(group_it != groups_.end()); + auto group_key = group_it->first; + auto group = std::move(group_it->second); + + groups_.erase(group_it); + + CHECK(!group.pending_notifications.empty()); + auto final_group_key = group_key; + for (auto &pending_notification : group.pending_notifications) { + if (pending_notification.date >= final_group_key.last_notification_date) { + final_group_key.last_notification_date = pending_notification.date; + } + } + CHECK(final_group_key.last_notification_date != 0); + + VLOG(notifications) << "Flush pending notifications in " << group_key << " up to " + << final_group_key.last_notification_date; + + auto last_group_key = get_last_updated_group_key(); + bool was_updated = group_key.last_notification_date != 0 && group_key < last_group_key; + bool is_updated = final_group_key < last_group_key; + + if (!is_updated) { + CHECK(!was_updated); + VLOG(notifications) << "There is no need to send updateNotificationGroup in " << group_key + << ", because of newer notification groups"; + for (auto &pending_notification : group.pending_notifications) { + group.notifications.emplace_back(pending_notification.notification_id, std::move(pending_notification.type)); + } + } else { + if (!was_updated) { + if (last_group_key.last_notification_date != 0) { + // need to remove last notification group to not exceed max_notification_group_size_ + send_remove_group_update(last_group_key.group_id); + } + send_add_group_update(group_key, group); + } + + DialogId notification_settings_dialog_id; + bool is_silent = false; + + // split notifications by groups with common settings + vector grouped_notifications; + for (auto &pending_notification : group.pending_notifications) { + if (notification_settings_dialog_id != pending_notification.settings_dialog_id || + is_silent != pending_notification.is_silent) { + flush_pending_notifications(group_key, group, grouped_notifications); + notification_settings_dialog_id = pending_notification.settings_dialog_id; + is_silent = pending_notification.is_silent; + } + grouped_notifications.push_back(std::move(pending_notification)); + } + flush_pending_notifications(group_key, group, grouped_notifications); + } + + group.pending_notifications_flush_time = 0; + group.pending_notifications.clear(); + if (group.notifications.size() > + keep_notification_group_size_ + EXTRA_GROUP_SIZE) { // ensure that we delete a lot of messages simultaneously + // keep only keep_notification_group_size_ last notifications in memory + group.notifications.erase( + group.notifications.begin(), + group.notifications.begin() + (group.notifications.size() - keep_notification_group_size_)); + } + + groups_.emplace(std::move(final_group_key), std::move(group)); } void NotificationManager::edit_notification(NotificationId notification_id, unique_ptr type) { @@ -85,6 +368,8 @@ void NotificationManager::edit_notification(NotificationId notification_id, uniq return; } + CHECK(notification_id.is_valid()); + CHECK(type != nullptr); VLOG(notifications) << "Edit " << notification_id << ": " << *type; } @@ -92,6 +377,8 @@ void NotificationManager::delete_notification(NotificationId notification_id) { if (is_disabled()) { return; } + + CHECK(notification_id.is_valid()); } void NotificationManager::remove_notification(NotificationId notification_id, Promise &&promise) { @@ -124,4 +411,48 @@ void NotificationManager::remove_notification_group(NotificationGroupId group_id promise.set_value(Unit()); } +void NotificationManager::on_notification_group_count_max_changed() { + if (is_disabled()) { + return; + } + + auto new_max_notification_group_count = + G()->shared_config().get_option_integer("notification_group_count_max", DEFAULT_GROUP_COUNT_MAX); + CHECK(MIN_NOTIFICATION_GROUP_COUNT_MAX <= new_max_notification_group_count && + new_max_notification_group_count <= MAX_NOTIFICATION_GROUP_COUNT_MAX); + + if (static_cast(new_max_notification_group_count) == max_notification_group_count_) { + return; + } + + if (max_notification_group_count_ != 0) { + // TODO + } + + max_notification_group_count_ = static_cast(new_max_notification_group_count); +} + +void NotificationManager::on_notification_group_size_max_changed() { + if (is_disabled()) { + return; + } + + auto new_max_notification_group_size = + G()->shared_config().get_option_integer("notification_group_size_max", DEFAULT_GROUP_SIZE_MAX); + CHECK(MIN_NOTIFICATION_GROUP_SIZE_MAX <= new_max_notification_group_size && + new_max_notification_group_size <= MAX_NOTIFICATION_GROUP_SIZE_MAX); + + if (static_cast(new_max_notification_group_size) == max_notification_group_size_) { + return; + } + + if (max_notification_group_size_ != 0) { + // TODO + } + + max_notification_group_size_ = static_cast(new_max_notification_group_size); + keep_notification_group_size_ = + max_notification_group_size_ + max(EXTRA_GROUP_SIZE / 2, min(max_notification_group_size_, EXTRA_GROUP_SIZE)); +} + } // namespace td diff --git a/td/telegram/NotificationManager.h b/td/telegram/NotificationManager.h index 5eb02656b..bb9500497 100644 --- a/td/telegram/NotificationManager.h +++ b/td/telegram/NotificationManager.h @@ -10,11 +10,16 @@ #include "td/telegram/NotificationGroupId.h" #include "td/telegram/NotificationId.h" #include "td/telegram/NotificationType.h" +#include "td/telegram/td_api.h" #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" +#include "td/actor/Timeout.h" #include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +#include namespace td { @@ -24,14 +29,20 @@ class Td; class NotificationManager : public Actor { public: + static constexpr int32 MIN_NOTIFICATION_GROUP_COUNT_MAX = 1; + static constexpr int32 MAX_NOTIFICATION_GROUP_COUNT_MAX = 25; + static constexpr int32 MIN_NOTIFICATION_GROUP_SIZE_MAX = 1; + static constexpr int32 MAX_NOTIFICATION_GROUP_SIZE_MAX = 25; + NotificationManager(Td *td, ActorShared<> parent); NotificationId get_next_notification_id(); NotificationGroupId get_next_notification_group_id(); - void add_notification(NotificationGroupId group_id, DialogId dialog_id, DialogId notification_settings_dialog_id, - bool silent, NotificationId notification_id, unique_ptr type); + void add_notification(NotificationGroupId group_id, DialogId dialog_id, int32 date, + DialogId notification_settings_dialog_id, bool is_silent, NotificationId notification_id, + unique_ptr type); void edit_notification(NotificationId notification_id, unique_ptr type); @@ -42,40 +53,110 @@ class NotificationManager : public Actor { void remove_notification_group(NotificationGroupId group_id, NotificationId max_notification_id, Promise &&promise); + void on_notification_group_count_max_changed(); + + void on_notification_group_size_max_changed(); + private: + static constexpr int32 DEFAULT_GROUP_COUNT_MAX = 10; + static constexpr int32 DEFAULT_GROUP_SIZE_MAX = 10; + static constexpr size_t EXTRA_GROUP_SIZE = 10; + + static constexpr int32 DEFAULT_ONLINE_CLOUD_TIMEOUT_MS = 300000; + static constexpr int32 DEFAULT_ONLINE_CLOUD_DELAY_MS = 30000; + static constexpr int32 DEFAULT_DEFAULT_DELAY_MS = 1500; + + static constexpr int32 MIN_NOTIFICATION_DELAY_MS = 1; + struct Notification { NotificationId notification_id; unique_ptr type; + + Notification(NotificationId notification_id, unique_ptr type) + : notification_id(notification_id), type(std::move(type)) { + } }; struct PendingNotification { + int32 date = 0; DialogId settings_dialog_id; - bool silent = false; + bool is_silent = false; NotificationId notification_id; unique_ptr type; }; - struct NotificationGroup { + struct NotificationGroupKey { + NotificationGroupId group_id; DialogId dialog_id; + int32 last_notification_date = 0; + + bool operator<(const NotificationGroupKey &other) const { + if (last_notification_date != other.last_notification_date) { + return last_notification_date > other.last_notification_date; + } + if (dialog_id != other.dialog_id) { + return dialog_id.get() > other.dialog_id.get(); + } + return group_id.get() > other.group_id.get(); + } + + friend StringBuilder &operator<<(StringBuilder &string_builder, const NotificationGroupKey &group_key) { + return string_builder << '[' << group_key.group_id << ',' << group_key.dialog_id << ',' + << group_key.last_notification_date << ']'; + } + }; + struct NotificationGroup { int32 total_count = 0; vector notifications; + + double pending_notifications_flush_time = 0; vector pending_notifications; }; + using NotificationGroups = std::map; + + static void on_flush_pending_notifications_timeout_callback(void *notification_manager_ptr, int64 group_id_int); + bool is_disabled() const; void start_up() override; void tear_down() override; + static td_api::object_ptr get_notification_object(DialogId dialog_id, + const Notification ¬ification); + + void send_update_notification_group(td_api::object_ptr update); + + NotificationGroups::iterator get_group(NotificationGroupId group_id); + + NotificationGroupKey get_last_updated_group_key() const; + + void send_remove_group_update(NotificationGroupId group_id); + + void send_add_group_update(const NotificationGroupKey &group_key, const NotificationGroup &group); + + int32 get_notification_delay_ms(DialogId dialog_id, const PendingNotification ¬ification) const; + + void flush_pending_notifications(NotificationGroupKey &group_key, NotificationGroup &group, + vector &pending_notifications); + + void flush_pending_notifications(NotificationGroupId group_id); + NotificationId current_notification_id_; NotificationGroupId current_notification_group_id_; - static constexpr int32 DEFAULT_NOTIFICATION_GROUP_COUNT_MAX = 10; - static constexpr int32 DEFAULT_NOTIFICATION_GROUP_SIZE_MAX = 10; + size_t max_notification_group_count_ = 0; + size_t max_notification_group_size_ = 0; + size_t keep_notification_group_size_ = 0; - int32 max_notification_group_count_; - int32 max_notification_group_size_; + int32 online_cloud_timeout_ms_ = DEFAULT_ONLINE_CLOUD_TIMEOUT_MS; + int32 notification_cloud_delay_ms_ = DEFAULT_ONLINE_CLOUD_DELAY_MS; + int32 notification_default_delay_ms_ = DEFAULT_DEFAULT_DELAY_MS; + + NotificationGroups groups_; + + MultiTimeout flush_pending_notifications_timeout_{"FlushPendingNotificationsTimeout"}; Td *td_; ActorShared<> parent_; diff --git a/td/telegram/NotificationType.cpp b/td/telegram/NotificationType.cpp index 06f9192bc..7a129edf0 100644 --- a/td/telegram/NotificationType.cpp +++ b/td/telegram/NotificationType.cpp @@ -6,9 +6,20 @@ // #include "td/telegram/NotificationType.h" +#include "td/telegram/MessagesManager.h" +#include "td/telegram/Td.h" + namespace td { class NotificationTypeMessage : public NotificationType { + td_api::object_ptr get_notification_type_object(DialogId dialog_id) const override { + auto message_object = G()->td().get_actor_unsafe()->messages_manager_->get_message_object({dialog_id, message_id_}); + if (message_object == nullptr) { + return nullptr; + } + return td_api::make_object(std::move(message_object)); + } + StringBuilder &to_string_builder(StringBuilder &string_builder) const override { return string_builder << "NewMessageNotification[" << message_id_ << ']'; } @@ -25,6 +36,10 @@ class NotificationTypeMessage : public NotificationType { }; class NotificationTypeSecretChat : public NotificationType { + td_api::object_ptr get_notification_type_object(DialogId dialog_id) const override { + return td_api::make_object(); + } + StringBuilder &to_string_builder(StringBuilder &string_builder) const override { return string_builder << "NewSecretChatNotification[]"; } @@ -39,6 +54,10 @@ class NotificationTypeSecretChat : public NotificationType { }; class NotificationTypeCall : public NotificationType { + td_api::object_ptr get_notification_type_object(DialogId dialog_id) const override { + return td_api::make_object(call_id_.get()); + } + StringBuilder &to_string_builder(StringBuilder &string_builder) const override { return string_builder << "NewCallNotification[" << call_id_ << ']'; } diff --git a/td/telegram/NotificationType.h b/td/telegram/NotificationType.h index d7672a8f3..734f9479d 100644 --- a/td/telegram/NotificationType.h +++ b/td/telegram/NotificationType.h @@ -8,6 +8,7 @@ #include "td/telegram/CallId.h" #include "td/telegram/MessageId.h" +#include "td/telegram/td_api.h" #include "td/utils/common.h" #include "td/utils/StringBuilder.h" @@ -25,6 +26,8 @@ class NotificationType { virtual ~NotificationType() { } + virtual td_api::object_ptr get_notification_type_object(DialogId dialog_id) const = 0; + virtual StringBuilder &to_string_builder(StringBuilder &string_builder) const = 0; protected: diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index a63fd507e..0292ecf1c 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3485,6 +3485,10 @@ void Td::on_config_option_updated(const string &name) { } else if (name == "language_pack_version") { send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, -1); return; + } else if (name == "notification_group_count_max") { + send_closure(notification_manager_actor_, &NotificationManager::on_notification_group_count_max_changed); + } else if (name == "notification_group_size_max") { + send_closure(notification_manager_actor_, &NotificationManager::on_notification_group_size_max_changed); } else if (is_internal_config_option(name)) { return; } @@ -6226,10 +6230,14 @@ void Td::on_request(uint64 id, td_api::setOption &request) { } break; case 'n': - if (!is_bot && set_integer_option("notification_group_count_max", 1, 25)) { + if (!is_bot && + set_integer_option("notification_group_count_max", NotificationManager::MIN_NOTIFICATION_GROUP_COUNT_MAX, + NotificationManager::MAX_NOTIFICATION_GROUP_COUNT_MAX)) { return; } - if (!is_bot && set_integer_option("notification_group_size_max", 1, 25)) { + if (!is_bot && + set_integer_option("notification_group_size_max", NotificationManager::MIN_NOTIFICATION_GROUP_SIZE_MAX, + NotificationManager::MAX_NOTIFICATION_GROUP_SIZE_MAX)) { return; } break; diff --git a/td/telegram/td_emscripten.cpp b/td/telegram/td_emscripten.cpp index 2535c4f6c..678e8acf9 100644 --- a/td/telegram/td_emscripten.cpp +++ b/td/telegram/td_emscripten.cpp @@ -4,9 +4,6 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -// Just for testing. -// Will be completly rewritten - #include "td/actor/actor.h" #include "td/telegram/td_json_client.h"