Support t.me/+phonenumber links.

This commit is contained in:
John Preston 2022-03-01 14:44:29 +03:00
parent f7957a8903
commit bea2cfd363
6 changed files with 81 additions and 21 deletions

View file

@ -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.";

View file

@ -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<LocalUrlHandler> &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;

View file

@ -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) {

View file

@ -190,6 +190,7 @@ public:
void enumerateUsers(Fn<void(not_null<UserData*>)> action) const;
void enumerateGroups(Fn<void(not_null<PeerData*>)> action) const;
void enumerateChannels(Fn<void(not_null<ChannelData*>)> action) const;
[[nodiscard]] UserData *userByPhone(const QString &phone) const;
[[nodiscard]] PeerData *peerByUsername(const QString &username) const;
[[nodiscard]] not_null<History*> history(PeerId peerId);

View file

@ -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<QString>(&info.usernameOrId)) {
resolveUsername(*username, [=](not_null<PeerData*> peer) {
if (!info.phone.isEmpty()) {
resolvePhone(info.phone, [=](not_null<PeerData*> peer) {
showPeerByLinkResolved(peer, info);
});
} else if (const auto name = std::get_if<QString>(&info.usernameOrId)) {
resolveUsername(*name, [=](not_null<PeerData*> peer) {
showPeerByLinkResolved(peer, info);
});
} else if (const auto id = std::get_if<ChannelId>(&info.usernameOrId)) {
@ -186,6 +191,29 @@ void SessionNavigation::showPeerByLink(const PeerByLinkInfo &info) {
}
}
void SessionNavigation::resolvePhone(
const QString &phone,
Fn<void(not_null<PeerData*>)> 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<void(not_null<PeerData*>)> 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<void(not_null<PeerData*>)> 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<void(not_null<ChannelData*>)> done) {

View file

@ -173,6 +173,7 @@ public:
using RepliesByLinkInfo = std::variant<v::null_t, CommentId, ThreadId>;
struct PeerByLinkInfo {
std::variant<QString, ChannelId> usernameOrId;
QString phone;
MsgId messageId = ShowAtUnreadMsgId;
RepliesByLinkInfo repliesInfo;
QString startToken;
@ -230,6 +231,9 @@ public:
private:
void resolvePhone(
const QString &phone,
Fn<void(not_null<PeerData*>)> done);
void resolveUsername(
const QString &username,
Fn<void(not_null<PeerData*>)> done);
@ -237,6 +241,10 @@ private:
ChannelId channelId,
Fn<void(not_null<ChannelData*>)> done);
void resolveDone(
const MTPcontacts_ResolvedPeer &result,
Fn<void(not_null<PeerData*>)> done);
void showPeerByLinkResolved(
not_null<PeerData*> peer,
const PeerByLinkInfo &info);