Add td_api::getMessageLinkInfo.

GitOrigin-RevId: 34213fdada780b9bb8fe6a6db5a0de3be097b55c
This commit is contained in:
levlam 2019-08-05 12:12:59 +03:00
parent b4cc4fea93
commit 5ca0a72ddf
8 changed files with 282 additions and 10 deletions

View File

@ -641,7 +641,7 @@ chatInviteLink invite_link:string = ChatInviteLink;
//@photo Chat photo; may be null //@photo Chat photo; may be null
//@member_count Number of members //@member_count Number of members
//@member_user_ids User identifiers of some chat members that may be known to the current user //@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<int32> is_public:Bool = ChatInviteLinkInfo; chatInviteLinkInfo chat_id:int53 type:ChatType title:string photo:chatPhoto member_count:int32 member_user_ids:vector<int32> is_public:Bool = ChatInviteLinkInfo;
@ -2005,7 +2005,7 @@ checkChatUsernameResultUsernameInvalid = CheckChatUsernameResult;
//@description The username is occupied //@description The username is occupied
checkChatUsernameResultUsernameOccupied = CheckChatUsernameResult; 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; checkChatUsernameResultPublicChatsTooMuch = CheckChatUsernameResult;
//@description The user can't be a member of a public supergroup //@description The user can't be a member of a public supergroup
@ -2278,9 +2278,16 @@ chatReportReasonCopyright = ChatReportReason;
chatReportReasonCustom text:string = 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; 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 //@description Contains a part of a file @data File bytes
filePart data:bytes = FilePart; 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 //@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; 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; 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; 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 //@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message //@message_id Identifier of the message
//@for_album Pass true if a link for a whole media album should be returned //@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 //@message_id Identifier of the message
getMessageLink chat_id:int53 message_id:int53 = HttpUrl; 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 //@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 //@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

Binary file not shown.

View File

@ -9528,7 +9528,7 @@ void ContactsManager::reload_channel(ChannelId channel_id, Promise<Unit> &&promi
have_channel_force(channel_id); have_channel_force(channel_id);
auto input_channel = get_input_channel(channel_id); auto input_channel = get_input_channel(channel_id);
if (input_channel == nullptr) { if (input_channel == nullptr) {
return promise.set_error(Status::Error(6, "Supergroup info not found")); input_channel = make_tl_object<telegram_api::inputChannel>(channel_id.get(), 0);
} }
// there is no much reason to combine different requests into one request // there is no much reason to combine different requests into one request

View File

@ -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<Unit> &&promise, void MessagesManager::get_message_force_from_server(Dialog *d, MessageId message_id, Promise<Unit> &&promise,
tl_object_ptr<telegram_api::InputMessage> input_message) { tl_object_ptr<telegram_api::InputMessage> 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"); auto m = get_message_force(d, message_id, "get_message_force_from_server");
if (m == nullptr && message_id.is_valid() && message_id.is_server()) { if (m == nullptr && message_id.is_valid() && message_id.is_server()) {
auto dialog_type = d->dialog_id.get_type(); 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(); << dialog_id.get_channel_id().get() << "/" << message_id.get_server_message_id().get();
} }
Result<MessagesManager::MessageLinkInfo> 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<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 (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<int32>(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<int32>(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<MessageLinkInfo> &&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<Unit> &&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<ResolveUsernameQuery>(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<MessageLinkInfo> &&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<Unit> &&result) mutable {
promise.set_value(std::move(info));
}));
}
td_api::object_ptr<td_api::messageLinkInfo> 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<td_api::message> 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<td_api::messageLinkInfo>(is_public, dialog_id.get(), std::move(message), for_album);
}
Status MessagesManager::delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) { Status MessagesManager::delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) {
if (td_->auth_manager_->is_bot()) { if (td_->auth_manager_->is_bot()) {
return Status::Error(6, "Bots can't delete chat reply markup"); 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) { DialogId MessagesManager::resolve_dialog_username(const string &username) const {
auto it = resolved_usernames_.find(clean_username(username)); auto cleaned_username = clean_username(username);
auto it = resolved_usernames_.find(cleaned_username);
if (it != resolved_usernames_.end()) { if (it != resolved_usernames_.end()) {
return it->second.dialog_id; 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()) { if (it2 != inaccessible_resolved_usernames_.end()) {
return it2->second; return it2->second;
} }

View File

@ -326,7 +326,7 @@ class MessagesManager : public Actor {
void clear_recently_found_dialogs(); 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<Unit> &&promise); DialogId search_public_dialog(const string &username_to_search, bool force, Promise<Unit> &&promise);
@ -496,6 +496,18 @@ class MessagesManager : public Actor {
string get_message_link(FullMessageId full_message_id, Promise<Unit> &&promise); string get_message_link(FullMessageId full_message_id, Promise<Unit> &&promise);
struct MessageLinkInfo {
string username;
// or
ChannelId channel_id;
MessageId message_id;
bool is_single = false;
};
void get_message_link_info(Slice url, Promise<MessageLinkInfo> &&promise);
td_api::object_ptr<td_api::messageLinkInfo> 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 delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) TD_WARN_UNUSED_RESULT;
Status set_dialog_draft_message(DialogId dialog_id, 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_loop(double server_now);
void ttl_db_on_result(Result<std::pair<std::vector<std::pair<DialogId, BufferSlice>>, int32>> r_result, bool dummy); void ttl_db_on_result(Result<std::pair<std::vector<std::pair<DialogId, BufferSlice>>, int32>> r_result, bool dummy);
static Result<MessageLinkInfo> get_message_link_info(Slice url);
void on_get_message_link_dialog(MessageLinkInfo &&info, Promise<MessageLinkInfo> &&promise);
static MessageId get_first_database_message_id_by_index(const Dialog *d, SearchMessagesFilter filter); 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, void on_search_dialog_messages_db_result(int64 random_id, DialogId dialog_id, MessageId from_message_id,

View File

@ -1175,6 +1175,33 @@ class GetMessageLinkRequest : public RequestActor<> {
} }
}; };
class GetMessageLinkInfoRequest : public RequestActor<MessagesManager::MessageLinkInfo> {
string url_;
MessagesManager::MessageLinkInfo message_link_info_;
void do_run(Promise<MessagesManager::MessageLinkInfo> &&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> td, uint64 request_id, string url)
: RequestActor(std::move(td), request_id), url_(std::move(url)) {
}
};
class EditMessageTextRequest : public RequestOnceActor { class EditMessageTextRequest : public RequestOnceActor {
FullMessageId full_message_id_; FullMessageId full_message_id_;
tl_object_ptr<td_api::ReplyMarkup> reply_markup_; tl_object_ptr<td_api::ReplyMarkup> 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_); 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) { 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))); send_closure(actor_id(this), &Td::send_result, id, file_manager_->get_file_object(FileId(request.file_id_, 0)));
} }

View File

@ -474,6 +474,8 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, const td_api::getMessageLink &request); 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, const td_api::getFile &request);
void on_request(uint64 id, td_api::getRemoteFile &request); void on_request(uint64 id, td_api::getRemoteFile &request);

View File

@ -2352,6 +2352,8 @@ class CliClient final : public Actor {
string message_id; string message_id;
std::tie(chat_id, message_id) = split(args); std::tie(chat_id, message_id) = split(args);
send_request(td_api::make_object<td_api::getMessageLink>(as_chat_id(chat_id), as_message_id(message_id))); send_request(td_api::make_object<td_api::getMessageLink>(as_chat_id(chat_id), as_message_id(message_id)));
} else if (op == "gmli") {
send_request(td_api::make_object<td_api::getMessageLinkInfo>(args));
} else if (op == "gcmbd") { } else if (op == "gcmbd") {
string chat_id; string chat_id;
string date; string date;