Add class td_api::InternalLinkType and parse some intenal links.

This commit is contained in:
levlam 2021-05-25 17:06:27 +03:00
parent 5f0b24926f
commit acd6caae9b
4 changed files with 215 additions and 2 deletions

View File

@ -2998,6 +2998,15 @@ chatReportReasonFake = ChatReportReason;
chatReportReasonCustom = ChatReportReason;
//@class InternalLinkType @description Describes an internal t.me or tg: link, which must be processed by the app in a special way
//@description The link is a link to a background. Call searchBackground with a given background name to process the link @background_name Name of the background
internalLinkTypeBackground background_name:string = InternalLinkType;
//@description The link is a link to a Telegram message. Call getMessageLinkInfo to process the link
internalLinkTypeMessage = InternalLinkType;
//@description Contains an HTTPS link to a message in a supergroup or channel @link Message link @is_public True, if the link will work for non-members of the chat
messageLink link:string is_public:Bool = MessageLink;

View File

@ -6,12 +6,15 @@
//
#include "td/telegram/LinkManager.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/HttpUrl.h"
#include "td/utils/logging.h"
@ -19,6 +22,32 @@
namespace td {
class LinkManager::InternalLinkBackground : public InternalLink {
string background_name_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeBackground>(background_name_);
}
InternalLinkType get_type() const final {
return InternalLinkType::Background;
}
public:
explicit InternalLinkBackground(string background_name) : background_name_(background_name) {
}
};
class LinkManager::InternalLinkMessage : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeMessage>();
}
InternalLinkType get_type() const final {
return InternalLinkType::Message;
}
};
class RequestUrlAuthQuery : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> promise_;
string url_;
@ -203,6 +232,145 @@ Result<string> LinkManager::check_link(Slice link) {
return http_url.get_url();
}
LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
LinkInfo result;
if (link.empty()) {
return result;
}
bool is_tg = false;
if (tolower_begins_with(link, "tg:")) {
link.remove_prefix(3);
if (begins_with(link, "//")) {
link.remove_prefix(2);
}
is_tg = true;
}
auto r_http_url = parse_url(link);
if (r_http_url.is_error()) {
return result;
}
auto http_url = r_http_url.move_as_ok();
if (!http_url.userinfo_.empty() || http_url.is_ipv6_) {
return result;
}
if (is_tg) {
if (tolower_begins_with(link, "http://") || http_url.protocol_ == HttpUrl::Protocol::Https ||
http_url.specified_port_ != 0) {
return result;
}
result.is_internal_ = true;
result.is_tg_ = true;
result.query_ = link;
return result;
} else {
if (http_url.port_ != 80 && http_url.port_ != 443) {
return result;
}
vector<Slice> t_me_urls{Slice("t.me"), Slice("telegram.me"), Slice("telegram.dog")};
string cur_t_me_url = G()->shared_config().get_option_string("t_me_url");
if (tolower_begins_with(cur_t_me_url, "http://") || tolower_begins_with(cur_t_me_url, "https://")) {
Slice t_me_url = cur_t_me_url;
t_me_url = t_me_url.substr(t_me_url[4] == 's' ? 8 : 7);
if (!td::contains(t_me_urls, t_me_url)) {
t_me_urls.push_back(t_me_url);
}
}
for (auto t_me_url : t_me_urls) {
if (http_url.host_ == t_me_url) {
result.is_internal_ = true;
result.is_tg_ = false;
result.query_ = http_url.query_;
return result;
}
}
}
return result;
}
unique_ptr<LinkManager::InternalLink> LinkManager::parse_internal_link(Slice link) {
auto info = get_link_info(link);
if (!info.is_internal_) {
return nullptr;
}
if (info.is_tg_) {
return parse_tg_link_query(info.query_);
} else {
return parse_t_me_link_query(info.query_);
}
}
unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice query) {
const auto url_query = parse_url_query(query);
const auto &path = url_query.path_;
auto copy_arg = [&](Slice name) {
auto arg = url_query.get_arg(name);
if (arg.empty()) {
return string();
}
return PSTRING() << name << '=' << url_encode(arg);
};
if (path.size() == 1 && path[0] == "resolve") {
// resolve?domain=username&post=12345&single
if (!url_query.get_arg("domain").empty() && !url_query.get_arg("post").empty()) {
return td::make_unique<InternalLinkMessage>();
}
} else if (path.size() == 1 && path[0] == "privatepost") {
// privatepost?channel=123456789&msg_id=12345
if (!url_query.get_arg("channel").empty() && !url_query.get_arg("msg_id").empty()) {
return td::make_unique<InternalLinkMessage>();
}
} else if (path.size() == 1 && path[0] == "bg") {
// bg?color=<color>
// bg?gradient=<hex_color>-<hex_color>&rotation=...
// bg?gradient=<hex_color>~<hex_color>~<hex_color>~<hex_color>
// bg?slug=<background_name>&mode=blur+motion
// bg?slug=<pattern_name>&intensity=...&bg_color=...&mode=blur+motion
if (!url_query.get_arg("color").empty()) {
return td::make_unique<InternalLinkBackground>(url_query.get_arg("color").str());
}
if (!url_query.get_arg("gradient").empty()) {
return td::make_unique<InternalLinkBackground>(PSTRING() << url_encode(url_query.get_arg("gradient")) << '?'
<< copy_arg("rotation"));
}
if (!url_query.get_arg("slug").empty()) {
return td::make_unique<InternalLinkBackground>(PSTRING() << url_encode(url_query.get_arg("slug")) << '?'
<< copy_arg("mode") << copy_arg("intensity")
<< copy_arg("bg_color") << copy_arg("rotation"));
}
}
return nullptr;
}
unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice query) {
CHECK(query[0] == '/');
const auto url_query = parse_url_query(query);
const auto &path = url_query.path_;
if (path.size() == 3 && path[0] == "c") {
// /c/123456789/12345
return td::make_unique<InternalLinkMessage>();
} else if (path.size() == 2 && path[0] == "bg") {
// /bg/<hex_color>
// /bg/<hex_color>-<hex_color>?rotation=...
// /bg/<hex_color>~<hex_color>~<hex_color>~<hex_color>
// /bg/<background_name>?mode=blur+motion
// /bg/<pattern_name>?intensity=...&bg_color=...&mode=blur+motion
return td::make_unique<InternalLinkBackground>(query.substr(4).str());
} else if (path.size() == 2 && !path[0].empty()) {
// /<username>/12345?single
return td::make_unique<InternalLinkMessage>();
}
return nullptr;
}
void LinkManager::get_login_url_info(DialogId dialog_id, MessageId message_id, int32 button_id,
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise) {
TRY_RESULT_PROMISE(promise, url, td_->messages_manager_->get_login_button_url(dialog_id, message_id, button_id));

View File

@ -17,6 +17,8 @@
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <utility>
namespace td {
class Td;
@ -31,9 +33,36 @@ class LinkManager : public Actor {
LinkManager &operator=(LinkManager &&) = delete;
~LinkManager() override;
// checks whether link is a valid tg, ton or HTTP(S) URL and returns it in a canonical form
enum class InternalLinkType : int32 { Background, Message };
class InternalLink {
public:
InternalLink() = default;
InternalLink(const InternalLink &) = delete;
InternalLink &operator=(const InternalLink &) = delete;
InternalLink(InternalLink &&) = delete;
InternalLink &operator=(InternalLink &&) = delete;
virtual ~InternalLink() = default;
virtual td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const = 0;
virtual InternalLinkType get_type() const = 0;
};
// checks whether the link is a valid tg, ton or HTTP(S) URL and returns it in a canonical form
static Result<string> check_link(Slice link);
struct LinkInfo {
bool is_internal_ = false;
bool is_tg_ = false;
Slice query_;
};
// returns information about the link
static LinkInfo get_link_info(Slice link);
// checks whether the link is a supported tg or t.me URL and parses it
static unique_ptr<InternalLink> parse_internal_link(Slice link);
void get_login_url_info(DialogId dialog_id, MessageId message_id, int32 button_id,
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise);
@ -48,6 +77,13 @@ class LinkManager : public Actor {
private:
void tear_down() override;
class InternalLinkBackground;
class InternalLinkMessage;
static unique_ptr<InternalLink> parse_tg_link_query(Slice query);
static unique_ptr<InternalLink> parse_t_me_link_query(Slice query);
Td *td_;
ActorShared<> parent_;
};

View File

@ -17457,7 +17457,7 @@ Result<MessagesManager::MessageLinkInfo> MessagesManager::get_message_link_info(
string cur_t_me_url = G()->shared_config().get_option_string("t_me_url");
if (begins_with(cur_t_me_url, "http://") || begins_with(cur_t_me_url, "https://")) {
Slice t_me_url = cur_t_me_url;
t_me_url = t_me_url.substr(url[4] == 's' ? 8 : 7);
t_me_url = t_me_url.substr(t_me_url[4] == 's' ? 8 : 7);
if (!td::contains(t_me_urls, t_me_url)) {
t_me_urls.push_back(t_me_url);
}