diff --git a/CMakeLists.txt b/CMakeLists.txt index 34da5c0e9..ab0545d8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,6 +323,7 @@ set(TDLIB_SOURCE td/telegram/files/ResourceManager.cpp td/telegram/Game.cpp td/telegram/Global.cpp + td/telegram/GroupCallManager.cpp td/telegram/HashtagHints.cpp td/telegram/InlineQueriesManager.cpp td/telegram/InputDialogId.cpp @@ -497,6 +498,7 @@ set(TDLIB_SOURCE td/telegram/FullMessageId.h td/telegram/Game.h td/telegram/Global.h + td/telegram/GroupCallManager.h td/telegram/HashtagHints.h td/telegram/InlineQueriesManager.h td/telegram/InputDialogId.h diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index a82805e58..8dd8a621c 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -566,13 +566,14 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int32 memb //@member_count Number of members in the supergroup or channel; 0 if unknown. Currently it is guaranteed to be known only if the supergroup or channel was received through searchPublicChats, searchChatsNearby, getInactiveSupergroupChats, getSuitableDiscussionChats, getGroupsInCommon, or getUserPrivacySettingRules //@has_linked_chat True, if the channel has a discussion group, or the supergroup is the designated discussion group for a channel //@has_location True, if the supergroup is connected to a location, i.e. the supergroup is a location-based supergroup +//@has_active_call True, if the supergroup has active group call //@sign_messages True, if messages sent to the channel should contain information about the sender. This field is only applicable to channels //@is_slow_mode_enabled True, if the slow mode is enabled in the supergroup //@is_channel True, if the supergroup is a channel //@is_verified True, if the supergroup or channel is verified //@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 as a scam -supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_verified:Bool restriction_reason:string is_scam:Bool = Supergroup; +supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool has_active_call:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_verified:Bool restriction_reason:string is_scam:Bool = Supergroup; //@description Contains full information about a supergroup or channel //@photo Chat photo; may be null @@ -582,6 +583,7 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co //@restricted_count Number of restricted users in the supergroup; 0 if unknown //@banned_count Number of users banned from chat; 0 if unknown //@linked_chat_id Chat identifier of a discussion group for the channel, or a channel, for which the supergroup is the designated discussion group; 0 if none or unknown +//@group_call_id Identifier of an active group call; empty if none or unknown //@slow_mode_delay Delay between consecutive sent messages for non-administrator supergroup members, in seconds //@slow_mode_delay_expires_in Time left before next message can be sent in the supergroup, in seconds. An updateSupergroupFullInfo update is not triggered when value of this field changes, but both new and old values are non-zero //@can_get_members True, if members of the chat can be retrieved @@ -595,7 +597,7 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co //@invite_link Invite link for this chat //@upgraded_from_basic_group_id Identifier of the basic group from which supergroup was upgraded; 0 if none //@upgraded_from_max_message_id Identifier of the last message in the basic group from which supergroup was upgraded; 0 if none -supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_get_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:string upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo; +supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 group_call_id:string slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_get_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:string upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo; //@class SecretChatState @description Describes the current secret chat state @@ -1627,11 +1629,11 @@ messageInvoice title:string description:string photo:photo currency:string total //@description A message with information about an ended call @is_video True, if the call was a video call @discard_reason Reason why the call was discarded @duration Call duration, in seconds messageCall is_video:Bool discard_reason:CallDiscardReason duration:int32 = MessageContent; -//@description A message with information about a group call -messageGroupCall = MessageContent; +//@description A message with information about a group call @group_call_id Group call identifier @duration Call duration; for finished calls only +messageGroupCall group_call_id:string duration:int32 = MessageContent; -//@description A message with information about an invite to a group call -messageInviteToGroupCall = MessageContent; +//@description A message with information about an invite to a group call @group_call_id Group call identifier @user_id Invited user identifier +messageInviteToGroupCall group_call_id:string user_id:int32 = MessageContent; //@description A newly created basic group @title Title of the basic group @member_user_ids User identifiers of members in the basic group messageBasicGroupChatCreate title:string member_user_ids:vector = MessageContent; @@ -2032,6 +2034,9 @@ callServer id:int64 ip_address:string ipv6_address:string port:int32 type:CallSe //@description Contains the call identifier @id Call identifier callId id:int32 = CallId; +//@description Contains the group call identifier @id Group call identifier +groupCallId id:string = GroupCallId; + //@class CallState @description Describes the current call state @@ -2054,6 +2059,10 @@ callStateDiscarded reason:CallDiscardReason need_rating:Bool need_debug_informat callStateError error:error = CallState; +//@description Describes a group call @id Group call identifier @is_active True, if the call is active @member_count Number of members in the group call @duration Call duration; for ended calls only +groupCall id:string is_active:Bool member_count:int32 duration:int32 = GroupCall; + + //@class CallProblem @description Describes the exact type of a problem with a call //@description The user heard their own voice @@ -3335,6 +3344,9 @@ updateFileGenerationStop generation_id:int64 = Update; //@description New call was created or information about a call was updated @call New data about a call updateCall call:call = Update; +//@description Information about a group call was updated @group_call New data about the call +updateGroupCall group_call:groupCall = Update; + //@description New call signaling data arrived @call_id The call identifier @data The data updateNewCallSignalingData call_id:int32 data:bytes = Update; @@ -4282,6 +4294,10 @@ sendCallRating call_id:int32 rating:int32 comment:string problems:vector @@ -3784,6 +3792,7 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { bool is_slow_mode_delay_active; bool has_stats_dc_id; bool has_photo; + bool has_active_group_call_id; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_description); PARSE_FLAG(has_administrator_count); @@ -3808,6 +3817,7 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { PARSE_FLAG(has_photo); PARSE_FLAG(is_can_view_statistics_inited); PARSE_FLAG(can_view_statistics); + PARSE_FLAG(has_active_group_call_id); END_PARSE_FLAGS(); if (has_description) { parse(description, parser); @@ -3856,6 +3866,9 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { if (has_photo) { parse(photo, parser); } + if (has_active_group_call_id) { + parse(active_group_call_id, parser); + } if (legacy_can_view_statistics) { LOG(DEBUG) << "Ignore legacy can view statistics flag"; @@ -5901,6 +5914,70 @@ void ContactsManager::set_channel_slow_mode_delay(DialogId dialog_id, int32 slow td_->create_handler(std::move(promise))->send(channel_id, slow_mode_delay); } +void ContactsManager::create_channel_group_call(DialogId dialog_id, Promise &&promise) { + if (!dialog_id.is_valid()) { + return promise.set_error(Status::Error(400, "Invalid chat identifier specified")); + } + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + if (dialog_id.get_type() != DialogType::Channel) { + return promise.set_error(Status::Error(400, "Chat is not a supergroup")); + } + + auto channel_id = dialog_id.get_channel_id(); + const Channel *c = get_channel(channel_id); + if (c == nullptr) { + return promise.set_error(Status::Error(400, "Chat info not found")); + } + if (!c->is_megagroup) { + return promise.set_error(Status::Error(400, "Chat is not a supergroup")); + } + if (!get_channel_permissions(c).can_manage_calls()) { + return promise.set_error(Status::Error(400, "Not enough rights in the supergroup")); + } + + auto new_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), channel_id, promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &ContactsManager::on_create_channel_group_call, channel_id, result.move_as_ok(), + std::move(promise)); + } + }); + send_closure(G()->group_call_manager(), &GroupCallManager::create_group_call, channel_id, std::move(new_promise)); +} + +void ContactsManager::on_create_channel_group_call(ChannelId channel_id, InputGroupCallId group_call_id, + Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + if (!group_call_id.is_valid()) { + return promise.set_error(Status::Error(500, "Receive invalid group call identifier")); + } + + Channel *c = get_channel(channel_id); + if (c == nullptr) { + return promise.set_error(Status::Error(500, "Channel not found")); + } + if (!c->has_active_group_call) { + c->has_active_group_call = true; + c->is_changed = true; + update_channel(c, channel_id); + } + + auto channel_full = get_channel_full_force(channel_id, "on_create_channel_group_call"); + if (channel_full != nullptr && channel_full->active_group_call_id != group_call_id) { + channel_full->active_group_call_id = group_call_id; + channel_full->is_changed = true; + update_channel_full(channel_full, channel_id); + } + promise.set_value(std::move(group_call_id)); +} + void ContactsManager::get_channel_statistics_dc_id(DialogId dialog_id, bool for_full_statistics, Promise &&promise) { if (!dialog_id.is_valid()) { @@ -8695,7 +8772,13 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s channel_full->expires_at = 0.0; } } - on_update_channel_full_photo(channel_full, channel_id, std::move(channel_full->photo)); + auto photo = std::move(channel_full->photo); + on_update_channel_full_photo(channel_full, channel_id, std::move(photo)); + + if (!c->has_active_group_call && channel_full->active_group_call_id.is_valid()) { + channel_full->active_group_call_id = InputGroupCallId(); + channel_full->expires_at = 0.0; + } update_channel_full(channel_full, channel_id, true); @@ -9532,6 +9615,14 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c LOG(ERROR) << "Receive can_view_statistics == true, but invalid statistics DC ID in " << channel_id; can_view_statistics = false; } + InputGroupCallId group_call_id; + if (channel_full->call_ != nullptr) { + group_call_id = InputGroupCallId(channel_full->call_); + if (group_call_id.is_valid() && !c->is_megagroup) { + LOG(ERROR) << "Receive " << group_call_id << " in " << channel_id; + group_call_id = InputGroupCallId(); + } + } channel->repair_request_version = 0; channel->expires_at = Time::now() + CHANNEL_FULL_EXPIRE_TIME; @@ -9568,6 +9659,19 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c channel->need_save_to_database = true; } + if (channel->active_group_call_id != group_call_id) { + channel->active_group_call_id = group_call_id; + bool has_active_group_call = group_call_id.is_valid(); + if (c->has_active_group_call != has_active_group_call) { + LOG(ERROR) << "Receive invalid has_active_group_call flag " << c->has_active_group_call << ", but have " + << group_call_id << " in " << channel_id; + c->has_active_group_call = has_active_group_call; + c->is_changed = true; + update_channel(c, channel_id); + } + channel->is_changed = true; + } + on_update_channel_full_photo( channel, channel_id, get_photo(td_->file_manager_.get(), std::move(channel_full->chat_photo_), DialogId(channel_id))); @@ -10943,7 +11047,8 @@ void ContactsManager::drop_channel_photos(ChannelId channel_id, bool is_empty, b } } -void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay) { +void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay, + bool drop_active_group_call_id) { LOG(INFO) << "Invalidate supergroup full for " << channel_id; // drop channel full cache auto channel_full = get_channel_full_force(channel_id, "invalidate_channel_full"); @@ -10958,6 +11063,10 @@ void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_in channel_full->is_slow_mode_next_send_date_changed = true; channel_full->is_changed = true; } + if (drop_active_group_call_id && channel_full->active_group_call_id.is_valid()) { + channel_full->active_group_call_id = InputGroupCallId(); + channel_full->is_changed = true; + } update_channel_full(channel_full, channel_id); } if (drop_invite_link) { @@ -13845,6 +13954,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char bool has_linked_channel = (channel.flags_ & CHANNEL_FLAG_HAS_LINKED_CHAT) != 0; bool has_location = (channel.flags_ & CHANNEL_FLAG_HAS_LOCATION) != 0; + bool has_active_group_call = (channel.flags_ & CHANNEL_FLAG_HAS_ACTIVE_GROUP_CALL) != 0; bool sign_messages = (channel.flags_ & CHANNEL_FLAG_SIGN_MESSAGES) != 0; bool is_slow_mode_enabled = (channel.flags_ & CHANNEL_FLAG_IS_SLOW_MODE_ENABLED) != 0; bool is_megagroup = (channel.flags_ & CHANNEL_FLAG_IS_MEGAGROUP) != 0; @@ -13948,11 +14058,14 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char } bool need_invalidate_channel_full = false; + bool need_drop_active_group_call_id = c->has_active_group_call != has_active_group_call; if (c->has_linked_channel != has_linked_channel || c->has_location != has_location || - c->sign_messages != sign_messages || c->is_megagroup != is_megagroup || c->is_verified != is_verified || + c->has_active_group_call != has_active_group_call || c->sign_messages != sign_messages || + c->is_megagroup != is_megagroup || c->is_verified != is_verified || c->restriction_reasons != restriction_reasons || c->is_scam != is_scam) { c->has_linked_channel = has_linked_channel; c->has_location = has_location; + c->has_active_group_call = has_active_group_call; c->sign_messages = sign_messages; c->is_slow_mode_enabled = is_slow_mode_enabled; c->is_megagroup = is_megagroup; @@ -13972,7 +14085,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char update_channel(c, channel_id); if (need_invalidate_channel_full) { - invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled); + invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled, need_drop_active_group_call_id); } } @@ -14013,6 +14126,7 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co tl_object_ptr banned_rights; // == nullptr on_update_channel_default_permissions(c, channel_id, get_restricted_rights(banned_rights)); + bool has_active_group_call = false; bool sign_messages = false; bool is_slow_mode_enabled = false; bool is_megagroup = (channel.flags_ & CHANNEL_FLAG_IS_MEGAGROUP) != 0; @@ -14036,11 +14150,12 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co } bool need_invalidate_channel_full = false; - if (c->sign_messages != sign_messages || c->is_slow_mode_enabled != is_slow_mode_enabled || - c->is_megagroup != is_megagroup || c->is_verified != is_verified || !c->restriction_reasons.empty() || - c->is_scam != is_scam) { + if (c->has_active_group_call != has_active_group_call || c->sign_messages != sign_messages || + c->is_slow_mode_enabled != is_slow_mode_enabled || c->is_megagroup != is_megagroup || + c->is_verified != is_verified || !c->restriction_reasons.empty() || c->is_scam != is_scam) { // c->has_linked_channel = has_linked_channel; // c->has_location = has_location; + c->has_active_group_call = has_active_group_call; c->sign_messages = sign_messages; c->is_slow_mode_enabled = is_slow_mode_enabled; c->is_megagroup = is_megagroup; @@ -14266,7 +14381,7 @@ td_api::object_ptr ContactsManager::get_update_unknown ChannelId channel_id) { return td_api::make_object(td_api::make_object( channel_id.get(), string(), 0, DialogParticipantStatus::Banned(0).get_chat_member_status_object(), 0, false, - false, false, false, true, false, "", false)); + false, false, false, false, true, false, "", false)); } int32 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char *source) const { @@ -14288,8 +14403,9 @@ tl_object_ptr ContactsManager::get_supergroup_object(Channel } return td_api::make_object( channel_id.get(), c->username, c->date, get_channel_status(c).get_chat_member_status_object(), - c->participant_count, c->has_linked_channel, c->has_location, c->sign_messages, c->is_slow_mode_enabled, - !c->is_megagroup, c->is_verified, get_restriction_reason_description(c->restriction_reasons), c->is_scam); + c->participant_count, c->has_linked_channel, c->has_location, c->has_active_group_call, c->sign_messages, + c->is_slow_mode_enabled, !c->is_megagroup, c->is_verified, + get_restriction_reason_description(c->restriction_reasons), c->is_scam); } tl_object_ptr ContactsManager::get_supergroup_full_info_object(ChannelId channel_id) const { @@ -14308,11 +14424,11 @@ tl_object_ptr ContactsManager::get_supergroup_full_i return td_api::make_object( get_chat_photo_object(td_->file_manager_.get(), channel_full->photo), channel_full->description, channel_full->participant_count, channel_full->administrator_count, channel_full->restricted_count, - channel_full->banned_count, DialogId(channel_full->linked_channel_id).get(), channel_full->slow_mode_delay, - slow_mode_delay_expires_in, channel_full->can_get_participants, channel_full->can_set_username, - channel_full->can_set_sticker_set, channel_full->can_set_location, channel_full->can_view_statistics, - channel_full->is_all_history_available, channel_full->sticker_set_id.get(), - channel_full->location.get_chat_location_object(), channel_full->invite_link, + channel_full->banned_count, DialogId(channel_full->linked_channel_id).get(), + channel_full->active_group_call_id.get_group_call_id(), channel_full->slow_mode_delay, slow_mode_delay_expires_in, + channel_full->can_get_participants, channel_full->can_set_username, channel_full->can_set_sticker_set, + channel_full->can_set_location, channel_full->can_view_statistics, channel_full->is_all_history_available, + channel_full->sticker_set_id.get(), channel_full->location.get_chat_location_object(), channel_full->invite_link, get_basic_group_id_object(channel_full->migrated_from_chat_id, "get_supergroup_full_info_object"), channel_full->migrated_from_max_message_id.get()); } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 364af4d48..fa0abf31b 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -21,6 +21,7 @@ #include "td/telegram/files/FileSourceId.h" #include "td/telegram/FolderId.h" #include "td/telegram/FullMessageId.h" +#include "td/telegram/InputGroupCallId.h" #include "td/telegram/Location.h" #include "td/telegram/MessageId.h" #include "td/telegram/net/DcId.h" @@ -219,7 +220,8 @@ class ContactsManager : public Actor { void speculative_delete_channel_participant(ChannelId channel_id, UserId deleted_user_id, bool by_me); - void invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay); + void invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay, + bool drop_active_group_call_id = false); bool on_get_channel_error(ChannelId channel_id, const Status &status, const string &source); @@ -367,6 +369,8 @@ class ContactsManager : public Actor { void set_channel_slow_mode_delay(DialogId dialog_id, int32 slow_mode_delay, Promise &&promise); + void create_channel_group_call(DialogId dialog_id, Promise &&promise); + void report_channel_spam(ChannelId channel_id, UserId user_id, const vector &message_ids, Promise &&promise); @@ -793,6 +797,7 @@ class ContactsManager : public Actor { bool has_linked_channel = false; bool has_location = false; + bool has_active_group_call = false; bool sign_messages = false; bool is_slow_mode_enabled = false; @@ -851,6 +856,8 @@ class ContactsManager : public Actor { DcId stats_dc_id; + InputGroupCallId active_group_call_id; + int32 slow_mode_delay = 0; int32 slow_mode_next_send_date = 0; @@ -1021,6 +1028,7 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FLAG_HAS_LINKED_CHAT = 1 << 20; static constexpr int32 CHANNEL_FLAG_HAS_LOCATION = 1 << 21; static constexpr int32 CHANNEL_FLAG_IS_SLOW_MODE_ENABLED = 1 << 22; + static constexpr int32 CHANNEL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 23; static constexpr int32 CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 0; static constexpr int32 CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT = 1 << 1; @@ -1043,6 +1051,7 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FULL_FLAG_HAS_SLOW_MODE_NEXT_SEND_DATE = 1 << 18; static constexpr int32 CHANNEL_FULL_FLAG_HAS_SCHEDULED_MESSAGES = 1 << 19; static constexpr int32 CHANNEL_FULL_FLAG_CAN_VIEW_STATISTICS = 1 << 20; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 21; static constexpr int32 CHANNEL_FULL_FLAG_IS_BLOCKED = 1 << 22; static constexpr int32 CHAT_INVITE_FLAG_IS_CHANNEL = 1 << 0; @@ -1312,6 +1321,9 @@ class ContactsManager : public Actor { void update_bot_info(BotInfo *bot_info, UserId user_id, bool send_update, bool from_database); + void on_create_channel_group_call(ChannelId channel_id, InputGroupCallId group_call_id, + Promise &&promise); + bool is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id); bool is_user_contact(const User *u, UserId user_id) const; diff --git a/td/telegram/Global.h b/td/telegram/Global.h index 1333cea4f..b265c4f61 100644 --- a/td/telegram/Global.h +++ b/td/telegram/Global.h @@ -38,6 +38,7 @@ class ConnectionCreator; class ContactsManager; class FileManager; class FileReferenceManager; +class GroupCallManager; class LanguagePackManager; class MessagesManager; class MtprotoHeader; @@ -216,6 +217,13 @@ class Global : public ActorContext { file_reference_manager_ = std::move(file_reference_manager); } + ActorId group_call_manager() const { + return group_call_manager_; + } + void set_group_call_manager(ActorId group_call_manager) { + group_call_manager_ = group_call_manager; + } + ActorId language_pack_manager() const { return language_pack_manager_; } @@ -393,6 +401,7 @@ class Global : public ActorContext { ActorId contacts_manager_; ActorId file_manager_; ActorId file_reference_manager_; + ActorId group_call_manager_; ActorId language_pack_manager_; ActorId messages_manager_; ActorId notification_manager_; diff --git a/td/telegram/InputGroupCallId.cpp b/td/telegram/InputGroupCallId.cpp index b50b039d7..296dc3d89 100644 --- a/td/telegram/InputGroupCallId.cpp +++ b/td/telegram/InputGroupCallId.cpp @@ -7,6 +7,7 @@ #include "td/telegram/InputGroupCallId.h" #include "td/utils/logging.h" +#include "td/utils/misc.h" namespace td { @@ -14,12 +15,34 @@ InputGroupCallId::InputGroupCallId(const tl_object_ptrid_), access_hash(input_group_call->access_hash_) { } +Result InputGroupCallId::from_group_call_id(const string &group_call_id) { + if (group_call_id.empty()) { + return InputGroupCallId(); + } + + auto splitted = split(group_call_id, '_'); + auto r_group_call_id = to_integer_safe(splitted.first); + auto r_access_hash = to_integer_safe(splitted.second); + if (r_group_call_id.is_error() || r_access_hash.is_error()) { + return Status::Error("Invalid group call identifier specified"); + } + + return InputGroupCallId{r_group_call_id.ok(), r_access_hash.ok()}; +} + +string InputGroupCallId::get_group_call_id() const { + if (is_valid()) { + return PSTRING() << group_call_id << '_' << access_hash; + } + return string(); +} + tl_object_ptr InputGroupCallId::get_input_group_call() const { return make_tl_object(group_call_id, access_hash); } StringBuilder &operator<<(StringBuilder &string_builder, InputGroupCallId input_group_call_id) { - return string_builder << "input group call " << input_group_call_id.group_call_id; + return string_builder << "group call " << input_group_call_id.group_call_id; } } // namespace td diff --git a/td/telegram/InputGroupCallId.h b/td/telegram/InputGroupCallId.h index a1db1a5bb..b8583e1e6 100644 --- a/td/telegram/InputGroupCallId.h +++ b/td/telegram/InputGroupCallId.h @@ -9,8 +9,11 @@ #include "td/telegram/telegram_api.h" #include "td/utils/common.h" +#include "td/utils/Status.h" #include "td/utils/StringBuilder.h" +#include + namespace td { class InputGroupCallId { @@ -22,18 +25,33 @@ class InputGroupCallId { explicit InputGroupCallId(const tl_object_ptr &input_group_call); + InputGroupCallId(int64 group_call_id, int64 access_hash) : group_call_id(group_call_id), access_hash(access_hash) { + } + + static Result from_group_call_id(const string &group_call_id); + + string get_group_call_id() const; + bool operator==(const InputGroupCallId &other) const { - return group_call_id == other.group_call_id && access_hash == other.access_hash; + return group_call_id == other.group_call_id; } bool operator!=(const InputGroupCallId &other) const { return !(*this == other); } + bool is_identical(const InputGroupCallId &other) const { + return group_call_id == other.group_call_id && access_hash == other.access_hash; + } + bool is_valid() const { return group_call_id != 0; } + std::size_t get_hash() const { + return std::hash()(group_call_id); + } + tl_object_ptr get_input_group_call() const; template @@ -51,4 +69,10 @@ class InputGroupCallId { friend StringBuilder &operator<<(StringBuilder &string_builder, InputGroupCallId input_group_call_id); }; +struct InputGroupCallIdHash { + std::size_t operator()(InputGroupCallId input_group_call_id) const { + return input_group_call_id.get_hash(); + } +}; + } // namespace td diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index cc52b6708..95ef9d25f 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -3303,6 +3303,9 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo if (old_->group_call_id != new_->group_call_id || old_->duration != new_->duration) { need_update = true; } + if (!old_->group_call_id.is_identical(new_->group_call_id)) { + is_content_changed = true; + } break; } case MessageContentType::InviteToGroupCall: { @@ -3311,6 +3314,9 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo if (old_->group_call_id != new_->group_call_id || old_->user_id != new_->user_id) { need_update = true; } + if (!old_->group_call_id.is_identical(new_->group_call_id)) { + is_content_changed = true; + } break; } case MessageContentType::Unsupported: { @@ -4024,7 +4030,7 @@ unique_ptr get_message_content(Td *td, FormattedText message, auto message_contact = move_tl_object_as(media); if (message_contact->user_id_ != 0) { td->contacts_manager_->get_user_id_object(UserId(message_contact->user_id_), - "messageMediaContact"); // to ensure updateUser + "MessageMediaContact"); // to ensure updateUser } return make_unique(Contact( std::move(message_contact->phone_number_), std::move(message_contact->first_name_), @@ -4677,12 +4683,12 @@ tl_object_ptr get_message_content_object(const MessageCo case MessageContentType::ChatDeleteUser: { const MessageChatDeleteUser *m = static_cast(content); return make_tl_object( - td->contacts_manager_->get_user_id_object(m->user_id, "messageChatDeleteMember")); + td->contacts_manager_->get_user_id_object(m->user_id, "MessageChatDeleteMember")); } case MessageContentType::ChatMigrateTo: { const MessageChatMigrateTo *m = static_cast(content); return make_tl_object( - td->contacts_manager_->get_supergroup_id_object(m->migrated_to_channel_id, "messageChatUpgradeTo")); + td->contacts_manager_->get_supergroup_id_object(m->migrated_to_channel_id, "MessageChatUpgradeTo")); } case MessageContentType::ChannelCreate: { const MessageChannelCreate *m = static_cast(content); @@ -4692,7 +4698,7 @@ tl_object_ptr get_message_content_object(const MessageCo const MessageChannelMigrateFrom *m = static_cast(content); return make_tl_object( m->title, - td->contacts_manager_->get_basic_group_id_object(m->migrated_from_chat_id, "messageChatUpgradeFrom")); + td->contacts_manager_->get_basic_group_id_object(m->migrated_from_chat_id, "MessageChatUpgradeFrom")); } case MessageContentType::PinMessage: { const MessagePinMessage *m = static_cast(content); @@ -4768,10 +4774,17 @@ tl_object_ptr get_message_content_object(const MessageCo td->messages_manager_->get_message_sender_object(m->traveler_dialog_id), td->messages_manager_->get_message_sender_object(m->watcher_dialog_id), m->distance); } - case MessageContentType::GroupCall: - return make_tl_object(); - case MessageContentType::InviteToGroupCall: - return make_tl_object(); + case MessageContentType::GroupCall: { + auto *m = static_cast(content); + return make_tl_object(m->group_call_id.get_group_call_id(), + m->duration >= 0 ? max(m->duration, 1) : 0); + } + case MessageContentType::InviteToGroupCall: { + auto *m = static_cast(content); + return make_tl_object( + m->group_call_id.get_group_call_id(), + td->contacts_manager_->get_user_id_object(m->user_id, "MessageInviteToGroupCall")); + } default: UNREACHABLE(); return nullptr; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index a7649bc84..c90d3abeb 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -43,6 +43,7 @@ #include "td/telegram/FolderId.h" #include "td/telegram/FullMessageId.h" #include "td/telegram/Global.h" +#include "td/telegram/GroupCallManager.h" #include "td/telegram/HashtagHints.h" #include "td/telegram/InlineQueriesManager.h" #include "td/telegram/JsonValue.h" @@ -3896,6 +3897,8 @@ void Td::dec_actor_refcnt() { LOG(DEBUG) << "FileManager was cleared" << timer; file_reference_manager_.reset(); LOG(DEBUG) << "FileReferenceManager was cleared" << timer; + group_call_manager_.reset(); + LOG(DEBUG) << "GroupCallManager was cleared" << timer; inline_queries_manager_.reset(); LOG(DEBUG) << "InlineQueriesManager was cleared" << timer; messages_manager_.reset(); @@ -4082,6 +4085,8 @@ void Td::clear() { LOG(DEBUG) << "FileManager actor was cleared" << timer; file_reference_manager_actor_.reset(); LOG(DEBUG) << "FileReferenceManager actor was cleared" << timer; + group_call_manager_actor_.reset(); + LOG(DEBUG) << "GroupCallManager actor was cleared" << timer; inline_queries_manager_actor_.reset(); LOG(DEBUG) << "InlineQueriesManager actor was cleared" << timer; messages_manager_actor_.reset(); // TODO: Stop silent @@ -4521,6 +4526,9 @@ void Td::init_managers() { G()->set_contacts_manager(contacts_manager_actor_.get()); country_info_manager_ = make_unique(this, create_reference()); country_info_manager_actor_ = register_actor("CountryInfoManager", country_info_manager_.get()); + group_call_manager_ = make_unique(this, create_reference()); + group_call_manager_actor_ = register_actor("GroupCallManager", group_call_manager_.get()); + G()->set_group_call_manager(group_call_manager_actor_.get()); inline_queries_manager_ = make_unique(this, create_reference()); inline_queries_manager_actor_ = register_actor("InlineQueriesManager", inline_queries_manager_.get()); messages_manager_ = make_unique(this, create_reference()); @@ -4941,6 +4949,7 @@ void Td::on_request(uint64 id, const td_api::getCurrentState &request) { // TODO updateFileGenerationStart generation_id:int64 original_path:string destination_path:string conversion:string = Update; // TODO updateCall call:call = Update; + // TODO updateGroupCall call:groupCall = Update; } auto update_terms_of_service = get_update_terms_of_service_object(); @@ -6058,6 +6067,20 @@ void Td::on_request(uint64 id, td_api::sendCallDebugInformation &request) { std::move(request.debug_information_), std::move(promise)); } +void Td::on_request(uint64 id, const td_api::createChatGroupCall &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(td_api::make_object(result.ok().get_group_call_id())); + } + }); + + contacts_manager_->create_channel_group_call(DialogId(request.chat_id_), std::move(query_promise)); +} + void Td::on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request) { CHECK_IS_USER(); CREATE_REQUEST(UpgradeGroupChatToSupergroupChatRequest, request.chat_id_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 7eb1fdc2a..797158a4e 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -57,6 +57,7 @@ class DeviceTokenManager; class DocumentsManager; class FileManager; class FileReferenceManager; +class GroupCallManager; class InlineQueriesManager; class HashtagHints; class LanguagePackManager; @@ -164,6 +165,8 @@ class Td final : public NetQueryCallback { ActorOwn file_manager_actor_; unique_ptr file_reference_manager_; ActorOwn file_reference_manager_actor_; + unique_ptr group_call_manager_; + ActorOwn group_call_manager_actor_; unique_ptr inline_queries_manager_; ActorOwn inline_queries_manager_actor_; unique_ptr messages_manager_; @@ -702,6 +705,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::sendCallDebugInformation &request); + void on_request(uint64 id, const td_api::createChatGroupCall &request); + void on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request); void on_request(uint64 id, const td_api::getChatListsToAddChat &request); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 7191d924e..b516143a1 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -21,6 +21,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/FolderId.h" #include "td/telegram/Global.h" +#include "td/telegram/GroupCallManager.h" #include "td/telegram/InlineQueriesManager.h" #include "td/telegram/LanguagePackManager.h" #include "td/telegram/Location.h" @@ -900,6 +901,30 @@ vector *> UpdatesManager::get_new_mes return messages; } +vector UpdatesManager::get_update_new_group_call_ids(const telegram_api::Updates *updates_ptr) { + vector group_call_ids; + auto updates = get_updates(updates_ptr); + if (updates != nullptr) { + for (auto &update : *updates) { + InputGroupCallId group_call_id; + if (update->get_id() == telegram_api::updateGroupCall::ID) { + auto group_call_ptr = static_cast(update.get())->call_.get(); + if (group_call_ptr->get_id() == telegram_api::groupCall::ID) { + auto group_call = static_cast(group_call_ptr); + group_call_id = InputGroupCallId(group_call->id_, group_call->access_hash_); + } + } + + if (group_call_id.is_valid()) { + group_call_ids.push_back(group_call_id); + } else { + LOG(ERROR) << "Receive unexpected " << to_string(update); + } + } + } + return group_call_ids; +} + vector UpdatesManager::get_update_notify_settings_dialog_ids(const telegram_api::Updates *updates_ptr) { vector dialog_ids; auto updates = get_updates(updates_ptr); @@ -2122,6 +2147,10 @@ void UpdatesManager::on_update(tl_object_ptrdata_.as_slice().str()); } +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + send_closure(G()->group_call_manager(), &GroupCallManager::on_update_group_call, std::move(update->call_)); +} + void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { td_->contacts_manager_->on_update_contacts_reset(); } @@ -2184,7 +2213,4 @@ void UpdatesManager::on_update(tl_object_ptr update, void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { } -void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { -} - } // namespace td diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index efe3f3dbf..1a58c698c 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -9,6 +9,7 @@ #include "td/telegram/ChannelId.h" #include "td/telegram/ChatId.h" #include "td/telegram/DialogId.h" +#include "td/telegram/InputGroupCallId.h" #include "td/telegram/PtsManager.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -44,6 +45,8 @@ class UpdatesManager : public Actor { static vector *> get_new_messages( const telegram_api::Updates *updates_ptr); + static vector get_update_new_group_call_ids(const telegram_api::Updates *updates_ptr); + static vector get_update_notify_settings_dialog_ids(const telegram_api::Updates *updates_ptr); static vector get_chat_dialog_ids(const telegram_api::Updates *updates_ptr); @@ -298,6 +301,8 @@ class UpdatesManager : public Actor { void on_update(tl_object_ptr update, bool /*force_apply*/); void on_update(tl_object_ptr update, bool /*force_apply*/); + void on_update(tl_object_ptr update, bool /*force_apply*/); + void on_update(tl_object_ptr update, bool /*force_apply*/); void on_update(tl_object_ptr update, bool /*force_apply*/); @@ -320,8 +325,6 @@ class UpdatesManager : public Actor { void on_update(tl_object_ptr update, bool /*force_apply*/); void on_update(tl_object_ptr update, bool /*force_apply*/); - - void on_update(tl_object_ptr update, bool /*force_apply*/); }; } // namespace td diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index fbdc143a0..d12ae41b2 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2837,6 +2837,8 @@ class CliClient final : public Actor { as_call_id(call_id), to_integer(rating), "Wow, such good call! (TDLib test)", std::move(problems))); } else if (op == "scdi" || op == "SendCallDebugInformation") { send_request(td_api::make_object(as_call_id(args), "{}")); + } else if (op == "ccgc") { + send_request(td_api::make_object(as_chat_id(args))); } else if (op == "gcil") { send_request(td_api::make_object(as_chat_id(args))); } else if (op == "ccil") {