diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 17da7f5d3..19addd464 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4714,6 +4714,13 @@ internalLinkTypeUserToken token:string = InternalLinkType; //@is_live_stream True, if the video chat is expected to be a live stream in a channel or a broadcast group internalLinkTypeVideoChat chat_username:string invite_hash:string is_live_stream:Bool = InternalLinkType; +//@description The link is a link to a web app. Call searchPublicChat with the given bot username, check that the user is a bot, then call searchWebApp with the received bot and the given web_app_short_name. +//-Process received webAppInfo by showing a confirmation dialog if needed, calling getWebAppLinkUrl and opening returned URL +//@bot_username Username of the bot that owns the Web App +//@web_app_short_name Short name of the Web App +//@start_parameter Start parameter to be passed to getWebAppLinkUrl +internalLinkTypeWebApp bot_username:string web_app_short_name:string start_parameter:string = InternalLinkType; + //@description Contains an HTTPS link to a message in a supergroup or channel, or a forum topic @link The link @is_public True, if the link will work for non-members of the chat messageLink link:string is_public:Bool = MessageLink; diff --git a/td/telegram/LinkManager.cpp b/td/telegram/LinkManager.cpp index f339ea943..d1c38c7fe 100644 --- a/td/telegram/LinkManager.cpp +++ b/td/telegram/LinkManager.cpp @@ -58,6 +58,13 @@ static bool is_valid_phone_number(Slice phone_number) { return true; } +static bool is_valid_web_app_name(Slice name) { + if (name.empty() || !is_alpha(name[0])) { + return false; + } + return true; +} + static string get_url_query_hash(bool is_tg, const HttpUrlQuery &url_query) { const auto &path = url_query.path_; if (is_tg) { @@ -594,6 +601,23 @@ class LinkManager::InternalLinkVoiceChat final : public InternalLink { } }; +class LinkManager::InternalLinkWebApp final : public InternalLink { + string bot_username_; + string web_app_short_name_; + string start_parameter_; + + td_api::object_ptr get_internal_link_type_object() const final { + return td_api::make_object(bot_username_, web_app_short_name_, start_parameter_); + } + + public: + InternalLinkWebApp(string bot_username, string web_app_short_name, string start_parameter) + : bot_username_(std::move(bot_username)) + , web_app_short_name_(std::move(web_app_short_name)) + , start_parameter_(std::move(start_parameter)) { + } +}; + class GetDeepLinkInfoQuery final : public Td::ResultHandler { Promise> promise_; @@ -1086,6 +1110,11 @@ unique_ptr LinkManager::parse_tg_link_query(Slice que // resolve?domain=&game= return td::make_unique(std::move(username), arg.second); } + if (arg.first == "appname" && is_valid_web_app_name(arg.second)) { + // resolve?domain=&appname=&startapp= + return td::make_unique(std::move(username), arg.second, + url_query.get_arg("startapp").str()); + } } if (!url_query.get_arg("attach").empty()) { // resolve?domain=&attach= @@ -1423,6 +1452,9 @@ unique_ptr LinkManager::parse_t_me_link_query(Slice q << "&post=" << post << copy_arg("single") << thread << copy_arg("comment") << copy_arg("t")); } + if (path.size() == 2 && is_valid_web_app_name(path[1])) { + return td::make_unique(path[0], path[1], url_query.get_arg("startapp").str()); + } auto username = path[0]; for (auto &arg : url_query.args_) { if (arg.first == "voicechat" || arg.first == "videochat" || arg.first == "livestream") { diff --git a/td/telegram/LinkManager.h b/td/telegram/LinkManager.h index cd3295d44..ee493f8d9 100644 --- a/td/telegram/LinkManager.h +++ b/td/telegram/LinkManager.h @@ -139,6 +139,7 @@ class LinkManager final : public Actor { class InternalLinkUserPhoneNumber; class InternalLinkUserToken; class InternalLinkVoiceChat; + class InternalLinkWebApp; enum class LinkType : int32 { External, TMe, Tg, Telegraph }; diff --git a/test/link.cpp b/test/link.cpp index dfb4f5bbb..be0a17a6e 100644 --- a/test/link.cpp +++ b/test/link.cpp @@ -244,6 +244,11 @@ TEST(Link, parse_internal_link) { auto video_chat = [](const td::string &chat_username, const td::string &invite_hash, bool is_live_stream) { return td::td_api::make_object(chat_username, invite_hash, is_live_stream); }; + auto web_app = [](const td::string &bot_username, const td::string &web_app_short_name, + const td::string &start_parameter) { + return td::td_api::make_object(bot_username, web_app_short_name, + start_parameter); + }; parse_internal_link("t.me/levlam/1", message("tg:resolve?domain=levlam&post=1")); parse_internal_link("telegram.me/levlam/1", message("tg:resolve?domain=levlam&post=1")); @@ -354,7 +359,6 @@ TEST(Link, parse_internal_link) { message("tg:resolve?domain=username&post=67890&single&thread=12345")); parse_internal_link("t.me/username/1asdasdas/asdasd//asd/asd/asd/?single", message("tg:resolve?domain=username&post=1&single")); - parse_internal_link("t.me/username/asd", public_chat("username")); parse_internal_link("t.me/username/0", public_chat("username")); parse_internal_link("t.me/username/-12345", public_chat("username")); parse_internal_link("t.me//12345?single", nullptr); @@ -446,7 +450,7 @@ TEST(Link, parse_internal_link) { parse_internal_link("t.me/bg/111111-222222%20?rotation=180%20", background("111111-222222%20?rotation=180%20")); parse_internal_link("t.me/bg/111111~222222", background("111111~222222")); parse_internal_link("t.me/bg/abacaba", background("abacaba")); - parse_internal_link("t.me/Bg/abacaba", public_chat("Bg")); + parse_internal_link("t.me/Bg/abacaba", web_app("Bg", "abacaba", "")); parse_internal_link("t.me/bg/111111~222222#asdasd", background("111111~222222")); parse_internal_link("t.me/bg/111111~222222?mode=12", background("111111~222222?mode=12")); parse_internal_link("t.me/bg/111111~222222?mode=12&text=1", background("111111~222222?mode=12")); @@ -786,7 +790,7 @@ TEST(Link, parse_internal_link) { parse_internal_link("t.me/username/0/a//s/as?voicechat=", video_chat("username", "", false)); parse_internal_link("t.me/username/0/a//s/as?videochat=2", video_chat("username", "2", false)); parse_internal_link("t.me/username/0/a//s/as?livestream=3", video_chat("username", "3", true)); - parse_internal_link("t.me/username/aasdas?test=1&voicechat=#12312", video_chat("username", "", false)); + parse_internal_link("t.me/username/aasdas/2?test=1&voicechat=#12312", video_chat("username", "", false)); parse_internal_link("t.me/username/0?voicechat=", video_chat("username", "", false)); parse_internal_link("t.me/username/-1?voicechat=asdasd", video_chat("username", "asdasd", false)); parse_internal_link("t.me/username?voicechat=", video_chat("username", "", false)); @@ -804,7 +808,7 @@ TEST(Link, parse_internal_link) { parse_internal_link("tg:resolve?domain=telegram&&&&&&&start=%30", bot_start("telegram", "0")); parse_internal_link("t.me/username/0/a//s/as?start=", bot_start("username", "")); - parse_internal_link("t.me/username/aasdas?test=1&start=#12312", bot_start("username", "")); + parse_internal_link("t.me/username/aasdas/2?test=1&start=#12312", bot_start("username", "")); parse_internal_link("t.me/username/0?start=", bot_start("username", "")); parse_internal_link("t.me/username/-1?start=asdasd", bot_start("username", "asdasd")); parse_internal_link("t.me/username?start=", bot_start("username", "")); @@ -850,7 +854,7 @@ TEST(Link, parse_internal_link) { true, true, false))); parse_internal_link("t.me/username/0/a//s/as?startgroup=", bot_start_in_group("username", "", nullptr)); - parse_internal_link("t.me/username/aasdas?test=1&startgroup=#12312", bot_start_in_group("username", "", nullptr)); + parse_internal_link("t.me/username/aasdas/2?test=1&startgroup=#12312", bot_start_in_group("username", "", nullptr)); parse_internal_link("t.me/username/0?startgroup=", bot_start_in_group("username", "", nullptr)); parse_internal_link("t.me/username/-1?startgroup=asdasd", bot_start_in_group("username", "asdasd", nullptr)); parse_internal_link("t.me/username?startgroup=", bot_start_in_group("username", "", nullptr)); @@ -897,7 +901,7 @@ TEST(Link, parse_internal_link) { parse_internal_link("tg:resolve?domain=telegram&&&&&&&game=%30", game("telegram", "0")); parse_internal_link("t.me/username/0/a//s/as?game=asd", game("username", "asd")); - parse_internal_link("t.me/username/aasdas?test=1&game=asd#12312", game("username", "asd")); + parse_internal_link("t.me/username/aasdas/2?test=1&game=asd#12312", game("username", "asd")); parse_internal_link("t.me/username/0?game=asd", game("username", "asd")); parse_internal_link("t.me/username/-1?game=asdasd", game("username", "asdasd")); parse_internal_link("t.me/username?game=asd", game("username", "asd")); @@ -906,6 +910,33 @@ TEST(Link, parse_internal_link) { parse_internal_link("t.me//username?game=asd", nullptr); parse_internal_link("https://telegram.dog/tele%63ram?game=t%63st", game("telecram", "tcst")); + parse_internal_link("tg:resolve?domain=username&appname=aasdasd&startapp=123asd", + web_app("username", "aasdasd", "123asd")); + parse_internal_link("TG://resolve?domain=username&appname=&startapp=123asd", public_chat("username")); + parse_internal_link("TG://test@resolve?domain=username&appname=asd", nullptr); + parse_internal_link("tg:resolve:80?domain=username&appname=asd", nullptr); + parse_internal_link("tg:http://resolve?domain=username&appname=asd", nullptr); + parse_internal_link("tg:https://resolve?domain=username&appname=asd", nullptr); + parse_internal_link("tg:resolve?domain=&appname=asd", unknown_deep_link("tg://resolve?domain=&appname=asd")); + parse_internal_link("tg:resolve?domain=telegram&&&&&&&appname=%41&startapp=", web_app("telegram", "A", "")); + + parse_internal_link("t.me/username/0/a//s/as?appname=asd", public_chat("username")); + parse_internal_link("t.me/username/aasdas/2?test=1&appname=asd#12312", public_chat("username")); + parse_internal_link("t.me/username/0?appname=asd", public_chat("username")); + parse_internal_link("t.me/username/-1?appname=asdasd", public_chat("username")); + parse_internal_link("t.me/username?appname=asd", public_chat("username")); + parse_internal_link("t.me/username?appname=", public_chat("username")); + parse_internal_link("t.me/username#appname=asdas", public_chat("username")); + parse_internal_link("t.me//username?appname=asd", nullptr); + parse_internal_link("https://telegram.dog/tele%63ram?appname=t%63st", public_chat("telecram")); + parse_internal_link("t.me/username/def/asd", public_chat("username")); + parse_internal_link("t.me/username/asd#12312&startapp=qwe", web_app("username", "asd", "")); + parse_internal_link("t.me/username/asd?12312&startapp=qwe", web_app("username", "asd", "qwe")); + parse_internal_link("t.me/username/asdasd?startapp=0", web_app("username", "asdasd", "0")); + parse_internal_link("t.me/username/asd", web_app("username", "asd", "")); + parse_internal_link("t.me/username/", public_chat("username")); + parse_internal_link("https://telegram.dog/tele%63ram/t%63st", web_app("telecram", "tcst", "")); + parse_internal_link("tg:resolve?domain=username&Game=asd", public_chat("username")); parse_internal_link("TG://test@resolve?domain=username", nullptr); parse_internal_link("tg:resolve:80?domain=username", nullptr); @@ -927,7 +958,7 @@ TEST(Link, parse_internal_link) { parse_internal_link("t.me/asdf0", public_chat("asdf0")); parse_internal_link("t.me/asd__fg", nullptr); parse_internal_link("t.me/username/0/a//s/as?gam=asd", public_chat("username")); - parse_internal_link("t.me/username/aasdas?test=1", public_chat("username")); + parse_internal_link("t.me/username/aasdas/2?test=1", public_chat("username")); parse_internal_link("t.me/username/0", public_chat("username")); parse_internal_link("t.me//username", nullptr); parse_internal_link("https://telegram.dog/tele%63ram", public_chat("telecram"));