Add support for multiple usernames.

This commit is contained in:
levlam 2022-10-12 21:04:18 +03:00
parent 87174e5e81
commit 897032e0fd
16 changed files with 391 additions and 134 deletions

View File

@ -452,6 +452,7 @@ set(TDLIB_SOURCE
td/telegram/TopDialogCategory.cpp
td/telegram/TopDialogManager.cpp
td/telegram/UpdatesManager.cpp
td/telegram/Usernames.cpp
td/telegram/Venue.cpp
td/telegram/VideoNotesManager.cpp
td/telegram/VideosManager.cpp
@ -714,6 +715,7 @@ set(TDLIB_SOURCE
td/telegram/UniqueId.h
td/telegram/UpdatesManager.h
td/telegram/UserId.h
td/telegram/Usernames.h
td/telegram/Venue.h
td/telegram/Version.h
td/telegram/VideoNotesManager.h

View File

@ -486,11 +486,18 @@ emojiStatus custom_emoji_id:int64 = EmojiStatus;
emojiStatuses emoji_statuses:vector<emojiStatus> = EmojiStatuses;
//@description Describes usernames assigned to a user, a supergroup, or a channel
//@active_usernames List of active usernames; the first one must be shown as the primary username. The order of active usernames can be changed with reorderActiveUsernames or reorderSupergroupActiveUsernames
//@disabled_usernames List of currently disabled usernames; the username can be activated or disabled with toggleUsernameIsActive/toggleSupergroupUsernameIsActive
//@editable_username The active username, which can be changed with setUsername/setSupergroupUsername; if there is at least one active username, then the username can't be empty
usernames active_usernames:vector<string> disabled_usernames:vector<string> editable_username:string = Usernames;
//@description Represents a user
//@id User identifier
//@first_name First name of the user
//@last_name Last name of the user
//@username Username of the user
//@usernames Usernames of the user; may be null
//@phone_number Phone number of the user
//@status Current online status of the user
//@profile_photo Profile photo of the user; may be null
@ -507,7 +514,7 @@ emojiStatuses emoji_statuses:vector<emojiStatus> = EmojiStatuses;
//@type Type of the user
//@language_code IETF language tag of the user's language; only available to bots
//@added_to_attachment_menu True, if the user added the current bot to attachment menu; only available to bots
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto emoji_status:emojiStatus is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_premium:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string added_to_attachment_menu:Bool = User;
user id:int53 first_name:string last_name:string usernames:usernames phone_number:string status:UserStatus profile_photo:profilePhoto emoji_status:emojiStatus is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_premium:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string added_to_attachment_menu:Bool = User;
//@description Contains information about a bot
@ -716,7 +723,7 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int53 memb
//@description Represents a supergroup or channel with zero or more members (subscribers in the case of channels). From the point of view of the system, a channel is a special kind of a supergroup: only administrators can post and see the list of members, and posts from all administrators use the name and photo of the channel instead of individual names and profile photos. Unlike supergroups, channels can have an unlimited number of subscribers
//@id Supergroup or channel identifier
//@username Username of the supergroup or channel; empty for private supergroups or channels
//@usernames Usernames of the supergroup or channel; may be null
//@date Point in time (Unix timestamp) when the current user joined, or the point in time when the supergroup or channel was created, in case the user is not a member
//@status Status of the current user in the supergroup or channel; custom title will always be empty
//@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
@ -732,7 +739,7 @@ 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 username:string 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_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup;
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_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup;
//@description Contains full information about a supergroup or channel
//@photo Chat photo; may be null
@ -1149,7 +1156,7 @@ chatsNearby users_nearby:vector<chatNearby> supergroups_nearby:vector<chatNearby
//@class PublicChatType @description Describes a type of public chats
//@description The chat is public, because it has username
//@description The chat is public, because it has an active username
publicChatTypeHasUsername = PublicChatType;
//@description The chat is public, because it is a location-based supergroup
@ -2099,7 +2106,7 @@ messageUnsupported = MessageContent;
//@class TextEntityType @description Represents a part of the text which must be formatted differently
//@description A mention of a user by their username
//@description A mention of a user, a supergroup, or a channel by their username
textEntityTypeMention = TextEntityType;
//@description A hashtag text, beginning with "#"
@ -2917,7 +2924,7 @@ chatEventStickerSetChanged old_sticker_set_id:int64 new_sticker_set_id:int64 = C
//@description The chat title was changed @old_title Previous chat title @new_title New chat title
chatEventTitleChanged old_title:string new_title:string = ChatEventAction;
//@description The chat username was changed @old_username Previous chat username @new_username New chat username
//@description The chat editable username was changed @old_username Previous chat username @new_username New chat username
chatEventUsernameChanged old_username:string new_username:string = ChatEventAction;
//@description The has_protected_content setting of a channel was toggled @has_protected_content New value of has_protected_content
@ -4926,7 +4933,7 @@ getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 off
//@chat_id Chat identifier @remove_from_chat_list Pass true to remove the chat from all chat lists @revoke Pass true to delete chat history for all users
deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok;
//@description Deletes a chat along with all messages in the corresponding chat for all chat members. For group chats this will release the username and remove all members. Use the field chat.can_be_deleted_for_all_users to find whether the method can be applied to the chat @chat_id Chat identifier
//@description Deletes a chat along with all messages in the corresponding chat for all chat members. For group chats this will release the usernames and remove all members. Use the field chat.can_be_deleted_for_all_users to find whether the method can be applied to the chat @chat_id Chat identifier
deleteChat chat_id:int53 = Ok;
//@description Searches for messages with given words in the chat. Returns the results in reverse chronological order, i.e. in order of decreasing message_id. Cannot be used in secret chats with a non-empty query
@ -5615,7 +5622,7 @@ transferChatOwnership chat_id:int53 user_id:int53 password:string = Ok;
//@description Returns information about a single member of a chat @chat_id Chat identifier @member_id Member identifier
getChatMember chat_id:int53 member_id:MessageSender = ChatMember;
//@description Searches for a specified query in the first name, last name and username of the members of a specified chat. Requires administrator rights in channels
//@description Searches for a specified query in the first name, last name and usernames of the members of a specified chat. Requires administrator rights in channels
//@chat_id Chat identifier
//@query Query to search for
//@limit The maximum number of users to be returned; up to 200
@ -6189,7 +6196,7 @@ setName first_name:string last_name:string = Ok;
//@description Changes the bio of the current user @bio The new value of the user bio; 0-GetOption("bio_length_max") characters without line feeds
setBio bio:string = Ok;
//@description Changes the username of the current user @username The new value of the username. Use an empty string to remove the username
//@description Changes the editable username of the current user @username The new value of the username. Use an empty string to remove the username. The username can't be completely removed if there is another active or disabled username
setUsername username:string = Ok;
//@description Changes the emoji status of the current user; for Telegram Premium users only
@ -6271,7 +6278,7 @@ disconnectWebsite website_id:int64 = Ok;
disconnectAllWebsites = Ok;
//@description Changes the username of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username
//@description Changes the editable username of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username. The username can't be completely removed if there is another active or disabled username
setSupergroupUsername supergroup_id:int53 username:string = Ok;
//@description Changes the sticker set of a supergroup; requires can_change_info administrator right @supergroup_id Identifier of the supergroup @sticker_set_id New value of the supergroup sticker set identifier. Use 0 to remove the supergroup sticker set

View File

@ -774,13 +774,13 @@ class UpdateChannelUsernameQuery final : public Td::ResultHandler {
return on_error(Status::Error(500, "Supergroup username is not updated"));
}
td_->contacts_manager_->on_update_channel_username(channel_id_, std::move(username_));
// td_->contacts_manager_->on_update_channel_editable_username(channel_id_, std::move(username_));
promise_.set_value(Unit());
}
void on_error(Status status) final {
if (status.message() == "USERNAME_NOT_MODIFIED" || status.message() == "CHAT_NOT_MODIFIED") {
td_->contacts_manager_->on_update_channel_username(channel_id_, std::move(username_));
// td_->contacts_manager_->on_update_channel_editable_username(channel_id_, std::move(username_));
if (!td_->auth_manager_->is_bot()) {
promise_.set_value(Unit());
return;
@ -3672,7 +3672,7 @@ template <class StorerT>
void ContactsManager::User::store(StorerT &storer) const {
using td::store;
bool has_last_name = !last_name.empty();
bool has_username = !username.empty();
bool legacy_has_username = false;
bool has_photo = photo.small_file_id.is_valid();
bool has_language_code = !language_code.empty();
bool have_access_hash = access_hash != -1;
@ -3680,6 +3680,7 @@ void ContactsManager::User::store(StorerT &storer) const {
bool has_is_contact = true;
bool has_restriction_reasons = !restriction_reasons.empty();
bool has_emoji_status = !emoji_status.is_empty();
bool has_usernames = !usernames.is_empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(is_received);
STORE_FLAG(is_verified);
@ -3690,7 +3691,7 @@ void ContactsManager::User::store(StorerT &storer) const {
STORE_FLAG(is_inline_bot);
STORE_FLAG(need_location_bot);
STORE_FLAG(has_last_name);
STORE_FLAG(has_username);
STORE_FLAG(legacy_has_username);
STORE_FLAG(has_photo); // 10
STORE_FLAG(false); // legacy is_restricted
STORE_FLAG(has_language_code);
@ -3709,14 +3710,12 @@ void ContactsManager::User::store(StorerT &storer) const {
STORE_FLAG(is_premium); // 25
STORE_FLAG(attach_menu_enabled);
STORE_FLAG(has_emoji_status);
STORE_FLAG(has_usernames);
END_STORE_FLAGS();
store(first_name, storer);
if (has_last_name) {
store(last_name, storer);
}
if (has_username) {
store(username, storer);
}
store(phone_number, storer);
if (have_access_hash) {
store(access_hash, storer);
@ -3743,13 +3742,16 @@ void ContactsManager::User::store(StorerT &storer) const {
if (has_emoji_status) {
store(emoji_status, storer);
}
if (has_usernames) {
store(usernames, storer);
}
}
template <class ParserT>
void ContactsManager::User::parse(ParserT &parser) {
using td::parse;
bool has_last_name;
bool has_username;
bool legacy_has_username;
bool has_photo;
bool legacy_is_restricted;
bool has_language_code;
@ -3758,6 +3760,7 @@ void ContactsManager::User::parse(ParserT &parser) {
bool has_is_contact;
bool has_restriction_reasons;
bool has_emoji_status;
bool has_usernames;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_received);
PARSE_FLAG(is_verified);
@ -3768,7 +3771,7 @@ void ContactsManager::User::parse(ParserT &parser) {
PARSE_FLAG(is_inline_bot);
PARSE_FLAG(need_location_bot);
PARSE_FLAG(has_last_name);
PARSE_FLAG(has_username);
PARSE_FLAG(legacy_has_username);
PARSE_FLAG(has_photo);
PARSE_FLAG(legacy_is_restricted);
PARSE_FLAG(has_language_code);
@ -3787,13 +3790,17 @@ void ContactsManager::User::parse(ParserT &parser) {
PARSE_FLAG(is_premium);
PARSE_FLAG(attach_menu_enabled);
PARSE_FLAG(has_emoji_status);
PARSE_FLAG(has_usernames);
END_PARSE_FLAGS();
parse(first_name, parser);
if (has_last_name) {
parse(last_name, parser);
}
if (has_username) {
if (legacy_has_username) {
CHECK(!has_usernames);
string username;
parse(username, parser);
usernames = Usernames(std::move(username), vector<telegram_api::object_ptr<telegram_api::username>>());
}
parse(phone_number, parser);
if (parser.version() < static_cast<int32>(Version::FixMinUsers)) {
@ -3841,6 +3848,10 @@ void ContactsManager::User::parse(ParserT &parser) {
if (has_emoji_status) {
parse(emoji_status, parser);
}
if (has_usernames) {
CHECK(!legacy_has_username);
parse(usernames, parser);
}
if (!check_utf8(first_name)) {
LOG(ERROR) << "Have invalid first name \"" << first_name << '"';
@ -3852,11 +3863,6 @@ void ContactsManager::User::parse(ParserT &parser) {
last_name.clear();
cache_version = 0;
}
if (!check_utf8(username)) {
LOG(ERROR) << "Have invalid username \"" << username << '"';
username.clear();
cache_version = 0;
}
clean_phone_number(phone_number);
if (first_name.empty() && last_name.empty()) {
@ -4213,13 +4219,14 @@ template <class StorerT>
void ContactsManager::Channel::store(StorerT &storer) const {
using td::store;
bool has_photo = photo.small_file_id.is_valid();
bool has_username = !username.empty();
bool legacy_has_username = false;
bool use_new_rights = true;
bool has_participant_count = participant_count != 0;
bool have_default_permissions = true;
bool has_cache_version = cache_version != 0;
bool has_restriction_reasons = !restriction_reasons.empty();
bool legacy_has_active_group_call = false;
bool has_usernames = !usernames.is_empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(false);
STORE_FLAG(false);
@ -4231,7 +4238,7 @@ void ContactsManager::Channel::store(StorerT &storer) const {
STORE_FLAG(is_megagroup);
STORE_FLAG(is_verified);
STORE_FLAG(has_photo);
STORE_FLAG(has_username); // 10
STORE_FLAG(legacy_has_username); // 10
STORE_FLAG(false);
STORE_FLAG(use_new_rights);
STORE_FLAG(has_participant_count);
@ -4249,6 +4256,7 @@ void ContactsManager::Channel::store(StorerT &storer) const {
STORE_FLAG(can_be_deleted); // 25
STORE_FLAG(join_to_send);
STORE_FLAG(join_request);
STORE_FLAG(has_usernames);
END_STORE_FLAGS();
store(status, storer);
@ -4257,9 +4265,6 @@ void ContactsManager::Channel::store(StorerT &storer) const {
if (has_photo) {
store(photo, storer);
}
if (has_username) {
store(username, storer);
}
store(date, storer);
if (has_restriction_reasons) {
store(restriction_reasons, storer);
@ -4273,13 +4278,16 @@ void ContactsManager::Channel::store(StorerT &storer) const {
if (has_cache_version) {
store(cache_version, storer);
}
if (has_usernames) {
store(usernames, storer);
}
}
template <class ParserT>
void ContactsManager::Channel::parse(ParserT &parser) {
using td::parse;
bool has_photo;
bool has_username;
bool legacy_has_username;
bool legacy_is_restricted;
bool left;
bool kicked;
@ -4293,6 +4301,7 @@ void ContactsManager::Channel::parse(ParserT &parser) {
bool has_cache_version;
bool has_restriction_reasons;
bool legacy_has_active_group_call;
bool has_usernames;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(left);
PARSE_FLAG(kicked);
@ -4304,7 +4313,7 @@ void ContactsManager::Channel::parse(ParserT &parser) {
PARSE_FLAG(is_megagroup);
PARSE_FLAG(is_verified);
PARSE_FLAG(has_photo);
PARSE_FLAG(has_username);
PARSE_FLAG(legacy_has_username);
PARSE_FLAG(legacy_is_restricted);
PARSE_FLAG(use_new_rights);
PARSE_FLAG(has_participant_count);
@ -4322,6 +4331,7 @@ void ContactsManager::Channel::parse(ParserT &parser) {
PARSE_FLAG(can_be_deleted);
PARSE_FLAG(join_to_send);
PARSE_FLAG(join_request);
PARSE_FLAG(has_usernames);
END_PARSE_FLAGS();
if (use_new_rights) {
@ -4344,8 +4354,11 @@ void ContactsManager::Channel::parse(ParserT &parser) {
if (has_photo) {
parse(photo, parser);
}
if (has_username) {
if (legacy_has_username) {
CHECK(!has_usernames);
string username;
parse(username, parser);
usernames = Usernames(std::move(username), vector<telegram_api::object_ptr<telegram_api::username>>());
}
parse(date, parser);
if (legacy_is_restricted) {
@ -4369,17 +4382,16 @@ void ContactsManager::Channel::parse(ParserT &parser) {
if (has_cache_version) {
parse(cache_version, parser);
}
if (has_usernames) {
CHECK(!legacy_has_username);
parse(usernames, parser);
}
if (!check_utf8(title)) {
LOG(ERROR) << "Have invalid title \"" << title << '"';
title.clear();
cache_version = 0;
}
if (!check_utf8(username)) {
LOG(ERROR) << "Have invalid username \"" << username << '"';
username.clear();
cache_version = 0;
}
if (legacy_has_active_group_call) {
cache_version = 0;
}
@ -5098,7 +5110,7 @@ string ContactsManager::get_user_search_text(UserId user_id) const {
string ContactsManager::get_user_search_text(const User *u) {
CHECK(u != nullptr);
return PSTRING() << u->first_name << ' ' << u->last_name << ' ' << u->username;
return PSTRING() << u->first_name << ' ' << u->last_name << ' ' << implode(u->usernames.get_active_usernames());
}
string ContactsManager::get_channel_search_text(ChannelId channel_id) const {
@ -5111,7 +5123,7 @@ string ContactsManager::get_channel_search_text(ChannelId channel_id) const {
string ContactsManager::get_channel_search_text(const Channel *c) {
CHECK(c != nullptr);
return PSTRING() << c->title << ' ' << c->username;
return PSTRING() << c->title << ' ' << implode(c->usernames.get_active_usernames());
}
int32 ContactsManager::get_secret_chat_date(SecretChatId secret_chat_id) const {
@ -5130,7 +5142,7 @@ int32 ContactsManager::get_secret_chat_ttl(SecretChatId secret_chat_id) const {
return c->ttl;
}
string ContactsManager::get_user_username(UserId user_id) const {
string ContactsManager::get_user_first_username(UserId user_id) const {
if (!user_id.is_valid()) {
return string();
}
@ -5139,15 +5151,15 @@ string ContactsManager::get_user_username(UserId user_id) const {
if (u == nullptr) {
return string();
}
return u->username;
return u->usernames.get_first_username();
}
string ContactsManager::get_channel_username(ChannelId channel_id) const {
string ContactsManager::get_channel_first_username(ChannelId channel_id) const {
auto c = get_channel(channel_id);
if (c == nullptr) {
return string();
}
return c->username;
return c->usernames.get_first_username();
}
UserId ContactsManager::get_secret_chat_user_id(SecretChatId secret_chat_id) const {
@ -5332,7 +5344,7 @@ void ContactsManager::check_dialog_username(DialogId dialog_id, const string &us
return promise.set_error(Status::Error(400, "Not enough rights to change username"));
}
if (username == c->username) {
if (username == c->usernames.get_editable_username()) {
return promise.set_value(CheckDialogUsernameResult::Ok);
}
break;
@ -6699,7 +6711,7 @@ void ContactsManager::set_channel_username(ChannelId channel_id, const string &u
return promise.set_error(Status::Error(400, "Username is invalid"));
}
if (!username.empty() && c->username.empty()) {
if (!username.empty() && c->usernames.is_empty()) {
auto channel_full = get_channel_full(channel_id, false, "set_channel_username");
if (channel_full != nullptr && !channel_full->can_set_username) {
return promise.set_error(Status::Error(400, "Can't set supergroup username"));
@ -8208,7 +8220,7 @@ void ContactsManager::finish_get_created_public_dialogs(PublicDialogType type, R
void ContactsManager::update_created_public_channels(Channel *c, ChannelId channel_id) {
if (created_public_channels_inited_[0]) {
bool was_changed = false;
if (c->username.empty() || !c->status.is_creator()) {
if (c->usernames.is_empty() || !c->status.is_creator()) {
was_changed = td::remove(created_public_channels_[0], channel_id);
} else {
if (!td::contains(created_public_channels_[0], channel_id)) {
@ -8809,7 +8821,7 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
if (is_received || !u->is_received) {
on_update_user_name(u, user_id, std::move(user->first_name_), std::move(user->last_name_),
std::move(user->username_));
Usernames{std::move(user->username_), std::move(user->usernames_)});
}
on_update_user_emoji_status(u, user_id, EmojiStatus(std::move(user->emoji_status_)));
@ -8867,7 +8879,7 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
need_location_bot != u->need_location_bot || can_be_added_to_attach_menu != u->can_be_added_to_attach_menu ||
attach_menu_enabled != u->attach_menu_enabled) {
LOG_IF(ERROR, is_bot != u->is_bot && !is_deleted && !u->is_deleted && u->is_received)
<< "User.is_bot has changed for " << user_id << "/" << u->username << " from " << source << " from "
<< "User.is_bot has changed for " << user_id << "/" << u->usernames << " from " << source << " from "
<< u->is_bot << " to " << is_bot;
u->is_verified = is_verified;
u->is_support = is_support;
@ -9694,8 +9706,8 @@ void ContactsManager::on_load_channel_from_database(ChannelId channel_id, string
CHECK(!c->is_being_saved);
}
if (temp_c.username != c->username) {
on_channel_username_changed(c, channel_id, temp_c.username, c->username);
if (temp_c.usernames != c->usernames) {
on_channel_usernames_changed(c, channel_id, temp_c.usernames, c->usernames);
CHECK(!c->is_being_saved);
}
}
@ -11660,7 +11672,8 @@ void ContactsManager::on_get_channel_full_failed(ChannelId channel_id) {
}
}
void ContactsManager::on_update_user_name(UserId user_id, string &&first_name, string &&last_name, string &&username) {
void ContactsManager::on_update_user_name(UserId user_id, string &&first_name, string &&last_name,
Usernames &&usernames) {
if (!user_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << user_id;
return;
@ -11668,7 +11681,7 @@ void ContactsManager::on_update_user_name(UserId user_id, string &&first_name, s
User *u = get_user_force(user_id);
if (u != nullptr) {
on_update_user_name(u, user_id, std::move(first_name), std::move(last_name), std::move(username));
on_update_user_name(u, user_id, std::move(first_name), std::move(last_name), std::move(usernames));
update_user(u, user_id);
} else {
LOG(INFO) << "Ignore update user name about unknown " << user_id;
@ -11676,7 +11689,7 @@ void ContactsManager::on_update_user_name(UserId user_id, string &&first_name, s
}
void ContactsManager::on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name,
string &&username) {
Usernames &&usernames) {
if (first_name.empty() && last_name.empty()) {
first_name = u->phone_number;
}
@ -11687,11 +11700,11 @@ void ContactsManager::on_update_user_name(User *u, UserId user_id, string &&firs
LOG(DEBUG) << "Name has changed for " << user_id;
u->is_changed = true;
}
td_->messages_manager_->on_dialog_username_updated(DialogId(user_id), u->username, username);
if (u->username != username) {
u->username = std::move(username);
td_->messages_manager_->on_dialog_usernames_updated(DialogId(user_id), u->usernames, usernames);
if (u->usernames != usernames) {
u->usernames = std::move(usernames);
u->is_username_changed = true;
LOG(DEBUG) << "Username has changed for " << user_id;
LOG(DEBUG) << "Usernames has changed for " << user_id;
u->is_changed = true;
}
}
@ -12595,9 +12608,9 @@ bool ContactsManager::on_get_channel_error(ChannelId channel_id, const Status &s
c->access_hash, c->title, 0);
on_chat_update(update, "CHANNEL_PRIVATE");
} else if (!c->status.is_banned()) {
if (!c->username.empty()) {
LOG(INFO) << "Drop username of " << channel_id;
on_update_channel_username(c, channel_id, "");
if (!c->usernames.is_empty()) {
LOG(INFO) << "Drop usernames of " << channel_id;
on_update_channel_usernames(c, channel_id, Usernames());
}
on_update_channel_has_location(c, channel_id, false);
@ -14202,7 +14215,7 @@ void ContactsManager::on_update_channel_noforwards(Channel *c, ChannelId channel
}
}
void ContactsManager::on_update_channel_username(ChannelId channel_id, string &&username) {
void ContactsManager::on_update_channel_usernames(ChannelId channel_id, Usernames &&usernames) {
if (!channel_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << channel_id;
return;
@ -14210,30 +14223,30 @@ void ContactsManager::on_update_channel_username(ChannelId channel_id, string &&
Channel *c = get_channel_force(channel_id);
if (c != nullptr) {
on_update_channel_username(c, channel_id, std::move(username));
on_update_channel_usernames(c, channel_id, std::move(usernames));
update_channel(c, channel_id);
} else {
LOG(INFO) << "Ignore update channel username about unknown " << channel_id;
LOG(INFO) << "Ignore update channel usernames about unknown " << channel_id;
}
}
void ContactsManager::on_update_channel_username(Channel *c, ChannelId channel_id, string &&username) {
td_->messages_manager_->on_dialog_username_updated(DialogId(channel_id), c->username, username);
if (c->username != username) {
void ContactsManager::on_update_channel_usernames(Channel *c, ChannelId channel_id, Usernames &&usernames) {
td_->messages_manager_->on_dialog_usernames_updated(DialogId(channel_id), c->usernames, usernames);
if (c->usernames != usernames) {
if (c->is_update_supergroup_sent) {
on_channel_username_changed(c, channel_id, c->username, username);
on_channel_usernames_changed(c, channel_id, c->usernames, usernames);
}
c->username = std::move(username);
c->usernames = std::move(usernames);
c->is_username_changed = true;
c->is_changed = true;
}
}
void ContactsManager::on_channel_username_changed(const Channel *c, ChannelId channel_id, const string &old_username,
const string &new_username) {
void ContactsManager::on_channel_usernames_changed(const Channel *c, ChannelId channel_id,
const Usernames &old_usernames, const Usernames &new_usernames) {
bool have_channel_full = get_channel_full(channel_id) != nullptr;
if (old_username.empty() || new_username.empty()) {
if (old_usernames.is_empty() || new_usernames.is_empty()) {
// moving channel from private to public can change availability of chat members
invalidate_channel_full(channel_id, !c->is_slow_mode_enabled);
}
@ -14609,7 +14622,7 @@ Result<ContactsManager::BotData> ContactsManager::get_bot_data(UserId user_id) c
}
BotData bot_data;
bot_data.username = u->username;
bot_data.username = u->usernames.get_first_username();
bot_data.can_join_groups = u->can_join_groups;
bot_data.can_read_all_group_messages = u->can_read_all_group_messages;
bot_data.is_inline = u->is_inline_bot;
@ -15206,7 +15219,7 @@ bool ContactsManager::is_channel_public(ChannelId channel_id) const {
}
bool ContactsManager::is_channel_public(const Channel *c) {
return c != nullptr && (!c->username.empty() || c->has_location);
return c != nullptr && (!c->usernames.is_empty() || c->has_location);
}
ChannelType ContactsManager::get_channel_type(ChannelId channel_id) const {
@ -16479,7 +16492,8 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
auto old_join_to_send = get_channel_join_to_send(c);
auto old_join_request = get_channel_join_request(c);
on_update_channel_title(c, channel_id, std::move(channel.title_));
on_update_channel_username(c, channel_id, std::move(channel.username_));
on_update_channel_usernames(c, channel_id,
Usernames(std::move(channel.username_), std::move(channel.usernames_)));
on_update_channel_photo(c, channel_id, std::move(channel.photo_));
on_update_channel_default_permissions(c, channel_id, RestrictedRights(channel.default_banned_rights_));
on_update_channel_has_location(c, channel_id, channel.has_geo_);
@ -16554,7 +16568,10 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
}
on_update_channel_photo(c, channel_id, std::move(channel.photo_));
on_update_channel_status(c, channel_id, std::move(status));
on_update_channel_username(c, channel_id, std::move(channel.username_)); // uses status, must be called after
on_update_channel_usernames(
c, channel_id,
Usernames(std::move(channel.username_),
std::move(channel.usernames_))); // uses status, must be called after on_update_channel_status
on_update_channel_default_permissions(c, channel_id, RestrictedRights(channel.default_banned_rights_));
on_update_channel_has_location(c, channel_id, channel.has_geo_);
on_update_channel_noforwards(c, channel_id, channel.noforwards_);
@ -16656,7 +16673,7 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co
}
int32 unban_date = (channel.flags_ & CHANNEL_FLAG_HAS_UNBAN_DATE) != 0 ? channel.until_date_ : 0;
on_update_channel_status(c, channel_id, DialogParticipantStatus::Banned(unban_date));
// on_update_channel_username(c, channel_id, ""); // don't know if channel username is empty, so don't update it
// on_update_channel_usernames(c, channel_id, Usernames()); // don't know if channel usernames are empty, so don't update it
tl_object_ptr<telegram_api::chatBannedRights> banned_rights; // == nullptr
on_update_channel_default_permissions(c, channel_id, RestrictedRights(banned_rights));
// on_update_channel_has_location(c, channel_id, false);
@ -16827,8 +16844,8 @@ td_api::object_ptr<td_api::UserStatus> ContactsManager::get_user_status_object(U
td_api::object_ptr<td_api::updateUser> ContactsManager::get_update_unknown_user_object(UserId user_id) {
return td_api::make_object<td_api::updateUser>(td_api::make_object<td_api::user>(
user_id.get(), "", "", "", "", td_api::make_object<td_api::userStatusEmpty>(), nullptr, nullptr, false, false,
false, false, false, "", false, false, false, td_api::make_object<td_api::userTypeUnknown>(), "", false));
user_id.get(), "", "", nullptr, "", td_api::make_object<td_api::userStatusEmpty>(), nullptr, nullptr, false,
false, false, false, false, "", false, false, false, td_api::make_object<td_api::userTypeUnknown>(), "", false));
}
int64 ContactsManager::get_user_id_object(UserId user_id, const char *source) const {
@ -16861,9 +16878,9 @@ tl_object_ptr<td_api::user> ContactsManager::get_user_object(UserId user_id, con
auto emoji_status = u->last_sent_emoji_status.is_valid() ? u->emoji_status.get_emoji_status_object() : nullptr;
return make_tl_object<td_api::user>(
user_id.get(), u->first_name, u->last_name, u->username, u->phone_number, get_user_status_object(user_id, u),
get_profile_photo_object(td_->file_manager_.get(), u->photo), std::move(emoji_status), u->is_contact,
u->is_mutual_contact, u->is_verified, u->is_premium, u->is_support,
user_id.get(), u->first_name, u->last_name, u->usernames.get_usernames_object(), u->phone_number,
get_user_status_object(user_id, u), get_profile_photo_object(td_->file_manager_.get(), u->photo),
std::move(emoji_status), u->is_contact, u->is_mutual_contact, u->is_verified, u->is_premium, u->is_support,
get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_fake, u->is_received,
std::move(type), u->language_code, u->attach_menu_enabled);
}
@ -16991,8 +17008,8 @@ td_api::object_ptr<td_api::updateSupergroup> ContactsManager::get_update_unknown
auto min_channel = get_min_channel(channel_id);
bool is_megagroup = min_channel == nullptr ? false : min_channel->is_megagroup_;
return td_api::make_object<td_api::updateSupergroup>(td_api::make_object<td_api::supergroup>(
channel_id.get(), string(), 0, DialogParticipantStatus::Banned(0).get_chat_member_status_object(), 0, false,
false, false, !is_megagroup, false, false, !is_megagroup, false, false, string(), false, false));
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, string(), false, false));
}
int64 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char *source) const {
@ -17017,9 +17034,10 @@ tl_object_ptr<td_api::supergroup> ContactsManager::get_supergroup_object(Channel
return nullptr;
}
return td_api::make_object<td_api::supergroup>(
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, get_channel_join_to_send(c),
get_channel_join_request(c), c->is_slow_mode_enabled, !c->is_megagroup, c->is_gigagroup, c->is_verified,
channel_id.get(), c->usernames.get_usernames_object(), c->date,
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_verified,
get_restriction_reason_description(c->restriction_reasons), c->is_scam, c->is_fake);
}

View File

@ -39,6 +39,7 @@
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/telegram/Usernames.h"
#include "td/actor/actor.h"
#include "td/actor/MultiPromise.h"
@ -131,8 +132,8 @@ class ContactsManager final : public Actor {
void for_each_secret_chat_with_user(UserId user_id, const std::function<void(SecretChatId)> &f);
string get_user_username(UserId user_id) const;
string get_channel_username(ChannelId channel_id) const;
string get_user_first_username(UserId user_id) const;
string get_channel_first_username(ChannelId channel_id) const;
int32 get_secret_chat_date(SecretChatId secret_chat_id) const;
int32 get_secret_chat_ttl(SecretChatId secret_chat_id) const;
@ -176,7 +177,7 @@ class ContactsManager final : public Actor {
void on_update_profile_success(int32 flags, const string &first_name, const string &last_name, const string &about);
void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, string &&username);
void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, Usernames &&usernames);
void on_update_user_phone_number(UserId user_id, string &&phone_number);
void on_update_user_photo(UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo_ptr);
void on_update_user_emoji_status(UserId user_id, tl_object_ptr<telegram_api::EmojiStatus> &&emoji_status);
@ -199,7 +200,7 @@ class ContactsManager final : public Actor {
void on_update_chat_default_permissions(ChatId chat_id, RestrictedRights default_permissions, int32 version);
void on_update_chat_pinned_message(ChatId chat_id, MessageId pinned_message_id, int32 version);
void on_update_channel_username(ChannelId channel_id, string &&username);
void on_update_channel_usernames(ChannelId channel_id, Usernames &&usernames);
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);
@ -643,7 +644,7 @@ class ContactsManager final : public Actor {
struct User {
string first_name;
string last_name;
string username;
Usernames usernames;
string phone_number;
int64 access_hash = -1;
EmojiStatus emoji_status;
@ -845,7 +846,7 @@ class ContactsManager final : public Actor {
int64 access_hash = 0;
string title;
DialogPhoto photo;
string username;
Usernames usernames;
vector<RestrictionReason> restriction_reasons;
DialogParticipantStatus status = DialogParticipantStatus::Banned(0);
RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false};
@ -1078,6 +1079,7 @@ class ContactsManager final : public Actor {
static constexpr int32 USER_FLAG_IS_PREMIUM = 1 << 28;
static constexpr int32 USER_FLAG_ATTACH_MENU_ENABLED = 1 << 29;
static constexpr int32 USER_FLAG_HAS_EMOJI_STATUS = 1 << 30;
static constexpr int32 USER_FLAG_HAS_USERNAMES = 1 << 0;
static constexpr int32 USER_FULL_FLAG_IS_BLOCKED = 1 << 0;
static constexpr int32 USER_FULL_FLAG_HAS_ABOUT = 1 << 1;
@ -1114,7 +1116,7 @@ class ContactsManager final : public Actor {
static constexpr int32 CHANNEL_FLAG_USER_IS_CREATOR = 1 << 0;
static constexpr int32 CHANNEL_FLAG_USER_HAS_LEFT = 1 << 2;
static constexpr int32 CHANNEL_FLAG_IS_BROADCAST = 1 << 5;
static constexpr int32 CHANNEL_FLAG_IS_PUBLIC = 1 << 6;
static constexpr int32 CHANNEL_FLAG_HAS_USERNAME = 1 << 6;
static constexpr int32 CHANNEL_FLAG_IS_VERIFIED = 1 << 7;
static constexpr int32 CHANNEL_FLAG_IS_MEGAGROUP = 1 << 8;
static constexpr int32 CHANNEL_FLAG_IS_RESTRICTED = 1 << 9;
@ -1137,6 +1139,7 @@ class ContactsManager final : public Actor {
static constexpr int32 CHANNEL_FLAG_NOFORWARDS = 1 << 27;
static constexpr int32 CHANNEL_FLAG_JOIN_TO_SEND = 1 << 28;
static constexpr int32 CHANNEL_FLAG_JOIN_REQUEST = 1 << 29;
static constexpr int32 CHANNEL_FLAG_HAS_USERNAMES = 1 << 0;
static constexpr int32 CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 0;
static constexpr int32 CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT = 1 << 1;
@ -1262,7 +1265,7 @@ class ContactsManager final : public Actor {
void on_set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise);
void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name, string &&username);
void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name, Usernames &&usernames);
void on_update_user_phone_number(User *u, UserId user_id, string &&phone_number);
void on_update_user_photo(User *u, UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo,
const char *source);
@ -1322,7 +1325,7 @@ class ContactsManager final : public Actor {
tl_object_ptr<telegram_api::ChatPhoto> &&chat_photo_ptr);
void on_update_channel_photo(Channel *c, ChannelId channel_id, DialogPhoto &&photo, bool invalidate_photo_cache);
static void on_update_channel_title(Channel *c, ChannelId channel_id, string &&title);
void on_update_channel_username(Channel *c, ChannelId channel_id, string &&username);
void on_update_channel_usernames(Channel *c, ChannelId channel_id, Usernames &&usernames);
void on_update_channel_status(Channel *c, ChannelId channel_id, DialogParticipantStatus &&status);
static void on_update_channel_default_permissions(Channel *c, ChannelId channel_id,
RestrictedRights default_permissions);
@ -1346,8 +1349,8 @@ class ContactsManager final : public Actor {
void on_channel_status_changed(Channel *c, ChannelId channel_id, const DialogParticipantStatus &old_status,
const DialogParticipantStatus &new_status);
void on_channel_username_changed(const Channel *c, ChannelId channel_id, const string &old_username,
const string &new_username);
void on_channel_usernames_changed(const Channel *c, ChannelId channel_id, const Usernames &old_usernames,
const Usernames &new_usernames);
void remove_linked_channel_id(ChannelId channel_id);
ChannelId get_linked_channel_id(ChannelId channel_id) const;
@ -1837,7 +1840,7 @@ class ContactsManager final : public Actor {
bool are_contacts_loaded_ = false;
int32 next_contacts_sync_date_ = 0;
Hints contacts_hints_; // search contacts by first name, last name and username
Hints contacts_hints_; // search contacts by first name, last name and usernames
vector<Promise<Unit>> load_contacts_queries_;
MultiPromiseActor load_contact_users_multipromise_{"LoadContactUsersMultiPromiseActor"};
int32 saved_contact_count_ = -1;

View File

@ -1948,7 +1948,7 @@ void InlineQueriesManager::save_recently_used_bots() {
value += ',';
value_ids += ',';
}
value += td_->contacts_manager_->get_user_username(bot_user_id);
value += td_->contacts_manager_->get_user_first_username(bot_user_id);
value_ids += to_string(bot_user_id.get());
}
G()->td_db()->get_binlog_pmc()->set("recently_used_inline_bot_usernames", value);

View File

@ -19175,7 +19175,7 @@ Result<std::pair<string, bool>> MessagesManager::get_message_link(FullMessageId
CHECK(linked_d != nullptr);
CHECK(linked_dialog_id.get_type() == DialogType::Channel);
auto *linked_m = get_message_force(linked_d, linked_message_id, "get_public_message_link");
auto channel_username = td_->contacts_manager_->get_channel_username(linked_dialog_id.get_channel_id());
auto channel_username = td_->contacts_manager_->get_channel_first_username(linked_dialog_id.get_channel_id());
if (linked_m != nullptr && is_active_message_reply_info(linked_dialog_id, linked_m->reply_info) &&
linked_message_id.is_server() && have_input_peer(linked_dialog_id, AccessRights::Read) &&
!channel_username.empty()) {
@ -19192,7 +19192,7 @@ Result<std::pair<string, bool>> MessagesManager::get_message_link(FullMessageId
}
}
auto dialog_username = td_->contacts_manager_->get_channel_username(dialog_id.get_channel_id());
auto dialog_username = td_->contacts_manager_->get_channel_first_username(dialog_id.get_channel_id());
bool is_public = !dialog_username.empty();
if (m->content->get_type() == MessageContentType::VideoNote && is_broadcast_channel(dialog_id) && is_public) {
return std::make_pair(
@ -19236,7 +19236,7 @@ string MessagesManager::get_message_embedding_code(FullMessageId full_message_id
return {};
}
if (dialog_id.get_type() != DialogType::Channel ||
td_->contacts_manager_->get_channel_username(dialog_id.get_channel_id()).empty()) {
td_->contacts_manager_->get_channel_first_username(dialog_id.get_channel_id()).empty()) {
promise.set_error(Status::Error(
400, "Message embedding code is available only for messages in public supergroups and channel chats"));
return {};
@ -25914,7 +25914,7 @@ void MessagesManager::set_dialog_default_send_message_as_dialog_id(DialogId dial
break;
}
if (!is_broadcast_channel(message_sender_dialog_id) ||
td_->contacts_manager_->get_channel_username(message_sender_dialog_id.get_channel_id()).empty()) {
td_->contacts_manager_->get_channel_first_username(message_sender_dialog_id.get_channel_id()).empty()) {
return promise.set_error(Status::Error(400, "Message sender chat must be a public channel"));
}
break;
@ -26562,8 +26562,8 @@ void MessagesManager::send_secret_message(DialogId dialog_id, const Message *m,
make_tl_object<secret_api::decryptedMessage>(
flags, false /*ignored*/, random_id, m->ttl,
m->content->get_type() == MessageContentType::Text ? text->text : string(), std::move(media.decrypted_media_),
std::move(entities), td_->contacts_manager_->get_user_username(m->via_bot_user_id), m->reply_to_random_id,
-m->media_album_id),
std::move(entities), td_->contacts_manager_->get_user_first_username(m->via_bot_user_id),
m->reply_to_random_id, -m->media_album_id),
std::move(media.input_file_), Promise<Unit>());
}
@ -33510,25 +33510,30 @@ void MessagesManager::on_get_dialog_query_finished(DialogId dialog_id, Status &&
}
}
void MessagesManager::on_dialog_username_updated(DialogId dialog_id, const string &old_username,
const string &new_username) {
void MessagesManager::on_dialog_usernames_updated(DialogId dialog_id, const Usernames &old_usernames,
const Usernames &new_usernames) {
CHECK(dialog_id.is_valid());
auto d = get_dialog(dialog_id);
if (d != nullptr) {
update_dialogs_hints(d);
}
if (old_username != new_username) {
if (old_usernames != new_usernames) {
message_embedding_codes_[0].erase(dialog_id);
message_embedding_codes_[1].erase(dialog_id);
}
if (!old_username.empty() && old_username != new_username) {
resolved_usernames_.erase(clean_username(old_username));
inaccessible_resolved_usernames_.erase(clean_username(old_username));
if (!old_usernames.is_empty() && old_usernames != new_usernames) {
for (auto &username : old_usernames.get_active_usernames()) {
auto cleaned_username = clean_username(username);
resolved_usernames_.erase(cleaned_username);
inaccessible_resolved_usernames_.erase(cleaned_username);
}
}
if (!new_username.empty()) {
auto cleaned_username = clean_username(new_username);
if (!cleaned_username.empty()) {
resolved_usernames_[cleaned_username] = ResolvedUsername{dialog_id, Time::now() + USERNAME_CACHE_EXPIRE_TIME};
if (!new_usernames.is_empty()) {
for (auto &username : new_usernames.get_active_usernames()) {
auto cleaned_username = clean_username(username);
if (!cleaned_username.empty()) {
resolved_usernames_[cleaned_username] = ResolvedUsername{dialog_id, Time::now() + USERNAME_CACHE_EXPIRE_TIME};
}
}
}
}

View File

@ -59,6 +59,7 @@
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/telegram/Usernames.h"
#include "td/actor/actor.h"
#include "td/actor/MultiPromise.h"
@ -856,7 +857,7 @@ class MessagesManager final : public Actor {
void on_dialog_photo_updated(DialogId dialog_id);
void on_dialog_title_updated(DialogId dialog_id);
void on_dialog_username_updated(DialogId dialog_id, const string &old_username, const string &new_username);
void on_dialog_usernames_updated(DialogId dialog_id, const Usernames &old_usernames, const Usernames &new_usernames);
void on_dialog_default_permissions_updated(DialogId dialog_id);
void on_dialog_has_protected_content_updated(DialogId dialog_id);
@ -3621,7 +3622,7 @@ class MessagesManager final : public Actor {
Timeout reload_dialog_filters_timeout_;
Hints dialogs_hints_; // search dialogs by title and username
Hints dialogs_hints_; // search dialogs by title and usernames
FlatHashSet<FullMessageId, FullMessageIdHash> active_live_location_full_message_ids_;
bool are_active_live_location_messages_loaded_ = false;

View File

@ -52,13 +52,13 @@ void RecentDialogList::save_dialogs() const {
switch (dialog_id.get_type()) {
case DialogType::User:
if (!td_->contacts_manager_->is_user_contact(dialog_id.get_user_id())) {
username = td_->contacts_manager_->get_user_username(dialog_id.get_user_id());
username = td_->contacts_manager_->get_user_first_username(dialog_id.get_user_id());
}
break;
case DialogType::Chat:
break;
case DialogType::Channel:
username = td_->contacts_manager_->get_channel_username(dialog_id.get_channel_id());
username = td_->contacts_manager_->get_channel_first_username(dialog_id.get_channel_id());
break;
case DialogType::SecretChat:
break;

View File

@ -160,7 +160,7 @@ td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponso
if (!td_->contacts_manager_->is_user_bot(user_id)) {
break;
}
auto bot_username = td_->contacts_manager_->get_user_username(user_id);
auto bot_username = td_->contacts_manager_->get_user_first_username(user_id);
if (bot_username.empty()) {
break;
}

View File

@ -56,6 +56,7 @@
#include "td/telegram/TdDb.h"
#include "td/telegram/telegram_api.hpp"
#include "td/telegram/ThemeManager.h"
#include "td/telegram/Usernames.h"
#include "td/telegram/VoiceNotesManager.h"
#include "td/telegram/WebPagesManager.h"
@ -3181,7 +3182,8 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateUserStatus> upd
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateUserName> update, Promise<Unit> &&promise) {
td_->contacts_manager_->on_update_user_name(UserId(update->user_id_), std::move(update->first_name_),
std::move(update->last_name_), string());
std::move(update->last_name_),
Usernames{string(), std::move(update->usernames_)});
promise.set_value(Unit());
}

109
td/telegram/Usernames.cpp Normal file
View File

@ -0,0 +1,109 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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)
//
#include "td/telegram/Usernames.h"
#include "td/telegram/misc.h"
#include "td/telegram/secret_api.h"
namespace td {
Usernames::Usernames(string &&first_username, vector<telegram_api::object_ptr<telegram_api::username>> &&usernames) {
if (usernames.empty()) {
if (!first_username.empty()) {
active_usernames_.push_back(std::move(first_username));
editable_username_pos_ = 0;
}
return;
}
if (!first_username.empty() && usernames[0]->username_ != first_username) {
LOG(ERROR) << "Receive first username " << first_username << " with " << to_string(usernames);
return;
}
bool was_editable = false;
for (auto &username : usernames) {
if (username->username_.empty()) {
LOG(ERROR) << "Receive empty username in " << to_string(usernames);
return;
}
if (username->editable_) {
if (was_editable) {
LOG(ERROR) << "Receive two editable usernames in " << to_string(usernames);
return;
}
if (!username->active_) {
LOG(ERROR) << "Receive disabled editable usernames in " << to_string(usernames);
return;
}
was_editable = true;
}
}
if (!was_editable) {
LOG(ERROR) << "Receive no editable username in " << to_string(usernames);
return;
}
for (size_t i = 0; i < usernames.size(); i++) {
if (usernames[i]->active_) {
active_usernames_.push_back(std::move(usernames[i]->username_));
if (usernames[i]->editable_) {
editable_username_pos_ = narrow_cast<int32>(i);
}
} else {
disabled_usernames_.push_back(std::move(usernames[i]->username_));
}
}
CHECK(editable_username_pos_ != -1);
}
tl_object_ptr<td_api::usernames> Usernames::get_usernames_object() const {
if (is_empty()) {
return nullptr;
}
return make_tl_object<td_api::usernames>(vector<string>(active_usernames_), vector<string>(disabled_usernames_),
active_usernames_[editable_username_pos_]);
}
void Usernames::check_utf8_validness() {
for (auto &username : active_usernames_) {
if (!check_utf8(username)) {
LOG(ERROR) << "Have invalid active username \"" << username << '"';
*this = Usernames();
return;
}
}
for (auto &username : disabled_usernames_) {
if (!check_utf8(username)) {
LOG(ERROR) << "Have invalid disabled username \"" << username << '"';
*this = Usernames();
return;
}
}
}
bool operator==(const Usernames &lhs, const Usernames &rhs) {
return lhs.active_usernames_ == rhs.active_usernames_ && lhs.disabled_usernames_ == rhs.disabled_usernames_ &&
lhs.editable_username_pos_ == rhs.editable_username_pos_;
}
bool operator!=(const Usernames &lhs, const Usernames &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const Usernames &usernames) {
string_builder << "Usernames[";
if (!usernames.active_usernames_.empty()) {
string_builder << usernames.active_usernames_[usernames.editable_username_pos_] << ' '
<< usernames.active_usernames_;
}
if (!usernames.disabled_usernames_.empty()) {
string_builder << usernames.disabled_usernames_;
}
return string_builder << ']';
}
} // namespace td

110
td/telegram/Usernames.h Normal file
View File

@ -0,0 +1,110 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/tl_helpers.h"
namespace td {
class Usernames {
vector<string> active_usernames_;
vector<string> disabled_usernames_;
int32 editable_username_pos_ = -1;
friend bool operator==(const Usernames &lhs, const Usernames &rhs);
friend StringBuilder &operator<<(StringBuilder &string_builder, const Usernames &usernames);
void check_utf8_validness();
public:
Usernames() = default;
Usernames(string &&first_username, vector<telegram_api::object_ptr<telegram_api::username>> &&usernames);
td_api::object_ptr<td_api::usernames> get_usernames_object() const;
bool is_empty() const {
return editable_username_pos_ == -1;
}
string get_first_username() const {
if (is_empty()) {
return string();
}
return active_usernames_[0];
}
string get_editable_username() const {
if (is_empty()) {
return string();
}
return active_usernames_[editable_username_pos_];
}
const vector<string> &get_active_usernames() const {
return active_usernames_;
}
template <class StorerT>
void store(StorerT &storer) const {
CHECK(!is_empty())
CHECK(!active_usernames_.empty())
bool has_many_active_usernames = active_usernames_.size() > 0;
bool has_disabled_usernames = !disabled_usernames_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_many_active_usernames);
STORE_FLAG(has_disabled_usernames);
END_STORE_FLAGS();
if (has_many_active_usernames) {
td::store(active_usernames_, storer);
td::store(editable_username_pos_, storer);
} else {
td::store(active_usernames_[0], storer);
CHECK(editable_username_pos_ == 0);
}
if (has_disabled_usernames) {
td::store(disabled_usernames_, storer);
}
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
bool has_many_active_usernames;
bool has_disabled_usernames;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_many_active_usernames);
PARSE_FLAG(has_disabled_usernames);
END_PARSE_FLAGS();
if (has_many_active_usernames) {
td::parse(active_usernames_, parser);
td::parse(editable_username_pos_, parser);
CHECK(static_cast<size_t>(editable_username_pos_) < active_usernames_.size());
} else {
active_usernames_.resize(1);
td::parse(active_usernames_[0], parser);
editable_username_pos_ = 0;
}
if (has_disabled_usernames) {
td::parse(disabled_usernames_, parser);
}
check_utf8_validness();
}
};
bool operator==(const Usernames &lhs, const Usernames &rhs);
bool operator!=(const Usernames &lhs, const Usernames &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const Usernames &usernames);
} // namespace td

View File

@ -28,7 +28,6 @@ class Venue {
string type_;
friend bool operator==(const Venue &lhs, const Venue &rhs);
friend bool operator!=(const Venue &lhs, const Venue &rhs);
friend StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue);

View File

@ -2142,7 +2142,7 @@ unique_ptr<WebPageBlock> get_web_page_block(Td *td, tl_object_ptr<telegram_api::
LOG(INFO) << "Receive known min " << channel_id;
return td::make_unique<WebPageBlockChatLink>(td->contacts_manager_->get_channel_title(channel_id),
*td->contacts_manager_->get_channel_dialog_photo(channel_id),
td->contacts_manager_->get_channel_username(channel_id));
td->contacts_manager_->get_channel_first_username(channel_id));
} else {
bool has_access_hash = (channel->flags_ & telegram_api::channel::ACCESS_HASH_MASK) != 0;
return td::make_unique<WebPageBlockChatLink>(

View File

@ -250,9 +250,10 @@ class CliClient final : public Actor {
User &new_user = *new_user_ptr;
new_user.first_name = user.first_name_;
new_user.last_name = user.last_name_;
new_user.username = user.username_;
if (!new_user.username.empty()) {
username_to_user_id_[to_lower(new_user.username)] = user.id_;
if (user.usernames_ != nullptr) {
for (auto &username : user.usernames_->active_usernames_) {
username_to_user_id_[to_lower(username)] = user.id_;
}
}
}
@ -260,9 +261,6 @@ class CliClient final : public Actor {
const User *user = users_[user_id].get();
CHECK(user != nullptr);
log << user->first_name << " " << user->last_name << " #" << user_id;
if (!user->username.empty()) {
log << " @" << user->username;
}
}
void update_users(const td_api::users &users) {
@ -278,8 +276,10 @@ class CliClient final : public Actor {
FlatHashMap<string, int64> username_to_supergroup_id_;
void register_supergroup(const td_api::supergroup &supergroup) {
if (!supergroup.username_.empty()) {
username_to_supergroup_id_[to_lower(supergroup.username_)] = supergroup.id_;
if (supergroup.usernames_ != nullptr) {
for (auto &username : supergroup.usernames_->active_usernames_) {
username_to_supergroup_id_[to_lower(username)] = supergroup.id_;
}
}
}

View File

@ -297,7 +297,8 @@ class SetUsername final : public TestClinetTask {
CHECK(res->get_id() == td::td_api::user::ID);
auto user = td::move_tl_object_as<td::td_api::user>(res);
self_id_ = user->id_;
if (user->username_ != username_) {
auto current_username = user->usernames_ != nullptr ? user->usernames_->editable_username_ : td::string();
if (current_username != username_) {
LOG(INFO) << "SET USERNAME: " << username_;
send_query(td::make_tl_object<td::td_api::setUsername>(username_), [this](auto res) {
CHECK(res->get_id() == td::td_api::ok::ID);