Add support for users and groups nearby.

GitOrigin-RevId: 98bde4f064541a0b8d6200092db0f3bc7f192b1f
This commit is contained in:
levlam 2019-10-16 21:55:16 +03:00
parent acf8afd2d6
commit 47625f39f0
9 changed files with 264 additions and 8 deletions

View File

@ -646,6 +646,13 @@ chat id:int53 type:ChatType chat_list:ChatList title:string photo:chatPhoto perm
chats chat_ids:vector<int53> = Chats;
//@description Describes a chat located nearby @chat_id Chat identifier @distance Distance to the chat location in meters
chatNearby chat_id:int53 distance:int32 = ChatNearby;
//@description Represents a list of chats located nearby @users_nearby List of users nearby @supergroups_nearby List of location-based supergroups nearby
chatsNearby users_nearby:vector<chatNearby> supergroups_nearby:vector<chatNearby> = ChatsNearby;
//@description Contains a chat invite link @invite_link Chat invite link
chatInviteLink invite_link:string = ChatInviteLink;
@ -656,7 +663,7 @@ chatInviteLink invite_link:string = ChatInviteLink;
//@photo Chat photo; may be null
//@member_count Number of members
//@member_user_ids User identifiers of some chat members that may be known to the current user
//@is_public True, if the chat is a public supergroup or a channel with a username or a location
//@is_public True, if the chat is a public supergroup or a channel, i.e. it has a username or it is a location-based supergroup
chatInviteLinkInfo chat_id:int53 type:ChatType title:string photo:chatPhoto member_count:int32 member_user_ids:vector<int32> is_public:Bool = ChatInviteLinkInfo;
@ -665,7 +672,7 @@ chatInviteLinkInfo chat_id:int53 type:ChatType title:string photo:chatPhoto memb
//@description The chat can be reported as spam using the method reportChat with the reason chatReportReasonSpam
chatActionBarReportSpam = ChatActionBar;
//@description The chat is a supergroup with a location, which can be reported as unrelated to the group using the method reportChat with the reason chatReportReasonUnrelatedLocation
//@description The chat is a location-based supergroup, which can be reported as having unrelated location using the method reportChat with the reason chatReportReasonUnrelatedLocation
chatActionBarReportUnrelatedLocation = ChatActionBar;
//@description The chat is a private or a secret chat, which can be reported using the method reportChat, or the other user can be added to the contact list using the method addContact, or the other user can be blocked using the method blockUser
@ -2841,6 +2848,9 @@ updateConnectionState state:ConnectionState = Update;
//@description New terms of service must be accepted by the user. If the terms of service are declined, then the deleteAccount method should be called with the reason "Decline ToS update" @terms_of_service_id Identifier of the terms of service @terms_of_service The new terms of service
updateTermsOfService terms_of_service_id:string terms_of_service:termsOfService = Update;
//@description List of users nearby has changed. The update is sent only 60 seconds after a successful searchChatsNearby request @users_nearby The new list of users nearby
updateUsersNearby users_nearby:vector<chatNearby> = Update;
//@description A new incoming inline query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @user_location User location, provided by the client; may be null @query Text of the query @offset Offset of the first entry to return
updateNewInlineQuery id:int64 sender_user_id:int32 user_location:location query:string offset:string = Update;
@ -3069,6 +3079,9 @@ searchChats query:string limit:int32 = Chats;
//@description Searches for the specified query in the title and username of already known chats via request to the server. Returns chats in the order seen in the chat list @query Query to search for @limit Maximum number of chats to be returned
searchChatsOnServer query:string limit:int32 = Chats;
//@description Returns a list of users and location-based supergroups nearby. The list of users nearby will be updated for 60 seconds after the request by the updates updateUsersNearby. The request should be sent again every 25 seconds with adjusted location to not miss new chats @location Current user location
searchChatsNearby location:location = ChatsNearby;
//@description Returns a list of frequently used chats. Supported only if the chat info database is enabled @category Category of chats to be returned @limit Maximum number of chats to be returned; up to 30
getTopChats category:TopChatCategory limit:int32 = Chats;
@ -3442,7 +3455,7 @@ setChatDescription chat_id:int53 description:string = Ok;
//-If new chat members don't have access to old messages in the supergroup, then toggleSupergroupIsAllHistoryAvailable needs to be used first to change that
setChatDiscussionGroup chat_id:int53 discussion_chat_id:int53 = Ok;
//@description Changes the lcoation of a chat. Available only for location-based supergroups, see supergroupFullInfo.can_set_location @chat_id Chat identifier @location New location for the chat; must be valid and not null
//@description Changes the location of a chat. Available only for some location-based supergroups, use supergroupFullInfo.can_set_location to check whether the method is allowed to use @chat_id Chat identifier @location New location for the chat; must be valid and not null
setChatLocation chat_id:int53 location:chatLocation = Ok;
//@description Pins a message in a chat; requires can_pin_messages rights @chat_id Identifier of the chat @message_id Identifier of the new pinned message @disable_notification True, if there should be no notification about the pinned message

Binary file not shown.

View File

@ -748,6 +748,33 @@ class ResetContactsQuery : public Td::ResultHandler {
}
};
class SearchDialogsNearbyQuery : public Td::ResultHandler {
Promise<tl_object_ptr<telegram_api::Updates>> promise_;
public:
explicit SearchDialogsNearbyQuery(Promise<tl_object_ptr<telegram_api::Updates>> &&promise)
: promise_(std::move(promise)) {
}
void send(const Location &location) {
send_query(G()->net_query_creator().create(
create_storer(telegram_api::contacts_getLocated(location.get_input_geo_point()))));
}
void on_result(uint64 id, BufferSlice packet) override {
auto result_ptr = fetch_result<telegram_api::contacts_getLocated>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(uint64 id, Status status) override {
promise_.set_error(std::move(status));
}
};
class UploadProfilePhotoQuery : public Td::ResultHandler {
Promise<Unit> promise_;
FileId file_id_;
@ -2487,6 +2514,9 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent
channel_unban_timeout_.set_callback(on_channel_unban_timeout_callback);
channel_unban_timeout_.set_callback_data(static_cast<void *>(this));
user_nearby_timeout_.set_callback(on_user_nearby_timeout_callback);
user_nearby_timeout_.set_callback_data(static_cast<void *>(this));
}
void ContactsManager::tear_down() {
@ -2561,6 +2591,35 @@ void ContactsManager::on_channel_unban_timeout(ChannelId channel_id) {
update_channel(c, channel_id); // always call, because in case of failure we need to reactivate timeout
}
void ContactsManager::on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long) {
if (G()->close_flag()) {
return;
}
auto contacts_manager = static_cast<ContactsManager *>(contacts_manager_ptr);
send_closure_later(contacts_manager->actor_id(contacts_manager), &ContactsManager::on_user_nearby_timeout,
UserId(narrow_cast<int32>(user_id_long)));
}
void ContactsManager::on_user_nearby_timeout(UserId user_id) {
if (G()->close_flag()) {
return;
}
auto u = get_user(user_id);
CHECK(u != nullptr);
LOG(INFO) << "Remove " << user_id << " from nearby list";
DialogId dialog_id(user_id);
for (size_t i = 0; i < users_nearby_.size(); i++) {
if (users_nearby_[i].dialog_id == dialog_id) {
users_nearby_.erase(users_nearby_.begin() + i);
send_update_users_nearby();
return;
}
}
}
template <class StorerT>
void ContactsManager::BotInfo::store(StorerT &storer) const {
using td::store;
@ -4483,6 +4542,143 @@ void ContactsManager::share_phone_number(UserId user_id, Promise<Unit> &&promise
td_->create_handler<AcceptContactQuery>(std::move(promise))->send(user_id, std::move(input_user));
}
void ContactsManager::search_dialogs_nearby(const Location &location,
Promise<td_api::object_ptr<td_api::chatsNearby>> &&promise) {
if (location.empty()) {
return promise.set_error(Status::Error(400, "Invalid location specified"));
}
auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](
Result<tl_object_ptr<telegram_api::Updates>> result) mutable {
send_closure(actor_id, &ContactsManager::on_get_dialogs_nearby, std::move(result), std::move(promise));
});
td_->create_handler<SearchDialogsNearbyQuery>(std::move(query_promise))->send(location);
}
vector<td_api::object_ptr<td_api::chatNearby>> ContactsManager::get_chats_nearby_object(
const vector<DialogNearby> &dialogs_nearby) {
return transform(dialogs_nearby, [](const DialogNearby &dialog_nearby) {
return td_api::make_object<td_api::chatNearby>(dialog_nearby.dialog_id.get(), dialog_nearby.distance);
});
}
void ContactsManager::send_update_users_nearby() const {
send_closure(G()->td(), &Td::send_update,
td_api::make_object<td_api::updateUsersNearby>(get_chats_nearby_object(users_nearby_)));
}
void ContactsManager::on_get_dialogs_nearby(Result<tl_object_ptr<telegram_api::Updates>> result,
Promise<td_api::object_ptr<td_api::chatsNearby>> &&promise) {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
auto updates_ptr = result.move_as_ok();
if (updates_ptr->get_id() != telegram_api::updates::ID) {
LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates";
return promise.set_error(Status::Error(500, "Receive unsupported response from the server"));
}
auto update = telegram_api::move_object_as<telegram_api::updates>(updates_ptr);
LOG(INFO) << "Receive chats nearby in " << to_string(update);
on_get_users(std::move(update->users_), "on_get_dialogs_nearby");
on_get_chats(std::move(update->chats_), "on_get_dialogs_nearby");
for (auto &dialog_nearby : users_nearby_) {
user_nearby_timeout_.cancel_timeout(dialog_nearby.dialog_id.get_user_id().get());
}
users_nearby_.clear();
channels_nearby_.clear();
for (auto &update_ptr : update->updates_) {
if (update_ptr->get_id() != telegram_api::updatePeerLocated::ID) {
LOG(ERROR) << "Receive unexpected " << to_string(update);
continue;
}
on_update_peer_located(std::move(static_cast<telegram_api::updatePeerLocated *>(update_ptr.get())->peers_), false);
}
std::sort(users_nearby_.begin(), users_nearby_.end());
promise.set_value(td_api::make_object<td_api::chatsNearby>(get_chats_nearby_object(users_nearby_),
get_chats_nearby_object(channels_nearby_)));
send_update_users_nearby(); // for other clients connected to the same TDLib instance
}
void ContactsManager::on_update_peer_located(vector<tl_object_ptr<telegram_api::peerLocated>> &&peers,
bool from_update) {
auto now = G()->unix_time();
bool need_update = false;
for (auto &peer_located : peers) {
DialogId dialog_id(peer_located->peer_);
int32 expires_at = peer_located->expires_;
int32 distance = peer_located->distance_;
if (distance < 0 || distance > 50000000) {
LOG(ERROR) << "Receive wrong distance to " << to_string(peer_located);
continue;
}
if (expires_at <= now) {
LOG(INFO) << "Skip expired result " << to_string(peer_located);
continue;
}
auto dialog_type = dialog_id.get_type();
if (dialog_type == DialogType::User) {
auto user_id = dialog_id.get_user_id();
if (!have_user(user_id)) {
LOG(ERROR) << "Can't find " << user_id;
continue;
}
if (expires_at < now + 86400) {
user_nearby_timeout_.set_timeout_in(user_id.get(), expires_at - now + 1);
}
} else if (dialog_type == DialogType::Channel) {
auto channel_id = dialog_id.get_channel_id();
if (!have_channel(channel_id)) {
LOG(ERROR) << "Can't find " << channel_id;
continue;
}
if (expires_at != std::numeric_limits<int32>::max()) {
LOG(ERROR) << "Receive expiring at " << expires_at << " group location in " << to_string(peer_located);
}
if (from_update) {
LOG(ERROR) << "Receive nearby " << channel_id << " from update";
continue;
}
} else {
LOG(ERROR) << "Receive chat of wrong type in " << to_string(peer_located);
continue;
}
td_->messages_manager_->force_create_dialog(dialog_id, "on_update_peer_located");
if (from_update) {
bool is_found = false;
for (auto &dialog_nearby : users_nearby_) {
if (dialog_nearby.dialog_id == dialog_id) {
if (dialog_nearby.distance != distance) {
dialog_nearby.distance = distance;
need_update = true;
}
is_found = true;
break;
}
}
if (!is_found) {
users_nearby_.emplace_back(dialog_id, distance);
need_update = true;
}
} else {
auto &dialogs_nearby = dialog_type == DialogType::User ? users_nearby_ : channels_nearby_;
dialogs_nearby.emplace_back(dialog_id, distance);
}
}
if (need_update) {
std::sort(users_nearby_.begin(), users_nearby_.end());
send_update_users_nearby();
}
}
void ContactsManager::set_profile_photo(const tl_object_ptr<td_api::InputFile> &input_photo, Promise<Unit> &&promise) {
auto r_file_id =
td_->file_manager_->get_input_file_id(FileType::Photo, input_photo, DialogId(get_my_id()), false, false);

View File

@ -18,6 +18,7 @@
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/Location.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/QueryCombiner.h"
@ -179,6 +180,8 @@ class ContactsManager : public Actor {
void on_update_channel_is_all_history_available(ChannelId channel_id, bool is_all_history_available);
void on_update_channel_default_permissions(ChannelId channel_id, RestrictedRights default_permissions);
void on_update_peer_located(vector<tl_object_ptr<telegram_api::peerLocated>> &&peers, bool from_update);
void on_update_dialog_administrators(DialogId dialog_id, vector<UserId> administrator_user_ids, bool have_access);
void speculative_add_channel_participants(ChannelId channel_id, const vector<UserId> &added_user_ids,
@ -232,6 +235,8 @@ class ContactsManager : public Actor {
void on_channel_unban_timeout(ChannelId channel_id);
void on_user_nearby_timeout(UserId user_id);
void check_dialog_username(DialogId dialog_id, const string &username, Promise<CheckDialogUsernameResult> &&promise);
static td_api::object_ptr<td_api::CheckChatUsernameResult> get_check_chat_username_result_object(
@ -282,6 +287,8 @@ class ContactsManager : public Actor {
void share_phone_number(UserId user_id, Promise<Unit> &&promise);
void search_dialogs_nearby(const Location &location, Promise<td_api::object_ptr<td_api::chatsNearby>> &&promise);
void set_profile_photo(const tl_object_ptr<td_api::InputFile> &input_photo, Promise<Unit> &&promise);
void delete_profile_photo(int64 profile_photo_id, Promise<Unit> &&promise);
@ -783,6 +790,18 @@ class ContactsManager : public Actor {
bool is_megagroup = false;
};
struct DialogNearby {
DialogId dialog_id;
int32 distance;
DialogNearby(DialogId dialog_id, int32 distance) : dialog_id(dialog_id), distance(distance) {
}
bool operator<(const DialogNearby &other) const {
return distance < other.distance || (distance == other.distance && dialog_id.get() < other.dialog_id.get());
}
};
class UserLogEvent;
class ChatLogEvent;
class ChannelLogEvent;
@ -1120,6 +1139,14 @@ class ContactsManager : public Actor {
void on_clear_imported_contacts(vector<Contact> &&contacts, vector<size_t> contacts_unique_id,
std::pair<vector<size_t>, vector<Contact>> &&to_add, Promise<Unit> &&promise);
static vector<td_api::object_ptr<td_api::chatNearby>> get_chats_nearby_object(
const vector<DialogNearby> &dialogs_nearby);
void send_update_users_nearby() const;
void on_get_dialogs_nearby(Result<tl_object_ptr<telegram_api::Updates>> result,
Promise<td_api::object_ptr<td_api::chatsNearby>> &&promise);
static bool is_channel_public(const Channel *c);
static bool is_valid_invite_link(const string &invite_link);
@ -1187,6 +1214,8 @@ class ContactsManager : public Actor {
static void on_channel_unban_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long);
static void on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long);
void on_user_online_timeout(UserId user_id);
void tear_down() override;
@ -1287,6 +1316,9 @@ class ContactsManager : public Actor {
bool are_imported_contacts_changing_ = false;
bool need_clear_imported_contacts_ = false;
vector<DialogNearby> users_nearby_;
vector<DialogNearby> channels_nearby_;
vector<Contact> next_all_imported_contacts_;
vector<size_t> imported_contacts_unique_id_;
vector<size_t> imported_contacts_pos_;
@ -1296,6 +1328,7 @@ class ContactsManager : public Actor {
MultiTimeout user_online_timeout_{"UserOnlineTimeout"};
MultiTimeout channel_unban_timeout_{"ChannelUnbanTimeout"};
MultiTimeout user_nearby_timeout_{"UserOnlineTimeout"};
};
} // namespace td

View File

@ -5499,6 +5499,12 @@ void Td::on_request(uint64 id, td_api::searchChatsOnServer &request) {
CREATE_REQUEST(SearchChatsOnServerRequest, request.query_, request.limit_);
}
void Td::on_request(uint64 id, const td_api::searchChatsNearby &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
contacts_manager_->search_dialogs_nearby(Location(request.location_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getGroupsInCommon &request) {
CHECK_IS_USER();
CREATE_REQUEST(GetGroupsInCommonRequest, request.user_id_, request.offset_chat_id_, request.limit_);

View File

@ -514,6 +514,8 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, td_api::searchChatsOnServer &request);
void on_request(uint64 id, const td_api::searchChatsNearby &request);
void on_request(uint64 id, const td_api::addRecentlyFoundChat &request);
void on_request(uint64 id, const td_api::removeRecentlyFoundChat &request);

View File

@ -1536,6 +1536,10 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePeerSettings> u
td_->messages_manager_->on_get_peer_settings(DialogId(update->peer_), std::move(update->settings_));
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePeerLocated> update, bool /*force_apply*/) {
td_->contacts_manager_->on_update_peer_located(std::move(update->peers_), true);
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateWebPage> update, bool force_apply) {
CHECK(update != nullptr);
td_->web_pages_manager_->on_get_web_page(std::move(update->webpage_), DialogId());
@ -1905,7 +1909,4 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateMessagePoll> up
// unsupported updates
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePeerLocated> update, bool /*force_apply*/) {
}
} // namespace td

View File

@ -190,6 +190,7 @@ class UpdatesManager : public Actor {
void on_update(tl_object_ptr<telegram_api::updateReadHistoryOutbox> update, bool force_apply);
void on_update(tl_object_ptr<telegram_api::updateNotifySettings> update, bool /*force_apply*/);
void on_update(tl_object_ptr<telegram_api::updatePeerSettings> update, bool /*force_apply*/);
void on_update(tl_object_ptr<telegram_api::updatePeerLocated> update, bool /*force_apply*/);
void on_update(tl_object_ptr<telegram_api::updateWebPage> update, bool force_apply);
void on_update(tl_object_ptr<telegram_api::updateChannelWebPage> update, bool force_apply);
@ -280,8 +281,6 @@ class UpdatesManager : public Actor {
void on_update(tl_object_ptr<telegram_api::updateMessagePoll> update, bool /*force_apply*/);
// unsupported updates
void on_update(tl_object_ptr<telegram_api::updatePeerLocated> update, bool /*force_apply*/);
};
} // namespace td

View File

@ -3530,6 +3530,12 @@ class CliClient final : public Actor {
string query;
std::tie(limit, query) = split(args);
send_request(td_api::make_object<td_api::searchChatsOnServer>(query, to_integer<int32>(limit)));
} else if (op == "scn") {
string latitude;
string longitude;
std::tie(latitude, longitude) = split(args);
send_request(td_api::make_object<td_api::searchChatsNearby>(as_location(latitude, longitude)));
} else if (op == "sco") {
string limit;
string query;