diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index ed665ea2c..354263659 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1607,8 +1607,8 @@ animations animations:vector = Animations; importedContacts user_ids:vector importer_count:vector = ImportedContacts; -//@description Contains a link to chat statistics @url The URL containing chat statistics -chatStatisticsUrl url:string = ChatStatisticsUrl; +//@description Contains an HTTP URL @url The URL +httpUrl url:string = HttpUrl; //@class InputInlineQueryResult @description Represents a single result of an inline query; for bots only @@ -2910,6 +2910,11 @@ removeNotificationGroup notification_group_id:int32 max_notification_id:int32 = //@for_album Pass true if a link for a whole media album should be returned getPublicMessageLink chat_id:int53 message_id:int53 for_album:Bool = PublicMessageLink; +//@description Returns a private HTTPS link to a message. Available only for already sent messages in supergroups and channels. The link will work only for chat members +//@chat_id Identifier of the chat to which the message belongs +//@message_id Identifier of the message +getPrivateMessageLink chat_id:int53 message_id:int53 = HttpUrl; + //@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 @@ -3594,7 +3599,7 @@ reportChat chat_id:int53 reason:ChatReportReason message_ids:vector = Ok; //@description Returns URL with chat statistics. Currently this method can be used only for channels @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link -getChatStatisticsUrl chat_id:int53 parameters:string = ChatStatisticsUrl; +getChatStatisticsUrl chat_id:int53 parameters:string = HttpUrl; //@description Returns storage usage statistics. Can be called before authorization @chat_limit Maximum number of chats with the largest storage usage for which separate statistics should be returned. All other chats will be grouped in entries with chat_id == 0. If the chat info database is not used, the chat_limit is ignored and is always set to 0 diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 89951094a..a827d477a 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 6bc07514b..2e6257098 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -353,16 +353,18 @@ class ExportChannelMessageLinkQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; MessageId message_id_; - bool for_group_; + bool for_group_ = false; + bool ignore_result_ = false; public: explicit ExportChannelMessageLinkQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(ChannelId channel_id, MessageId message_id, bool for_group) { + void send(ChannelId channel_id, MessageId message_id, bool for_group, bool ignore_result) { channel_id_ = channel_id; message_id_ = message_id; for_group_ = for_group; + ignore_result_ = ignore_result; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_exportMessageLink( @@ -377,14 +379,18 @@ class ExportChannelMessageLinkQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(DEBUG) << "Receive result for ExportChannelMessageLinkQuery: " << to_string(ptr); - td->messages_manager_->on_get_public_message_link({DialogId(channel_id_), message_id_}, for_group_, - std::move(ptr->link_), std::move(ptr->html_)); + if (!ignore_result_) { + td->messages_manager_->on_get_public_message_link({DialogId(channel_id_), message_id_}, for_group_, + std::move(ptr->link_), std::move(ptr->html_)); + } promise_.set_value(Unit()); } void on_error(uint64 id, Status status) override { - td->contacts_manager_->on_get_channel_error(channel_id_, status, "ExportChannelMessageLinkQuery"); + if (!ignore_result_) { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "ExportChannelMessageLinkQuery"); + } promise_.set_error(std::move(status)); } }; @@ -3282,12 +3288,11 @@ class ReportPeerQuery : public Td::ResultHandler { }; class GetStatsUrlQuery : public Td::ResultHandler { - Promise> promise_; + Promise> promise_; DialogId dialog_id_; public: - explicit GetStatsUrlQuery(Promise> &&promise) - : promise_(std::move(promise)) { + explicit GetStatsUrlQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(DialogId dialog_id, const string ¶meters) { @@ -3305,7 +3310,7 @@ class GetStatsUrlQuery : public Td::ResultHandler { } auto result = result_ptr.move_as_ok(); - promise_.set_value(td_api::make_object(result->url_)); + promise_.set_value(td_api::make_object(result->url_)); } void on_error(uint64 id, Status status) override { @@ -6268,7 +6273,7 @@ void MessagesManager::on_get_peer_settings(DialogId dialog_id, } void MessagesManager::get_dialog_statistics_url(DialogId dialog_id, const string ¶meters, - Promise> &&promise) { + Promise> &&promise) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { return promise.set_error(Status::Error(3, "Chat not found")); @@ -12174,6 +12179,10 @@ std::pair MessagesManager::get_public_message_link(FullMessageId promise.set_error(Status::Error(6, "Message not found")); return {}; } + if (message_id.is_yet_unsent()) { + promise.set_error(Status::Error(6, "Message is yet unsent")); + return {}; + } if (!message_id.is_server()) { promise.set_error(Status::Error(6, "Message is local")); return {}; @@ -12182,7 +12191,7 @@ std::pair MessagesManager::get_public_message_link(FullMessageId auto it = public_message_links_[for_group].find(full_message_id); if (it == public_message_links_[for_group].end()) { td_->create_handler(std::move(promise)) - ->send(dialog_id.get_channel_id(), message_id, for_group); + ->send(dialog_id.get_channel_id(), message_id, for_group, false); return {}; } @@ -12196,6 +12205,42 @@ void MessagesManager::on_get_public_message_link(FullMessageId full_message_id, public_message_links_[for_group][full_message_id] = {std::move(url), std::move(html)}; } +string MessagesManager::get_private_message_link(FullMessageId full_message_id, Promise &&promise) { + auto dialog_id = full_message_id.get_dialog_id(); + auto d = get_dialog_force(dialog_id); + if (d == nullptr) { + promise.set_error(Status::Error(6, "Chat not found")); + return {}; + } + if (!have_input_peer(dialog_id, AccessRights::Read)) { + promise.set_error(Status::Error(6, "Can't access the chat")); + return {}; + } + if (dialog_id.get_type() != DialogType::Channel) { + promise.set_error( + Status::Error(6, "Private message links are available only for messages in supergroups and channel chats")); + return {}; + } + + auto message_id = full_message_id.get_message_id(); + auto message = get_message_force(d, message_id, "get_private_message_link"); + if (message == nullptr) { + promise.set_error(Status::Error(6, "Message not found")); + return {}; + } + if (!message_id.is_server()) { + promise.set_error(Status::Error(6, "Message is local")); + return {}; + } + + td_->create_handler(Promise()) + ->send(dialog_id.get_channel_id(), message_id, false, true); + + promise.set_value(Unit()); + return PSTRING() << G()->shared_config().get_option_string("t_me_url", "https://t.me/") << "c/" + << dialog_id.get_channel_id().get() << "/" << message_id.get_server_message_id().get(); +} + 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"); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 25f842eb0..f3cc1af7c 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -494,6 +494,8 @@ class MessagesManager : public Actor { void on_get_public_message_link(FullMessageId full_message_id, bool for_group, string url, string html); + string get_private_message_link(FullMessageId full_message_id, Promise &&promise); + Status delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) TD_WARN_UNUSED_RESULT; Status set_dialog_draft_message(DialogId dialog_id, @@ -637,7 +639,7 @@ class MessagesManager : public Actor { const vector &message_ids, Promise &&promise); void get_dialog_statistics_url(DialogId dialog_id, const string ¶meters, - Promise> &&promise); + Promise> &&promise); void on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings); diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index 9dc2e9ede..c65d437bf 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -2581,7 +2581,8 @@ void NotificationManager::process_push_notification(string payload, Promise { } }; +class GetPrivateMessageLinkRequest : public RequestActor<> { + FullMessageId full_message_id_; + + string link_; + + void do_run(Promise &&promise) override { + link_ = td->messages_manager_->get_private_message_link(full_message_id_, std::move(promise)); + } + + void do_send_result() override { + send_result(td_api::make_object(link_)); + } + + public: + GetPrivateMessageLinkRequest(ActorShared td, uint64 request_id, int64 dialog_id, int64 message_id) + : RequestActor(std::move(td), request_id), full_message_id_(DialogId(dialog_id), MessageId(message_id)) { + } +}; + class EditMessageTextRequest : public RequestOnceActor { FullMessageId full_message_id_; tl_object_ptr reply_markup_; @@ -4910,9 +4929,15 @@ void Td::on_request(uint64 id, const td_api::getMessages &request) { } void Td::on_request(uint64 id, const td_api::getPublicMessageLink &request) { + CHECK_IS_USER(); CREATE_REQUEST(GetPublicMessageLinkRequest, request.chat_id_, request.message_id_, request.for_album_); } +void Td::on_request(uint64 id, const td_api::getPrivateMessageLink &request) { + CHECK_IS_USER(); + CREATE_REQUEST(GetPrivateMessageLinkRequest, request.chat_id_, request.message_id_); +} + 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 1b43037a1..fee34ff38 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -469,6 +469,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::getPublicMessageLink &request); + void on_request(uint64 id, const td_api::getPrivateMessageLink &request); + void on_request(uint64 id, const td_api::getFile &request); void on_request(uint64 id, const td_api::getFileDownloadedPrefixSize &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index a86fd4036..14195695a 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2330,7 +2330,7 @@ class CliClient final : public Actor { string message_ids; std::tie(chat_id, message_ids) = split(args); send_request(td_api::make_object(as_chat_id(chat_id), as_message_ids(message_ids))); - } else if (op == "gpml") { + } else if (op == "gpuml") { string chat_id; string message_id; string for_album; @@ -2338,6 +2338,11 @@ class CliClient final : public Actor { std::tie(message_id, for_album) = split(args); send_request(td_api::make_object(as_chat_id(chat_id), as_message_id(message_id), as_bool(for_album))); + } else if (op == "gprml") { + string chat_id; + 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 == "gcmbd") { string chat_id; string date;