From 5ca0a72ddfe8e610997d16a9c5787921c5744281 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 5 Aug 2019 12:12:59 +0300 Subject: [PATCH] Add td_api::getMessageLinkInfo. GitOrigin-RevId: 34213fdada780b9bb8fe6a6db5a0de3be097b55c --- td/generate/scheme/td_api.tl | 20 ++- td/generate/scheme/td_api.tlo | Bin 156244 -> 156568 bytes td/telegram/ContactsManager.cpp | 2 +- td/telegram/MessagesManager.cpp | 216 +++++++++++++++++++++++++++++++- td/telegram/MessagesManager.h | 18 ++- td/telegram/Td.cpp | 32 +++++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 2 + 8 files changed, 282 insertions(+), 10 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 31741680..43643b82 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -641,7 +641,7 @@ chatInviteLink invite_link:string = ChatInviteLink; //@photo Chat photo; may be null //@member_count Number of members //@member_user_ids User identifiers of some chat members that may be known to the current user -//@is_public True, if the chat is a public supergroup or channel with a username +//@is_public True, if the chat is a public supergroup or a channel with a username chatInviteLinkInfo chat_id:int53 type:ChatType title:string photo:chatPhoto member_count:int32 member_user_ids:vector is_public:Bool = ChatInviteLinkInfo; @@ -2005,7 +2005,7 @@ checkChatUsernameResultUsernameInvalid = CheckChatUsernameResult; //@description The username is occupied checkChatUsernameResultUsernameOccupied = CheckChatUsernameResult; -//@description The user has too much public chats, one of them should be made private first +//@description The user has too much chats with username, one of them should be made private first checkChatUsernameResultPublicChatsTooMuch = CheckChatUsernameResult; //@description The user can't be a member of a public supergroup @@ -2278,9 +2278,16 @@ chatReportReasonCopyright = ChatReportReason; chatReportReasonCustom text:string = ChatReportReason; -//@description Contains a public HTTPS link to a message in a public supergroup or channel @link Message link @html HTML-code for embedding the message +//@description Contains a public HTTPS link to a message in a public supergroup or channel with a username @link Message link @html HTML-code for embedding the message publicMessageLink link:string html:string = PublicMessageLink; +//@description Contains information about a link to a message in a chat +//@is_public True, if the link is a public link for a message in a chat +//@chat_id If found, identifier of the chat to which the message belongs, 0 otherwise +//@message If found, the linked message; may be null +//@for_album True, if the whole media album to which the message belongs is linked +messageLinkInfo is_public:Bool chat_id:int53 message:message for_album:Bool = MessageLinkInfo; + //@description Contains a part of a file @data File bytes filePart data:bytes = FilePart; @@ -2959,7 +2966,7 @@ clearRecentlyFoundChats = Ok; //@description Checks whether a username can be set for a chat @chat_id Chat identifier; should be identifier of a supergroup chat, or a channel chat, or a private chat with self, or zero if chat is being created @username Username to be checked checkChatUsername chat_id:int53 username:string = CheckChatUsernameResult; -//@description Returns a list of public chats created by the user +//@description Returns a list of public chats with username created by the user getCreatedPublicChats = Chats; @@ -3032,7 +3039,7 @@ removeNotification notification_group_id:int32 notification_id:int32 = Ok; removeNotificationGroup notification_group_id:int32 max_notification_id:int32 = Ok; -//@description Returns a public HTTPS link to a message. Available only for messages in public supergroups and channels +//@description Returns a public HTTPS link to a message. Available only for messages in supergroups and channels with username //@chat_id Identifier of the chat to which the message belongs //@message_id Identifier of the message //@for_album Pass true if a link for a whole media album should be returned @@ -3043,6 +3050,9 @@ getPublicMessageLink chat_id:int53 message_id:int53 for_album:Bool = PublicMessa //@message_id Identifier of the message getMessageLink chat_id:int53 message_id:int53 = HttpUrl; +//@description Returns information about a public or private message link @url The message link in the format "https://t.me/c/...", or "tg://privatepost?...", or "https://t.me/username/...", or "tg://resolve?..." +getMessageLinkInfo url:string = MessageLinkInfo; + //@description Sends a message. Returns the sent message @chat_id Target chat @reply_to_message_id Identifier of the message to reply to or 0 //@disable_notification Pass true to disable notification for the message. Not supported in secret chats @from_background Pass true if the message is sent from the background diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 9cdc7034ac0fb8e7343583033165b74022451d3f..e21f00483c760c44703fe2bcde6dfa247ceb017c 100644 GIT binary patch delta 180 zcmca|gmcDmPTohe^{p77;O$1<>mnk~+x67>eN&5z6Vp?DGV`)M^V0GsKVX#D{6yr7 zFyocYH#PpBlm#owMO6e6yLn8=hh?&6l=|cgo7kqybur3KzO!Fox=$6Oz%(u<4Hl4^ u&1XIxInEflec~=gc2AjzEq2$0(o;*&Y-9kbobFS~r~}rsy@#3UL>vImz(Z#M delta 55 zcmbPnob$>NPTohe^{p77;LS$f>mr*4M860#Uf#^A`TykRBcFF1XN=fhyPJ{SbNd|@ Hraf^0LzWgJ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 4cdbfb7a..e9380362 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -9528,7 +9528,7 @@ void ContactsManager::reload_channel(ChannelId channel_id, Promise &&promi have_channel_force(channel_id); auto input_channel = get_input_channel(channel_id); if (input_channel == nullptr) { - return promise.set_error(Status::Error(6, "Supergroup info not found")); + input_channel = make_tl_object(channel_id.get(), 0); } // there is no much reason to combine different requests into one request diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index b03fff7a..e269c6a1 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -12061,6 +12061,7 @@ MessageId MessagesManager::get_replied_message_id(const Message *m) { void MessagesManager::get_message_force_from_server(Dialog *d, MessageId message_id, Promise &&promise, tl_object_ptr input_message) { + LOG(INFO) << "Get " << message_id << " in " << d->dialog_id << " using " << to_string(input_message); auto m = get_message_force(d, message_id, "get_message_force_from_server"); if (m == nullptr && message_id.is_valid() && message_id.is_server()) { auto dialog_type = d->dialog_id.get_type(); @@ -12371,6 +12372,214 @@ string MessagesManager::get_message_link(FullMessageId full_message_id, Promise< << dialog_id.get_channel_id().get() << "/" << message_id.get_server_message_id().get(); } +Result MessagesManager::get_message_link_info(Slice url) { + if (url.empty()) { + return Status::Error("URL must be non-empty"); + } + + url.truncate(url.find('#')); + + string lower_cased_url = to_lower(url); + url = lower_cased_url; + + Slice username; + Slice channel_id_slice; + Slice message_id_slice; + bool is_single = false; + if (begins_with(url, "tg:")) { + url = url.substr(3); + if (begins_with(url, "//")) { + url = url.substr(2); + } + + // resolve?domain=username&post=12345&single + // privatepost?channel=123456789&msg_id=12345 + + bool is_resolve = false; + if (begins_with(url, "resolve")) { + url = url.substr(7); + is_resolve = true; + } else if (begins_with(url, "privatepost")) { + url = url.substr(11); + } else { + return Status::Error("Wrong message link URL"); + } + + if (begins_with(url, "/")) { + url = url.substr(1); + } + if (!begins_with(url, "?")) { + return Status::Error("Wrong message link URL"); + } + url = url.substr(1); + + auto args = full_split(url, '&'); + for (auto arg : args) { + auto key_value = split(arg, '='); + if (is_resolve) { + if (key_value.first == "domain") { + username = key_value.second; + } + if (key_value.first == "post") { + message_id_slice = key_value.second; + } + } else { + if (key_value.first == "channel") { + channel_id_slice = key_value.second; + } + if (key_value.first == "msg_id") { + message_id_slice = key_value.second; + } + } + if (key_value.first == "single") { + is_single = true; + } + } + } else { + if (begins_with(url, "http://") || begins_with(url, "https://")) { + url = url.substr(url[4] == 's' ? 8 : 7); + } + + // t.me/c/123456789/12345 + // t.me/username/12345?single + + vector 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 (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); + if (std::find(t_me_urls.begin(), t_me_urls.end(), t_me_url) == t_me_urls.end()) { + t_me_urls.push_back(t_me_url); + } + } + + for (auto t_me_url : t_me_urls) { + if (begins_with(url, t_me_url)) { + url = url.substr(t_me_url.size()); + auto username_end_pos = url.find('/'); + if (username_end_pos == Slice::npos) { + return Status::Error("Wrong message link URL"); + } + username = url.substr(0, username_end_pos); + url = url.substr(username_end_pos + 1); + if (username == "c") { + username = Slice(); + auto channel_id_end_pos = url.find('/'); + if (channel_id_end_pos == Slice::npos) { + return Status::Error("Wrong message link URL"); + } + channel_id_slice = url.substr(0, channel_id_end_pos); + url = url.substr(channel_id_end_pos + 1); + } + + auto query_pos = url.find('?'); + message_id_slice = url.substr(0, query_pos); + is_single = query_pos != Slice::npos && url.substr(query_pos + 1) == "single"; + break; + } + } + } + + ChannelId channel_id; + if (username.empty()) { + auto r_channel_id = to_integer_safe(channel_id_slice); + if (r_channel_id.is_error() || !ChannelId(r_channel_id.ok()).is_valid()) { + return Status::Error("Wrong channel ID"); + } + channel_id = ChannelId(r_channel_id.ok()); + } + + auto r_message_id = to_integer_safe(message_id_slice); + if (r_message_id.is_error() || !ServerMessageId(r_message_id.ok()).is_valid()) { + return Status::Error("Wrong message ID"); + } + + MessageLinkInfo info; + info.username = username.str(); + info.channel_id = channel_id; + info.message_id = MessageId(ServerMessageId(r_message_id.ok())); + info.is_single = is_single; + LOG(INFO) << "Have link to " << info.message_id << " in chat @" << info.username << "/" << channel_id.get(); + return std::move(info); +} + +void MessagesManager::get_message_link_info(Slice url, Promise &&promise) { + auto r_message_link_info = get_message_link_info(url); + if (r_message_link_info.is_error()) { + return promise.set_error(Status::Error(400, r_message_link_info.error().message())); + } + + auto info = r_message_link_info.move_as_ok(); + CHECK(info.username.empty() == info.channel_id.is_valid()); + + bool have_dialog = info.username.empty() ? td_->contacts_manager_->have_channel_force(info.channel_id) + : resolve_dialog_username(info.username).is_valid(); + if (!have_dialog) { + auto query_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), info, promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + return promise.set_value(std::move(info)); + } + send_closure(actor_id, &MessagesManager::on_get_message_link_dialog, std::move(info), std::move(promise)); + }); + if (info.username.empty()) { + td_->contacts_manager_->reload_channel(info.channel_id, std::move(query_promise)); + } else { + td_->create_handler(std::move(query_promise))->send(info.username); + } + return; + } + + return on_get_message_link_dialog(std::move(info), std::move(promise)); +} + +void MessagesManager::on_get_message_link_dialog(MessageLinkInfo &&info, Promise &&promise) { + DialogId dialog_id; + if (info.username.empty()) { + if (!td_->contacts_manager_->have_channel(info.channel_id)) { + return promise.set_error(Status::Error(500, "Chat info not found")); + } + + dialog_id = DialogId(info.channel_id); + force_create_dialog(dialog_id, "on_get_message_link_dialog"); + } else { + dialog_id = resolve_dialog_username(info.username); + } + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return promise.set_error(Status::Error(500, "Chat not found")); + } + + get_message_force_from_server( + d, info.message_id, + PromiseCreator::lambda([info = std::move(info), promise = std::move(promise)](Result &&result) mutable { + promise.set_value(std::move(info)); + })); +} + +td_api::object_ptr MessagesManager::get_message_link_info_object( + const MessageLinkInfo &info) const { + CHECK(info.username.empty() == info.channel_id.is_valid()); + + bool is_public = !info.username.empty(); + DialogId dialog_id = is_public ? resolve_dialog_username(info.username) : DialogId(info.channel_id); + td_api::object_ptr message; + bool for_album = false; + + const Dialog *d = get_dialog(dialog_id); + if (d == nullptr) { + dialog_id = DialogId(); + } else { + const Message *m = get_message(d, info.message_id); + if (m != nullptr) { + message = get_message_object(dialog_id, m); + for_album = !info.is_single && m->media_album_id != 0; + } + } + + return td_api::make_object(is_public, dialog_id.get(), std::move(message), for_album); +} + Status MessagesManager::delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) { if (td_->auth_manager_->is_bot()) { return Status::Error(6, "Bots can't delete chat reply markup"); @@ -20864,13 +21073,14 @@ void MessagesManager::on_dialog_permissions_updated(DialogId dialog_id) { } } -DialogId MessagesManager::resolve_dialog_username(const string &username) { - auto it = resolved_usernames_.find(clean_username(username)); +DialogId MessagesManager::resolve_dialog_username(const string &username) const { + auto cleaned_username = clean_username(username); + auto it = resolved_usernames_.find(cleaned_username); if (it != resolved_usernames_.end()) { return it->second.dialog_id; } - auto it2 = inaccessible_resolved_usernames_.find(clean_username(username)); + auto it2 = inaccessible_resolved_usernames_.find(cleaned_username); if (it2 != inaccessible_resolved_usernames_.end()) { return it2->second; } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index f0e81f25..959a615b 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -326,7 +326,7 @@ class MessagesManager : public Actor { void clear_recently_found_dialogs(); - DialogId resolve_dialog_username(const string &username); + DialogId resolve_dialog_username(const string &username) const; DialogId search_public_dialog(const string &username_to_search, bool force, Promise &&promise); @@ -496,6 +496,18 @@ class MessagesManager : public Actor { string get_message_link(FullMessageId full_message_id, Promise &&promise); + struct MessageLinkInfo { + string username; + // or + ChannelId channel_id; + + MessageId message_id; + bool is_single = false; + }; + void get_message_link_info(Slice url, Promise &&promise); + + td_api::object_ptr get_message_link_info_object(const MessageLinkInfo &info) const; + Status delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) TD_WARN_UNUSED_RESULT; Status set_dialog_draft_message(DialogId dialog_id, @@ -1916,6 +1928,10 @@ class MessagesManager : public Actor { void ttl_db_loop(double server_now); void ttl_db_on_result(Result>, int32>> r_result, bool dummy); + static Result get_message_link_info(Slice url); + + void on_get_message_link_dialog(MessageLinkInfo &&info, Promise &&promise); + static MessageId get_first_database_message_id_by_index(const Dialog *d, SearchMessagesFilter filter); void on_search_dialog_messages_db_result(int64 random_id, DialogId dialog_id, MessageId from_message_id, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index c3863f44..2eca20ed 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1175,6 +1175,33 @@ class GetMessageLinkRequest : public RequestActor<> { } }; +class GetMessageLinkInfoRequest : public RequestActor { + string url_; + + MessagesManager::MessageLinkInfo message_link_info_; + + void do_run(Promise &&promise) override { + if (get_tries() < 2) { + promise.set_value(std::move(message_link_info_)); + return; + } + td->messages_manager_->get_message_link_info(url_, std::move(promise)); + } + + void do_set_result(MessagesManager::MessageLinkInfo &&result) override { + message_link_info_ = std::move(result); + } + + void do_send_result() override { + send_result(td->messages_manager_->get_message_link_info_object(message_link_info_)); + } + + public: + GetMessageLinkInfoRequest(ActorShared td, uint64 request_id, string url) + : RequestActor(std::move(td), request_id), url_(std::move(url)) { + } +}; + class EditMessageTextRequest : public RequestOnceActor { FullMessageId full_message_id_; tl_object_ptr reply_markup_; @@ -5195,6 +5222,11 @@ void Td::on_request(uint64 id, const td_api::getMessageLink &request) { CREATE_REQUEST(GetMessageLinkRequest, request.chat_id_, request.message_id_); } +void Td::on_request(uint64 id, td_api::getMessageLinkInfo &request) { + CLEAN_INPUT_STRING(request.url_); + CREATE_REQUEST(GetMessageLinkInfoRequest, std::move(request.url_)); +} + void Td::on_request(uint64 id, const td_api::getFile &request) { send_closure(actor_id(this), &Td::send_result, id, file_manager_->get_file_object(FileId(request.file_id_, 0))); } diff --git a/td/telegram/Td.h b/td/telegram/Td.h index c309bfbb..94c410fb 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -474,6 +474,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::getMessageLink &request); + void on_request(uint64 id, td_api::getMessageLinkInfo &request); + void on_request(uint64 id, const td_api::getFile &request); void on_request(uint64 id, td_api::getRemoteFile &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 8554cc17..cf18f541 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2352,6 +2352,8 @@ class CliClient final : public Actor { string message_id; std::tie(chat_id, message_id) = split(args); send_request(td_api::make_object(as_chat_id(chat_id), as_message_id(message_id))); + } else if (op == "gmli") { + send_request(td_api::make_object(args)); } else if (op == "gcmbd") { string chat_id; string date;