From bea2cfd363945e1dfb0b4a11a4d5cf3746274e86 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 1 Mar 2022 14:44:29 +0300 Subject: [PATCH] Support t.me/+phonenumber links. --- Telegram/Resources/langs/lang.strings | 1 + .../SourceFiles/core/local_url_handlers.cpp | 21 ++++--- Telegram/SourceFiles/data/data_session.cpp | 12 ++++ Telegram/SourceFiles/data/data_session.h | 1 + .../window/window_session_controller.cpp | 59 ++++++++++++++----- .../window/window_session_controller.h | 8 +++ 6 files changed, 81 insertions(+), 21 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0fe3161d6..c451a2cb5 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -304,6 +304,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_username_bad_symbols" = "Only a-z, 0-9, and underscores allowed."; "lng_username_available" = "This username is available."; "lng_username_not_found" = "User @{user} not found."; +"lng_username_by_phone_not_found" = "User {phone} not found."; "lng_username_link_willbe" = "This link will open a chat with you:"; "lng_username_link" = "This link opens a chat with you:"; "lng_username_copied" = "Link copied to clipboard."; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 667a54f37..8ad1a71d6 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -251,7 +251,7 @@ bool ShowWallPaper( params); } -bool ResolveUsername( +bool ResolveUsernameOrPhone( Window::SessionController *controller, const Match &match, const QVariant &context) { @@ -262,16 +262,20 @@ bool ResolveUsername( match->captured(1), qthelp::UrlParamNameTransform::ToLower); const auto domain = params.value(qsl("domain")); - const auto valid = [](const QString &domain) { + const auto phone = params.value(qsl("phone")); + const auto validDomain = [](const QString &domain) { return qthelp::regex_match( qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, {} ).valid(); }; + const auto validPhone = [](const QString &phone) { + return qthelp::regex_match(qsl("^[0-9]+$"), phone, {}).valid(); + }; if (domain == qsl("telegrampassport")) { return ShowPassportForm(controller, params); - } else if (!valid(domain)) { + } else if (!validDomain(domain) && !validPhone(phone)) { return false; } auto start = qsl("start"); @@ -295,7 +299,7 @@ bool ResolveUsername( const auto threadParam = params.value(qsl("thread")); const auto threadId = threadParam.toInt(); const auto gameParam = params.value(qsl("game")); - if (!gameParam.isEmpty() && valid(gameParam)) { + if (!gameParam.isEmpty() && validDomain(gameParam)) { startToken = gameParam; post = ShowAtGameShareMsgId; } @@ -303,6 +307,7 @@ bool ResolveUsername( using Navigation = Window::SessionNavigation; controller->showPeerByLink(Navigation::PeerByLinkInfo{ .usernameOrId = domain, + .phone = phone, .messageId = post, .repliesInfo = commentId ? Navigation::RepliesByLinkInfo{ @@ -682,7 +687,7 @@ const std::vector &LocalUrlHandlers() { }, { qsl("^resolve/?\\?(.+)(#|$)"), - ResolveUsername + ResolveUsernameOrPhone }, { qsl("^privatepost/?\\?(.+)(#|$)"), @@ -732,7 +737,9 @@ QString TryConvertUrlToLocal(QString url) { auto telegramMeMatch = regex_match(qsl("^(https?://)?(www\\.)?(telegram\\.(me|dog)|t\\.me)/(.+)$"), url, matchOptions); if (telegramMeMatch) { auto query = telegramMeMatch->capturedView(5); - if (auto joinChatMatch = regex_match(qsl("^(joinchat/|\\+|\\%20)([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), query, matchOptions)) { + if (auto phoneMatch = regex_match(qsl("^\\+([0-9]+)(\\?|$)"), query, matchOptions)) { + return qsl("tg://resolve?phone=") + phoneMatch->captured(1); + } else if (auto joinChatMatch = regex_match(qsl("^(joinchat/|\\+|\\%20)([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), query, matchOptions)) { return qsl("tg://join?invite=") + url_encode(joinChatMatch->captured(2)); } else if (auto stickerSetMatch = regex_match(qsl("^addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), query, matchOptions)) { return qsl("tg://addstickers?set=") + url_encode(stickerSetMatch->captured(1)); @@ -778,7 +785,7 @@ QString TryConvertUrlToLocal(QString url) { if (auto postMatch = regex_match(qsl("^/\\d+/?(?:\\?|$)"), usernameMatch->captured(2))) { postParam = qsl("&post=") + usernameMatch->captured(3); } - return qsl("tg://resolve/?domain=") + url_encode(usernameMatch->captured(1)) + postParam + (params.isEmpty() ? QString() : '&' + params); + return qsl("tg://resolve?domain=") + url_encode(usernameMatch->captured(1)) + postParam + (params.isEmpty() ? QString() : '&' + params); } } return url; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 34b435a6a..504a3984d 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -914,6 +914,18 @@ void Session::unregisterInvitedToCallUser( } } +UserData *Session::userByPhone(const QString &phone) const { + const auto pname = phone.trimmed(); + for (const auto &[peerId, peer] : _peers) { + if (const auto user = peer->asUser()) { + if (user->phone() == pname) { + return user; + } + } + } + return nullptr; +} + PeerData *Session::peerByUsername(const QString &username) const { const auto uname = username.trimmed(); for (const auto &[peerId, peer] : _peers) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 4146398b7..1cb9d638a 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -190,6 +190,7 @@ public: void enumerateUsers(Fn)> action) const; void enumerateGroups(Fn)> action) const; void enumerateChannels(Fn)> action) const; + [[nodiscard]] UserData *userByPhone(const QString &phone) const; [[nodiscard]] PeerData *peerByUsername(const QString &username) const; [[nodiscard]] not_null history(PeerId peerId); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 0c5f88d02..c2ec19125 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" +#include "ui/text/format_values.h" // Ui::FormatPhone. #include "ui/delayed_activation.h" #include "ui/chat/message_bubble.h" #include "ui/chat/chat_style.h" @@ -175,8 +176,12 @@ Main::Session &SessionNavigation::session() const { void SessionNavigation::showPeerByLink(const PeerByLinkInfo &info) { Core::App().hideMediaView(); - if (const auto username = std::get_if(&info.usernameOrId)) { - resolveUsername(*username, [=](not_null peer) { + if (!info.phone.isEmpty()) { + resolvePhone(info.phone, [=](not_null peer) { + showPeerByLinkResolved(peer, info); + }); + } else if (const auto name = std::get_if(&info.usernameOrId)) { + resolveUsername(*name, [=](not_null peer) { showPeerByLinkResolved(peer, info); }); } else if (const auto id = std::get_if(&info.usernameOrId)) { @@ -186,6 +191,29 @@ void SessionNavigation::showPeerByLink(const PeerByLinkInfo &info) { } } +void SessionNavigation::resolvePhone( + const QString &phone, + Fn)> done) { + if (const auto peer = _session->data().userByPhone(phone)) { + done(peer); + return; + } + _session->api().request(base::take(_resolveRequestId)).cancel(); + _resolveRequestId = _session->api().request(MTPcontacts_ResolvePhone( + MTP_string(phone) + )).done([=](const MTPcontacts_ResolvedPeer &result) { + resolveDone(result, done); + }).fail([=](const MTP::Error &error) { + _resolveRequestId = 0; + if (error.code() == 400) { + show(Ui::MakeInformBox(tr::lng_username_by_phone_not_found( + tr::now, + lt_phone, + Ui::FormatPhone(phone)))); + } + }).send(); +} + void SessionNavigation::resolveUsername( const QString &username, Fn)> done) { @@ -197,18 +225,7 @@ void SessionNavigation::resolveUsername( _resolveRequestId = _session->api().request(MTPcontacts_ResolveUsername( MTP_string(username) )).done([=](const MTPcontacts_ResolvedPeer &result) { - _resolveRequestId = 0; - Ui::hideLayer(); - if (result.type() != mtpc_contacts_resolvedPeer) { - return; - } - - const auto &d(result.c_contacts_resolvedPeer()); - _session->data().processUsers(d.vusers()); - _session->data().processChats(d.vchats()); - if (const auto peerId = peerFromMTP(d.vpeer())) { - done(_session->data().peer(peerId)); - } + resolveDone(result, done); }).fail([=](const MTP::Error &error) { _resolveRequestId = 0; if (error.code() == 400) { @@ -218,6 +235,20 @@ void SessionNavigation::resolveUsername( }).send(); } +void SessionNavigation::resolveDone( + const MTPcontacts_ResolvedPeer &result, + Fn)> done) { + _resolveRequestId = 0; + Ui::hideLayer(); + result.match([&](const MTPDcontacts_resolvedPeer &data) { + _session->data().processUsers(data.vusers()); + _session->data().processChats(data.vchats()); + if (const auto peerId = peerFromMTP(data.vpeer())) { + done(_session->data().peer(peerId)); + } + }); +} + void SessionNavigation::resolveChannelById( ChannelId channelId, Fn)> done) { diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index f368e28d5..207dc8a32 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -173,6 +173,7 @@ public: using RepliesByLinkInfo = std::variant; struct PeerByLinkInfo { std::variant usernameOrId; + QString phone; MsgId messageId = ShowAtUnreadMsgId; RepliesByLinkInfo repliesInfo; QString startToken; @@ -230,6 +231,9 @@ public: private: + void resolvePhone( + const QString &phone, + Fn)> done); void resolveUsername( const QString &username, Fn)> done); @@ -237,6 +241,10 @@ private: ChannelId channelId, Fn)> done); + void resolveDone( + const MTPcontacts_ResolvedPeer &result, + Fn)> done); + void showPeerByLinkResolved( not_null peer, const PeerByLinkInfo &info);