feat: support tg://chat?id= links

This commit is contained in:
AlexeyZavar
2025-10-04 22:43:01 +03:00
parent 2647cefdcc
commit 67c946f24a
8 changed files with 184 additions and 79 deletions
+40 -2
View File
@@ -46,10 +46,48 @@ bool ResolveUser(
return true;
}
searchById(
searchUserById(
userId,
&controller->session(),
[=](const QString &title, UserData *data)
[=](const QString &title, PeerData *data)
{
if (data) {
controller->showPeerInfo(data);
return;
}
Core::App().hideMediaView();
Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage()));
}
);
return true;
}
bool ResolveChat(
Window::SessionController *controller,
const Match &match,
const QVariant &context) {
if (!controller) {
return false;
}
const auto params = url_parse_params(
match->captured(1),
qthelp::UrlParamNameTransform::ToLower);
const auto chatId = params.value(qsl("id")).toLongLong();
if (!chatId) {
return false;
}
const auto peer = controller->session().data().peerLoaded(static_cast<PeerId>(chatId));
if (peer != nullptr) {
controller->showPeerInfo(peer);
return true;
}
searchChatById(
chatId,
&controller->session(),
[=](const QString &title, PeerData *data)
{
if (data) {
controller->showPeerInfo(data);
@@ -18,6 +18,11 @@ bool ResolveUser(
const Match &match,
const QVariant &context);
bool ResolveChat(
Window::SessionController *controller,
const Match &match,
const QVariant &context);
bool HandleAyu(
Window::SessionController *controller,
const Match &match,
@@ -221,10 +221,10 @@ void ActionStickerPackAuthor::searchAuthor(ID authorId) {
const auto session = _session;
const auto weak = base::make_weak(this);
searchById(
searchUserById(
authorId,
session,
[session, weak, authorId](const QString &username, UserData *user)
[session, weak, authorId](const QString &username, PeerData *user)
{
if (!weak) {
LOG(("ContextActionStickerAuthor: searchById callback after destruction"));
@@ -39,6 +39,7 @@
#include "ayu/ayu_state.h"
#include "ayu/data/messages_storage.h"
#include "ayu/features/filters/filters_controller.h"
#include "data/data_chat.h"
#include "data/data_poll.h"
#include "data/data_saved_sublist.h"
#include "lang/lang_text_entity.h"
@@ -47,8 +48,8 @@
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "unicode/regex.h"
namespace {
constexpr auto usernameResolverBotId = 7424190611L;
@@ -102,7 +103,8 @@ bool isExteraPeer(ID peerId) {
}
bool isSupporterPeer(ID peerId) {
return RCManager::getInstance().supporters().contains(peerId) || RCManager::getInstance().supporterChannels().contains(peerId);
return RCManager::getInstance().supporters().contains(peerId) || RCManager::getInstance().supporterChannels().
contains(peerId);
}
bool isCustomBadgePeer(ID peerId) {
@@ -135,8 +137,9 @@ rpl::producer<Info::Profile::Badge::Content> ExteraBadgeTypeFromPeer(not_null<Pe
return rpl::single(Info::Profile::Badge::Content{Info::Profile::BadgeType::None});
}
Fn<void()> badgeClickHandler(not_null<PeerData *> peer) {
return [=] {
Fn<void()> badgeClickHandler(not_null<PeerData*> peer) {
return [=]
{
const auto isCustomBadge = isCustomBadgePeer(getBareID(peer));
const auto isExtera = isExteraPeer(getBareID(peer));
const auto isSupporter = isSupporterPeer(getBareID(peer));
@@ -596,7 +599,7 @@ int getScheduleTime(int64 sumSize) {
return time;
}
bool isMessageSavable(const not_null<HistoryItem *> item) {
bool isMessageSavable(const not_null<HistoryItem*> item) {
const auto &settings = AyuSettings::getInstance();
if (!settings.saveDeletedMessages) {
@@ -618,7 +621,11 @@ void processMessageDelete(not_null<HistoryItem*> item) {
}
}
void resolveUser(ID userId, const QString &username, Main::Session *session, const UsernameResolverCallback &callback) {
void resolvePeer(
const QString &peerId,
const QString &username,
Main::Session *session,
const UsernameResolverCallback &callback) {
auto normalized = username.trimmed().toLower();
if (normalized.isEmpty()) {
callback(QString(), nullptr);
@@ -642,11 +649,9 @@ void resolveUser(ID userId, const QString &username, Main::Session *session, con
auto &data = result.c_contacts_resolvedPeer();
session->data().processUsers(data.vusers());
session->data().processChats(data.vchats());
const auto peer = session->data().peerLoaded(
peerFromMTP(data.vpeer()));
if (const auto user = peer ? peer->asUser() : nullptr) {
if ((user->id.value & PeerId::kChatTypeMask) == userId) {
callback(normalized, user);
if (const auto peer = session->data().peerLoaded(peerFromMTP(data.vpeer()))) {
if (QString::number(peer->id.value & PeerId::kChatTypeMask) == peerId) {
callback(normalized, peer);
return;
}
}
@@ -658,26 +663,15 @@ void resolveUser(ID userId, const QString &username, Main::Session *session, con
}).send();
}
void searchUser(long long userId, Main::Session *session, bool searchUserFlag, const UsernameResolverCallback &callback) {
void searchPeerInner(const QString &peerId, Main::Session *session, const UsernameResolverCallback &callback) {
if (!session) {
callback(QString(), nullptr);
return;
}
const auto bot = session->data().userLoaded(usernameResolverBotId);
if (!bot) {
if (searchUserFlag) {
resolveUser(usernameResolverBotId,
usernameResolverBotUsername,
session,
[=](const QString &title, UserData *data)
{
searchUser(userId, session, false, callback);
});
} else {
callback(QString(), nullptr);
}
callback(QString(), nullptr);
return;
}
@@ -686,7 +680,7 @@ void searchUser(long long userId, Main::Session *session, bool searchUserFlag, c
bot->inputUser,
MTP_inputPeerEmpty(),
MTPInputGeoPoint(),
MTP_string(QString::number(userId)),
MTP_string(peerId),
MTP_string("")
)).done([=](const MTPmessages_BotResults &result)
{
@@ -744,13 +738,13 @@ void searchUser(long long userId, Main::Session *session, bool searchUserFlag, c
continue;
}
ID id = 0; // 🆔
QString id; // 🆔
QString title; // 🏷
QString username; // 📧
for (auto &line : text.split('\n')) {
if (line.startsWith("🆔")) {
id = line.mid(line.indexOf(": ") + 2).toLongLong();
id = line.mid(line.indexOf(": ") + 2).trimmed();
} else if (line.startsWith("🏷")) {
title = line.mid(line.indexOf(": ") + 2);
} else if (line.startsWith("📧")) {
@@ -758,27 +752,33 @@ void searchUser(long long userId, Main::Session *session, bool searchUserFlag, c
}
}
if (id == 0 || id != userId) {
if (id.isEmpty() || id != peerId) {
continue;
}
if (id.startsWith("-100")) {
id = id.mid(4);
}
if (!username.isEmpty()) {
resolveUser(id,
username,
session,
[=](const QString &titleInner, UserData *data)
{
if (data) {
callback(titleInner, data);
} else {
callback(title, nullptr);
}
});
resolvePeer(
id,
username,
session,
[=](const QString &titleInner, PeerData *data)
{
if (data) {
callback(titleInner, data);
} else {
callback(title, nullptr);
}
});
return;
}
if (!title.isEmpty()) {
callback(title, nullptr);
return;
}
}
@@ -789,28 +789,79 @@ void searchUser(long long userId, Main::Session *session, bool searchUserFlag, c
}).handleAllErrors().send();
}
void searchById(ID userId, Main::Session *session, const UsernameResolverCallback &callback) {
void searchPeer(const QString &peerId, Main::Session *session, const UsernameResolverCallback &callback) {
if (!session) {
callback(QString(), nullptr);
return;
}
if (session->data().userLoaded(usernameResolverBotId)) {
searchPeerInner(peerId, session, callback);
} else {
resolvePeer(
QString::number(usernameResolverBotId),
usernameResolverBotUsername,
session,
[=](const QString &title, PeerData *data)
{
searchPeerInner(peerId, session, callback);
});
}
}
void searchUserById(ID userId, Main::Session *session, const UsernameResolverCallback &callback) {
if (userId == 0 || !session) {
callback(QString(), nullptr);
return;
}
if (const auto dataLoaded = session->data().userLoaded(userId)) {
callback(dataLoaded->username(), dataLoaded);
if (const auto userLoaded = session->data().userLoaded(userId)) {
callback(userLoaded->username(), userLoaded);
return;
}
searchUser(userId,
session,
true,
[=](const QString &title, UserData *data)
{
if (data && data->accessHash()) {
callback(title, data);
} else {
callback(QString(), nullptr);
}
});
searchPeer(
QString::number(userId),
session,
[=](const QString &title, PeerData *data)
{
if (data) {
if (const auto user = data->asUser(); user->accessHash()) {
callback(title, user);
return;
}
}
callback(QString(), nullptr);
});
}
void searchChatById(ID chatId, Main::Session *session, const UsernameResolverCallback &callback) {
if (chatId == 0 || !session) {
callback(QString(), nullptr);
return;
}
if (const auto channelLoaded = session->data().channelLoaded(chatId)) {
callback(channelLoaded->username(), channelLoaded);
return;
}
if (const auto chatLoaded = session->data().chatLoaded(chatId)) {
callback(chatLoaded->username(), chatLoaded);
return;
}
searchPeer(
QString("-100") + QString::number(chatId),
session,
[=](const QString &title, PeerData *data)
{
if (data && (data->isChat() || data->isChannel())) {
callback(title, data);
} else {
callback(QString(), nullptr);
}
});
}
ID getUserIdFromPackId(uint64 id) {
@@ -867,20 +918,27 @@ void resolveAllChats(const std::map<long long, QString> &peers) {
for (const auto &[id, username] : peers) {
std::latch latch(1);
auto onSuccess = [=, &latch](const MTPChatInvite &invite) {
invite.match([=](const MTPDchatInvite &data) {},
[=](const MTPDchatInviteAlready &data) {
if (const auto chat = session->data().processChat(data.vchat())) {
if (const auto channel = chat->asChannel()) {
channel->clearInvitePeek();
}
}
}, [=](const MTPDchatInvitePeek &data) {});
auto onSuccess = [=, &latch](const MTPChatInvite &invite)
{
invite.match([=](const MTPDchatInvite &data)
{
},
[=](const MTPDchatInviteAlready &data)
{
if (const auto chat = session->data().processChat(data.vchat())) {
if (const auto channel = chat->asChannel()) {
channel->clearInvitePeek();
}
}
},
[=](const MTPDchatInvitePeek &data)
{
});
latch.count_down();
};
auto onFail = [=, &latch](const MTP::Error &error) {
auto onFail = [=, &latch](const MTP::Error &error)
{
if (MTP::IsFloodError(error.type())) {
std::this_thread::sleep_for(std::chrono::seconds(20));
}
@@ -894,12 +952,12 @@ void resolveAllChats(const std::map<long long, QString> &peers) {
});
}
not_null<Main::Session *> currentSession() {
not_null<Main::Session*> currentSession() {
return &Core::App().domain().active().session();
}
template<typename T>
PeerData* getPeerFromDialogId(T id) {
PeerData *getPeerFromDialogId(T id) {
for (const auto &[index, account] : Core::App().domain().accounts()) {
if (const auto session = account->maybeSession()) {
PeerData *from = session->data().userLoaded(id);
@@ -919,10 +977,10 @@ PeerData* getPeerFromDialogId(T id) {
return nullptr;
}
PeerData* getPeerFromDialogId(ID id) {
PeerData *getPeerFromDialogId(ID id) {
return getPeerFromDialogId<ID>(id);
}
PeerData* getPeerFromDialogId(unsigned long long id) {
PeerData *getPeerFromDialogId(unsigned long long id) {
return getPeerFromDialogId<unsigned long long>(id);
}
@@ -14,7 +14,7 @@
#include "dialogs/dialogs_main_list.h"
#include "info/profile/info_profile_badge.h"
using UsernameResolverCallback = Fn<void(const QString &, UserData *)>;
using UsernameResolverCallback = Fn<void(const QString &, PeerData *)>;
Main::Session *getSession(ID userId);
void dispatchToMainThread(const std::function<void()> &callback, int delay = 0);
@@ -56,8 +56,8 @@ int getScheduleTime(int64 sumSize);
bool isMessageSavable(not_null<HistoryItem *> item);
void processMessageDelete(not_null<HistoryItem *> item);
void searchById(ID userId, Main::Session *session, bool retry, const UsernameResolverCallback &callback);
void searchById(ID userId, Main::Session *session, const UsernameResolverCallback &callback);
void searchUserById(ID userId, Main::Session *session, const UsernameResolverCallback &callback);
void searchChatById(ID chatId, Main::Session *session, const UsernameResolverCallback &callback);
ID getUserIdFromPackId(uint64 id);
@@ -781,10 +781,10 @@ void StickerSetBox::updateButtons() {
return;
}
searchById(
searchUserById(
innerId,
session,
[session, weak, innerId](const QString &username, UserData *user)
[session, weak, innerId](const QString &username, PeerData *user)
{
if (!weak) {
return;
@@ -1679,6 +1679,10 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
u"^user\\?(.+)(#|$)"_q,
AyuUrlHandlers::ResolveUser
},
{
u"^chat\\?(.+)(#|$)"_q,
AyuUrlHandlers::ResolveChat
},
{
u"^ayu(/?.+)?(#|$)"_q,
AyuUrlHandlers::HandleAyu
@@ -130,10 +130,10 @@ ResolvePhoneAction::ResolvePhoneAction(
const auto weak = base::make_weak(this);
const auto session = &controller->session();
searchById(
searchUserById(
possibleId,
session,
[weak](const QString &username, UserData *user)
[weak](const QString &username, PeerData *user)
{
if (!weak) {
return;