From 8991ddf26394f45d4b63bb2b3ae203c15d6d04f6 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 5 Sep 2023 16:50:41 +0300 Subject: [PATCH] Add supergroup.has_active_stories/has_unread_active_stories. --- td/generate/scheme/td_api.tl | 4 +- td/telegram/ContactsManager.cpp | 155 ++++++++++++++++++++++++++++++-- td/telegram/ContactsManager.h | 15 +++- td/telegram/StoryManager.cpp | 10 ++- 4 files changed, 173 insertions(+), 11 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c1c827489..f0e244527 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -987,7 +987,9 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int53 memb //@restriction_reason If non-empty, contains a human-readable description of the reason why access to this supergroup or channel must be restricted //@is_scam True, if many users reported this supergroup or channel as a scam //@is_fake True, if many users reported this supergroup or channel as a fake account -supergroup id:int53 usernames:usernames date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool join_to_send_messages:Bool join_by_request:Bool is_slow_mode_enabled:Bool is_channel:Bool is_broadcast_group:Bool is_forum:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup; +//@has_active_stories True, if the channel has non-expired stories available to the current user +//@has_unread_active_stories True, if the channel has unread non-expired stories available to the current user +supergroup id:int53 usernames:usernames date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool join_to_send_messages:Bool join_by_request:Bool is_slow_mode_enabled:Bool is_channel:Bool is_broadcast_group:Bool is_forum:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool has_active_stories:Bool has_unread_active_stories:Bool = Supergroup; //@description Contains full information about a supergroup or channel //@photo Chat photo; may be null if empty or unknown. If non-null, then it is the same photo as in chat.photo diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 177335d06..57ab922c3 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -5080,6 +5080,9 @@ void ContactsManager::Channel::store(StorerT &storer) const { bool legacy_has_active_group_call = false; bool has_usernames = !usernames.is_empty(); bool has_flags2 = true; + bool has_max_active_story_id = max_active_story_id.is_valid(); + bool has_max_read_story_id = max_read_story_id.is_valid(); + bool has_max_active_story_id_next_reload_time = max_active_story_id_next_reload_time > Time::now(); BEGIN_STORE_FLAGS(); STORE_FLAG(false); STORE_FLAG(false); @@ -5115,6 +5118,9 @@ void ContactsManager::Channel::store(StorerT &storer) const { if (has_flags2) { BEGIN_STORE_FLAGS(); STORE_FLAG(is_forum); + STORE_FLAG(has_max_active_story_id); + STORE_FLAG(has_max_read_story_id); + STORE_FLAG(has_max_active_story_id_next_reload_time); END_STORE_FLAGS(); } @@ -5140,6 +5146,15 @@ void ContactsManager::Channel::store(StorerT &storer) const { if (has_usernames) { store(usernames, storer); } + if (has_max_active_story_id) { + store(max_active_story_id, storer); + } + if (has_max_read_story_id) { + store(max_read_story_id, storer); + } + if (has_max_active_story_id_next_reload_time) { + store_time(max_active_story_id_next_reload_time, storer); + } } template @@ -5162,6 +5177,9 @@ void ContactsManager::Channel::parse(ParserT &parser) { bool legacy_has_active_group_call; bool has_usernames; bool has_flags2; + bool has_max_active_story_id = false; + bool has_max_read_story_id = false; + bool has_max_active_story_id_next_reload_time = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(left); PARSE_FLAG(kicked); @@ -5197,6 +5215,9 @@ void ContactsManager::Channel::parse(ParserT &parser) { if (has_flags2) { BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_forum); + PARSE_FLAG(has_max_active_story_id); + PARSE_FLAG(has_max_read_story_id); + PARSE_FLAG(has_max_active_story_id_next_reload_time); END_PARSE_FLAGS(); } @@ -5252,6 +5273,15 @@ void ContactsManager::Channel::parse(ParserT &parser) { CHECK(!legacy_has_username); parse(usernames, parser); } + if (has_max_active_story_id) { + parse(max_active_story_id, parser); + } + if (has_max_read_story_id) { + parse(max_read_story_id, parser); + } + if (has_max_active_story_id_next_reload_time) { + parse_time(max_active_story_id_next_reload_time, parser); + } if (!check_utf8(title)) { LOG(ERROR) << "Have invalid title \"" << title << '"'; @@ -10483,7 +10513,7 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, } if (is_me_regular_user && (stories_available || stories_unavailable)) { - // update at the end, because it calls need_poll_active_stories + // update at the end, because it calls need_poll_user_active_stories on_update_user_story_ids_impl(u, user_id, StoryId(user->stories_max_id_), StoryId()); } @@ -13638,7 +13668,7 @@ void ContactsManager::on_update_user_story_ids(UserId user_id, StoryId max_activ on_update_user_story_ids_impl(u, user_id, max_active_story_id, max_read_story_id); update_user(u, user_id); } else { - LOG(INFO) << "Ignore update user has stories about unknown " << user_id; + LOG(INFO) << "Ignore update user story identifiers about unknown " << user_id; } } @@ -13663,7 +13693,7 @@ void ContactsManager::on_update_user_story_ids_impl(User *u, UserId user_id, Sto u->max_active_story_id = max_active_story_id; u->need_save_to_database = true; } - if (need_poll_active_stories(u, user_id)) { + if (need_poll_user_active_stories(u, user_id)) { auto max_active_story_id_next_reload_time = Time::now() + MAX_ACTIVE_STORY_ID_RELOAD_TIME; if (max_active_story_id_next_reload_time > u->max_active_story_id_next_reload_time + MAX_ACTIVE_STORY_ID_RELOAD_TIME / 5) { @@ -15581,7 +15611,7 @@ void ContactsManager::invalidate_invite_link_info(const string &invite_link) { invite_link_infos_.erase(invite_link); } -bool ContactsManager::need_poll_active_stories(const User *u, UserId user_id) const { +bool ContactsManager::need_poll_user_active_stories(const User *u, UserId user_id) const { return u != nullptr && user_id != get_my_id() && !is_user_contact(u, user_id, false) && !is_user_bot(u) && !is_user_support(u) && !is_user_deleted(u) && u->was_online != 0; } @@ -15600,7 +15630,7 @@ void ContactsManager::on_view_user_active_stories(vector user_ids) { continue; } User *u = get_user(user_id); - if (!need_poll_active_stories(u, user_id) || Time::now() < u->max_active_story_id_next_reload_time || + if (!need_poll_user_active_stories(u, user_id) || Time::now() < u->max_active_story_id_next_reload_time || u->is_max_active_story_id_being_reloaded) { continue; } @@ -16320,6 +16350,99 @@ void ContactsManager::on_update_channel_noforwards(Channel *c, ChannelId channel } } +void ContactsManager::on_update_channel_story_ids(ChannelId channel_id, StoryId max_active_story_id, + StoryId max_read_story_id) { + if (!channel_id.is_valid()) { + LOG(ERROR) << "Receive invalid " << channel_id; + return; + } + + Channel *c = get_channel_force(channel_id); + if (c != nullptr) { + on_update_channel_story_ids_impl(c, channel_id, max_active_story_id, max_read_story_id); + update_channel(c, channel_id); + } else { + LOG(INFO) << "Ignore update channel story identifiers about unknown " << channel_id; + } +} + +void ContactsManager::on_update_channel_story_ids_impl(Channel *c, ChannelId channel_id, StoryId max_active_story_id, + StoryId max_read_story_id) { + if (td_->auth_manager_->is_bot()) { + return; + } + if (max_active_story_id != StoryId() && !max_active_story_id.is_server()) { + LOG(ERROR) << "Receive max active " << max_active_story_id << " for " << channel_id; + return; + } + if (max_read_story_id != StoryId() && !max_read_story_id.is_server()) { + LOG(ERROR) << "Receive max read " << max_read_story_id << " for " << channel_id; + return; + } + + auto has_unread_stories = get_channel_has_unread_stories(c); + if (c->max_active_story_id != max_active_story_id) { + LOG(DEBUG) << "Change last active story of " << channel_id << " from " << c->max_active_story_id << " to " + << max_active_story_id; + c->max_active_story_id = max_active_story_id; + c->need_save_to_database = true; + } + if (need_poll_channel_active_stories(c, channel_id)) { + auto max_active_story_id_next_reload_time = Time::now() + MAX_ACTIVE_STORY_ID_RELOAD_TIME; + if (max_active_story_id_next_reload_time > + c->max_active_story_id_next_reload_time + MAX_ACTIVE_STORY_ID_RELOAD_TIME / 5) { + LOG(DEBUG) << "Change max_active_story_id_next_reload_time of " << channel_id; + c->max_active_story_id_next_reload_time = max_active_story_id_next_reload_time; + c->need_save_to_database = true; + } + } + if (!max_active_story_id.is_valid()) { + CHECK(max_read_story_id == StoryId()); + if (c->max_read_story_id != StoryId()) { + LOG(DEBUG) << "Drop last read " << c->max_read_story_id << " of " << channel_id; + c->max_read_story_id = StoryId(); + c->need_save_to_database = true; + } + } else if (max_read_story_id.get() > c->max_read_story_id.get()) { + LOG(DEBUG) << "Change last read story of " << channel_id << " from " << c->max_read_story_id << " to " + << max_read_story_id; + c->max_read_story_id = max_read_story_id; + c->need_save_to_database = true; + } + if (has_unread_stories != get_channel_has_unread_stories(c)) { + LOG(DEBUG) << "Change has_unread_stories of " << channel_id; + c->is_changed = true; + } +} + +void ContactsManager::on_update_channel_max_read_story_id(ChannelId channel_id, StoryId max_read_story_id) { + CHECK(channel_id.is_valid()); + + Channel *c = get_channel(channel_id); + if (c != nullptr) { + on_update_channel_max_read_story_id(c, channel_id, max_read_story_id); + update_channel(c, channel_id); + } +} + +void ContactsManager::on_update_channel_max_read_story_id(Channel *c, ChannelId channel_id, StoryId max_read_story_id) { + if (td_->auth_manager_->is_bot()) { + return; + } + + auto has_unread_stories = get_channel_has_unread_stories(c); + if (max_read_story_id.get() > c->max_read_story_id.get()) { + LOG(DEBUG) << "Change last read story of " << channel_id << " from " << c->max_read_story_id << " to " + << max_read_story_id; + c->max_read_story_id = max_read_story_id; + c->need_save_to_database = true; + } + if (has_unread_stories != get_channel_has_unread_stories(c)) { + LOG(DEBUG) << "Change has_unread_stories of " << channel_id; + c->is_changed = true; + } +} + void ContactsManager::on_update_channel_editable_username(ChannelId channel_id, string &&username) { Channel *c = get_channel(channel_id); CHECK(c != nullptr); @@ -18747,6 +18870,8 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char bool is_forum = (channel.flags_ & CHANNEL_FLAG_IS_FORUM) != 0; bool have_participant_count = (channel.flags_ & CHANNEL_FLAG_HAS_PARTICIPANT_COUNT) != 0; int32 participant_count = have_participant_count ? channel.participants_count_ : 0; + bool stories_available = channel.stories_max_id_ > 0; + bool stories_unavailable = channel.stories_unavailable_; if (have_participant_count) { auto channel_full = get_channel_full_const(channel_id); @@ -18931,6 +19056,10 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char if (old_join_to_send != get_channel_join_to_send(c) || old_join_request != get_channel_join_request(c)) { c->is_changed = true; } + if (!td_->auth_manager_->is_bot() && (stories_available || stories_unavailable)) { + // update at the end, because it calls need_poll_channel_active_stories + on_update_channel_story_ids_impl(c, channel_id, StoryId(channel.stories_max_id_), StoryId()); + } if (c->cache_version != Channel::CACHE_VERSION) { c->cache_version = Channel::CACHE_VERSION; @@ -19163,6 +19292,7 @@ td_api::object_ptr ContactsManager::get_user_status_object(U } bool ContactsManager::get_user_has_unread_stories(const User *u) { + CHECK(u != nullptr); return u->max_active_story_id.get() > u->max_read_story_id.get(); } @@ -19379,7 +19509,7 @@ td_api::object_ptr ContactsManager::get_update_unknown bool is_megagroup = min_channel == nullptr ? false : min_channel->is_megagroup_; return td_api::make_object(td_api::make_object( channel_id.get(), nullptr, 0, DialogParticipantStatus::Banned(0).get_chat_member_status_object(), 0, false, false, - false, !is_megagroup, false, false, !is_megagroup, false, false, false, string(), false, false)); + false, !is_megagroup, false, false, !is_megagroup, false, false, false, string(), false, false, false, false)); } int64 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char *source) const { @@ -19395,6 +19525,16 @@ int64 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char return channel_id.get(); } +bool ContactsManager::need_poll_channel_active_stories(const Channel *c, ChannelId channel_id) const { + return c != nullptr && !get_channel_status(c).is_member() && + have_input_peer_channel(c, channel_id, AccessRights::Read); +} + +bool ContactsManager::get_channel_has_unread_stories(const Channel *c) { + CHECK(c != nullptr); + return c->max_active_story_id.get() > c->max_read_story_id.get(); +} + tl_object_ptr ContactsManager::get_supergroup_object(ChannelId channel_id) const { return get_supergroup_object(channel_id, get_channel(channel_id)); } @@ -19408,7 +19548,8 @@ tl_object_ptr ContactsManager::get_supergroup_object(Channel get_channel_status(c).get_chat_member_status_object(), c->participant_count, c->has_linked_channel, c->has_location, c->sign_messages, get_channel_join_to_send(c), get_channel_join_request(c), c->is_slow_mode_enabled, !c->is_megagroup, c->is_gigagroup, c->is_forum, c->is_verified, - get_restriction_reason_description(c->restriction_reasons), c->is_scam, c->is_fake); + get_restriction_reason_description(c->restriction_reasons), c->is_scam, c->is_fake, + c->max_active_story_id.is_valid(), get_channel_has_unread_stories(c)); } tl_object_ptr ContactsManager::get_supergroup_full_info_object(ChannelId channel_id) const { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 9b1b2115e..ee9adc752 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -213,6 +213,8 @@ class ContactsManager final : public Actor { void on_update_channel_editable_username(ChannelId channel_id, string &&username); void on_update_channel_usernames(ChannelId channel_id, Usernames &&usernames); + void on_update_channel_story_ids(ChannelId channel_id, StoryId max_active_story_id, StoryId max_read_story_id); + void on_update_channel_max_read_story_id(ChannelId channel_id, StoryId max_read_story_id); void on_update_channel_description(ChannelId channel_id, string &&description); void on_update_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id); void on_update_channel_linked_channel_id(ChannelId channel_id, ChannelId group_channel_id); @@ -977,6 +979,10 @@ class ContactsManager final : public Actor { int32 date = 0; int32 participant_count = 0; + double max_active_story_id_next_reload_time = 0.0; + StoryId max_active_story_id; + StoryId max_read_story_id; + static constexpr uint32 CACHE_VERSION = 10; uint32 cache_version = 0; @@ -1492,6 +1498,9 @@ class ContactsManager final : public Actor { RestrictedRights default_permissions); static void on_update_channel_has_location(Channel *c, ChannelId channel_id, bool has_location); static void on_update_channel_noforwards(Channel *c, ChannelId channel_id, bool noforwards); + void on_update_channel_story_ids_impl(Channel *c, ChannelId channel_id, StoryId max_active_story_id, + StoryId max_read_story_id); + void on_update_channel_max_read_story_id(Channel *c, ChannelId channel_id, StoryId max_read_story_id); void on_update_channel_bot_user_ids(ChannelId channel_id, vector &&bot_user_ids); @@ -1755,7 +1764,7 @@ class ContactsManager final : public Actor { void on_dismiss_suggested_action(SuggestedAction action, Result &&result); - bool need_poll_active_stories(const User *u, UserId user_id) const; + bool need_poll_user_active_stories(const User *u, UserId user_id) const; static bool get_user_has_unread_stories(const User *u); @@ -1780,6 +1789,10 @@ class ContactsManager final : public Actor { tl_object_ptr get_basic_group_full_info_object(ChatId chat_id, const ChatFull *chat_full) const; + bool need_poll_channel_active_stories(const Channel *c, ChannelId channel_id) const; + + static bool get_channel_has_unread_stories(const Channel *c); + td_api::object_ptr get_update_supergroup_object(ChannelId channel_id, const Channel *c) const; diff --git a/td/telegram/StoryManager.cpp b/td/telegram/StoryManager.cpp index e9d8bbb44..cef7bfb94 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -3501,8 +3501,12 @@ void StoryManager::on_update_dialog_max_story_ids(DialogId owner_dialog_id, Stor send_closure_later(td_->contacts_manager_actor_, &ContactsManager::on_update_user_story_ids, owner_dialog_id.get_user_id(), max_story_id, max_read_story_id); break; - case DialogType::Chat: case DialogType::Channel: + // use send_closure_later because story order can be updated from update_channel + send_closure_later(td_->contacts_manager_actor_, &ContactsManager::on_update_channel_story_ids, + owner_dialog_id.get_channel_id(), max_story_id, max_read_story_id); + break; + case DialogType::Chat: case DialogType::SecretChat: case DialogType::None: default: @@ -3515,8 +3519,10 @@ void StoryManager::on_update_dialog_max_read_story_id(DialogId owner_dialog_id, case DialogType::User: td_->contacts_manager_->on_update_user_max_read_story_id(owner_dialog_id.get_user_id(), max_read_story_id); break; - case DialogType::Chat: case DialogType::Channel: + td_->contacts_manager_->on_update_channel_max_read_story_id(owner_dialog_id.get_channel_id(), max_read_story_id); + break; + case DialogType::Chat: case DialogType::SecretChat: case DialogType::None: default: