Add internalLinkTypeSideMenuBot.

This commit is contained in:
levlam 2023-09-07 19:23:07 +03:00
parent 289c25fd93
commit a7d9da0229
4 changed files with 85 additions and 3 deletions

View File

@ -555,7 +555,7 @@ userTypeDeleted = UserType;
//@is_inline True, if the bot supports inline queries
//@inline_query_placeholder Placeholder for inline queries (displayed on the application input field)
//@need_location True, if the location of the user is expected to be sent with every inline query to this bot
//@can_be_added_to_attachment_menu True, if the bot can be added to attachment menu
//@can_be_added_to_attachment_menu True, if the bot can be added to attachment or side menu
userTypeBot can_be_edited:Bool can_join_groups:Bool can_read_all_group_messages:Bool is_inline:Bool inline_query_placeholder:string need_location:Bool can_be_added_to_attachment_menu:Bool = UserType;
//@description No information on the user besides the user identifier is available, yet this user has not been deleted. This object is extremely rare and must be handled like a deleted user. It is not possible to perform any actions on users of this type
@ -5074,6 +5074,13 @@ internalLinkTypeRestorePurchases = InternalLinkType;
//@description The link is a link to application settings
internalLinkTypeSettings = InternalLinkType;
//@description The link is a link to a bot, which can be installed to the side menu. Call searchPublicChat with the given bot username, check that the user is a bot and can be added to attachment menu.
//-Then, use getAttachmentMenuBot to receive information about the bot. If the bot isn't added to side menu, then user needs to confirm adding the bot to side and attachment menu.
//-If user confirms adding, then use toggleBotIsAddedToAttachmentMenu to add the bot. If the bot is added to attachment menu, then use getWebAppUrl with the given URL
//@bot_username Username of the bot
//@url URL to be passed to getWebAppUrl
internalLinkTypeSideMenuBot bot_username:string url:string = InternalLinkType;
//@description The link is a link to a sticker set. Call searchStickerSet with the given sticker set name to process the link and show the sticker set
//@sticker_set_name Name of the sticker set
//@expect_custom_emoji True, if the sticker set is expected to contain custom emoji

View File

@ -597,6 +597,22 @@ class LinkManager::InternalLinkSettings final : public InternalLink {
}
};
class LinkManager::InternalLinkSideMenuBot final : public InternalLink {
string bot_username_;
string url_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeSideMenuBot>(bot_username_, url_);
}
public:
InternalLinkSideMenuBot(string bot_username, string start_parameter) : bot_username_(std::move(bot_username)) {
if (!start_parameter.empty()) {
url_ = PSTRING() << "start://" << start_parameter;
}
}
};
class LinkManager::InternalLinkStickerSet final : public InternalLink {
string sticker_set_name_;
bool expect_custom_emoji_;
@ -1224,6 +1240,11 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
return td::make_unique<InternalLinkStory>(std::move(username), StoryId(to_integer<int32>(arg.second)));
}
}
if (url_query.has_arg("startapp") && !url_query.has_arg("appname")) {
// resolve?domain=<bot_username>&startapp=
// resolve?domain=<bot_username>&startapp=<start_parameter>
return td::make_unique<InternalLinkSideMenuBot>(std::move(username), url_query.get_arg("startapp").str());
}
if (!url_query.get_arg("attach").empty()) {
// resolve?domain=<username>&attach=<bot_username>
// resolve?domain=<username>&attach=<bot_username>&startattach=<start_parameter>
@ -1616,6 +1637,11 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
return td::make_unique<InternalLinkBotAddToChannel>(std::move(username), std::move(administrator_rights));
}
}
if (arg.first == "startapp" && is_valid_start_parameter(arg.second)) {
// /<bot_username>?startapp
// /<bot_username>?startapp=<parameter>
return td::make_unique<InternalLinkSideMenuBot>(std::move(username), arg.second);
}
if (arg.first == "game" && is_valid_game_name(arg.second)) {
// /<bot_username>?game=<short_name>
return td::make_unique<InternalLinkGame>(std::move(username), arg.second);
@ -1727,7 +1753,11 @@ Result<string> LinkManager::get_internal_link_impl(const td_api::InternalLinkTyp
if (!begins_with(link->url_, "start://")) {
return Status::Error(400, "Unsupported link URL specified");
}
start_parameter = PSTRING() << '=' << Slice(link->url_).substr(8);
auto start_parameter_slice = Slice(link->url_).substr(8);
if (start_parameter_slice.empty() || !is_valid_start_parameter(start_parameter_slice)) {
return Status::Error(400, "Invalid start parameter specified");
}
start_parameter = PSTRING() << '=' << start_parameter_slice;
}
if (link->target_chat_ == nullptr) {
return Status::Error(400, "Target chat must be non-empty");
@ -1917,6 +1947,28 @@ Result<string> LinkManager::get_internal_link_impl(const td_api::InternalLinkTyp
return Status::Error("HTTP link is unavailable for the link type");
}
return "tg://settings/auto_delete";
case td_api::internalLinkTypeSideMenuBot::ID: {
auto link = static_cast<const td_api::internalLinkTypeSideMenuBot *>(type_ptr);
if (!is_valid_username(link->bot_username_)) {
return Status::Error(400, "Invalid bot username specified");
}
string start_parameter;
if (!link->url_.empty()) {
if (!begins_with(link->url_, "start://")) {
return Status::Error(400, "Unsupported link URL specified");
}
auto start_parameter_slice = Slice(link->url_).substr(8);
if (start_parameter_slice.empty() || !is_valid_start_parameter(start_parameter_slice)) {
return Status::Error(400, "Invalid start parameter specified");
}
start_parameter = PSTRING() << '=' << start_parameter_slice;
}
if (is_internal) {
return PSTRING() << "tg://resolve?domain=" << link->bot_username_ << "&startapp" << start_parameter;
} else {
return PSTRING() << get_t_me_url() << link->bot_username_ << "?startapp" << start_parameter;
}
}
case td_api::internalLinkTypeEditProfileSettings::ID:
if (!is_internal) {
return Status::Error("HTTP link is unavailable for the link type");
@ -2197,7 +2249,7 @@ Result<string> LinkManager::get_internal_link_impl(const td_api::InternalLinkTyp
}
string start_parameter;
if (!link->start_parameter_.empty()) {
start_parameter = PSTRING() << (is_internal ? '&' : '?') << "startapp=" << url_encode(link->start_parameter_);
start_parameter = PSTRING() << (is_internal ? '&' : '?') << "startapp=" << link->start_parameter_;
}
if (is_internal) {
return PSTRING() << "tg://resolve?domain=" << link->bot_username_ << "&appname=" << link->web_app_short_name_

View File

@ -143,6 +143,7 @@ class LinkManager final : public Actor {
class InternalLinkQrCodeAuthentication;
class InternalLinkRestorePurchases;
class InternalLinkSettings;
class InternalLinkSideMenuBot;
class InternalLinkStickerSet;
class InternalLinkStory;
class InternalLinkTheme;

View File

@ -313,6 +313,11 @@ static auto settings() {
return td::td_api::make_object<td::td_api::internalLinkTypeSettings>();
}
static auto side_menu_bot(const td::string &bot_username, const td::string &start_parameter) {
return td::td_api::make_object<td::td_api::internalLinkTypeSideMenuBot>(
bot_username, start_parameter.empty() ? td::string() : "start://" + start_parameter);
}
static auto sticker_set(const td::string &sticker_set_name, bool expect_custom_emoji) {
return td::td_api::make_object<td::td_api::internalLinkTypeStickerSet>(sticker_set_name, expect_custom_emoji);
}
@ -1104,6 +1109,23 @@ TEST(Link, parse_internal_link_part4) {
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&startapp=aasdasd", side_menu_bot("username", "aasdasd"));
parse_internal_link("TG://resolve?domain=username&startapp=&startapp=123asd", side_menu_bot("username", ""));
parse_internal_link("TG://test@resolve?domain=username&startapp=asd", nullptr);
parse_internal_link("tg:resolve:80?domain=username&startapp=asd", nullptr);
parse_internal_link("tg:http://resolve?domain=username&startapp=asd", nullptr);
parse_internal_link("tg:https://resolve?domain=username&startapp=asd", nullptr);
parse_internal_link("tg:resolve?domain=&startapp=asd", unknown_deep_link("tg://resolve?domain=&startapp=asd"));
parse_internal_link("tg:resolve?domain=telegram&&&&&&&startapp=%41", side_menu_bot("telegram", "A"));
parse_internal_link("tg:resolve?domain=telegram&&&&&&&startapp=%41b", side_menu_bot("telegram", "Ab"));
parse_internal_link("tg:resolve?domain=telegram&&&&&&&startapp=%41bc", side_menu_bot("telegram", "Abc"));
parse_internal_link("t.me/username?startapp=qwe", side_menu_bot("username", "qwe"));
parse_internal_link("t.me/username?12312&startapp=qwe", side_menu_bot("username", "qwe"));
parse_internal_link("t.me/username?startapp=0", side_menu_bot("username", "0"));
parse_internal_link("https://telegram.dog/tele%63ram?startapp=t%63st", side_menu_bot("telecram", "tcst"));
parse_internal_link("https://telegram.dog?startapp=t%63st", nullptr);
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);