Merge commit '3d2067475e0764d4765b41aa372e5c3c10a5af5d'

Conflicts:
	td/telegram/MessagesManager.cpp
This commit is contained in:
Andrea Cavalli 2020-10-01 14:53:43 +02:00
commit 41e5ce21ac
21 changed files with 475 additions and 59 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(TDLib VERSION 1.6.8 LANGUAGES CXX C)
project(TDLib VERSION 1.6.9 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR "lib")

View File

@ -201,7 +201,7 @@ target_link_libraries(YourTarget PRIVATE Td::TdStatic)
Or you could install `TDLib` and then reference it in your CMakeLists.txt like this:
```
find_package(Td 1.6.8 REQUIRED)
find_package(Td 1.6.9 REQUIRED)
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
```
See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt).

View File

@ -156,6 +156,7 @@ function split_file($file, $chunks, $undo) {
'(?<name>[A-Z][A-Za-z]*) : public (Td::ResultHandler|NetActor|Request)|'.
'(CREATE_REQUEST|CREATE_NO_ARGS_REQUEST)[(](?<name>[A-Z][A-Za-z]*)|'.
'(?<name>complete_pending_preauthentication_requests)|'.
'(?<name>get_message_history_slice)|'.
'(Up|Down)load[a-zA-Z]*C(?<name>allback)|(up|down)load_[a-z_]*_c(?<name>allback)_|'.
'(?<name>lazy_to_json)|'.
'(?<name>LogEvent)[^sA]|'.

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(TdExample VERSION 1.0 LANGUAGES CXX)
find_package(Td 1.6.8 REQUIRED)
find_package(Td 1.6.9 REQUIRED)
add_executable(tdjson_example tdjson_example.cpp)
target_link_libraries(tdjson_example PRIVATE Td::TdJson)

View File

@ -1,6 +1,6 @@
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011">
<Metadata>
<Identity Id="Telegram.Td.UWP" Version="1.6.8" Language="en-US" Publisher="Telegram LLC" />
<Identity Id="Telegram.Td.UWP" Version="1.6.9" Language="en-US" Publisher="Telegram LLC" />
<DisplayName>TDLib for Universal Windows Platform</DisplayName>
<Description>TDLib is a library for building Telegram clients</Description>
<MoreInfo>https://core.telegram.org/tdlib</MoreInfo>

View File

@ -618,7 +618,7 @@ secretChat id:int32 user_id:int32 state:SecretChatState is_outbound:Bool ttl:int
//@description The message was originally written by a known user @sender_user_id Identifier of the user that originally sent the message
messageForwardOriginUser sender_user_id:int32 = MessageForwardOrigin;
//@description The message was originally written by an anonimous chat administrator on behalf of the chat @sender_chat_id Identifier of the chat that originally sent the message
//@description The message was originally written by an anonymous chat administrator on behalf of the chat @sender_chat_id Identifier of the chat that originally sent the message
messageForwardOriginChat sender_chat_id:int53 = MessageForwardOrigin;
//@description The message was originally written by a user, which is hidden by their privacy settings @sender_name Name of the sender
@ -662,7 +662,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
//@description Describes a message
//@id Message identifier; unique for the chat to which the message belongs
//@sender_user_id Identifier of the user who sent the message; 0 if unknown. Currently, it is unknown for channel posts, for channel posts automatically forwarded to discussion group and for anonimously sent supergroup messages
//@sender_user_id Identifier of the user who sent the message; 0 if unknown. Currently, it is unknown for channel posts, for channel posts automatically forwarded to discussion group and for anonymously sent supergroup messages
//@sender_chat_id Identifier of the chat on behalf of which the message was sent; 0 if none
//@chat_id Chat identifier
//@sending_state Information about the sending state of the message; may be null
@ -3624,10 +3624,19 @@ getGroupsInCommon user_id:int32 offset_chat_id:int53 limit:int32 = Chats;
//@chat_id Chat identifier
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
//@offset Specify 0 to get results from exactly the from_message_id or a negative offset up to 99 to get additionally some newer messages
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater or equal to -offset. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than or equal to -offset. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
//@only_local If true, returns only messages that are available locally without sending network requests
getChatHistory chat_id:int53 from_message_id:int53 offset:int32 limit:int32 only_local:Bool = Messages;
//@description Returns messages in a message thread of a message. Can be used only if message.can_get_message_thread == true. Message thread of a channel message is in the channel's linked supergroup.
//-The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id). For optimal performance the number of returned messages is chosen by the library
//@chat_id Chat identifier
//@message_id Message identifier, which thread history needs to be returned
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
//@offset Specify 0 to get results from exactly the from_message_id or a negative offset up to 99 to get additionally some newer messages
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than or equal to -offset. Fewer messages may be returned than specified by the limit, even if the end of the message thread history has not been reached
getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 offset:int32 limit:int32 = Messages;
//@description Deletes all messages in the chat. Use Chat.can_be_deleted_only_for_self and Chat.can_be_deleted_for_all_users fields to find whether and how the method can be applied to the chat
//@chat_id Chat identifier @remove_from_chat_list Pass true if the chat should be removed from the chat list @revoke Pass true to try to delete chat history for all users
deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok;

Binary file not shown.

View File

@ -371,7 +371,7 @@ Status CallActor::do_update_call(telegram_api::phoneCallWaiting &call) {
call_state_.is_received = true;
call_state_need_flush_ = true;
int64 call_ring_timeout_ms = G()->shared_config().get_option_integer("call_ring_timeout_ms", 90000);
set_timeout_in(call_ring_timeout_ms * 0.001);
set_timeout_in(static_cast<double>(call_ring_timeout_ms) * 0.001);
}
}
@ -454,7 +454,7 @@ void CallActor::on_begin_exchanging_key() {
call_state_.type = CallState::Type::ExchangingKey;
call_state_need_flush_ = true;
int64 call_receive_timeout_ms = G()->shared_config().get_option_integer("call_receive_timeout_ms", 20000);
double timeout = call_receive_timeout_ms * 0.001;
double timeout = static_cast<double>(call_receive_timeout_ms) * 0.001;
LOG(INFO) << "Set call timeout to " << timeout;
set_timeout_in(timeout);
}
@ -632,7 +632,7 @@ void CallActor::try_send_request_query() {
auto query = G()->net_query_creator().create(tl_query);
state_ = State::WaitRequestResult;
int64 call_receive_timeout_ms = G()->shared_config().get_option_integer("call_receive_timeout_ms", 20000);
double timeout = call_receive_timeout_ms * 0.001;
double timeout = static_cast<double>(call_receive_timeout_ms) * 0.001;
LOG(INFO) << "Set call timeout to " << timeout;
set_timeout_in(timeout);
query->total_timeout_limit_ = max(timeout, 10.0);

View File

@ -3358,6 +3358,10 @@ void ContactsManager::UserFull::parse(ParserT &parser) {
if (has_photo) {
parse(photo, parser);
}
if (legacy_is_blocked) {
LOG(DEBUG) << "Ignore legacy is_blocked flag";
}
}
template <class StorerT>
@ -4059,14 +4063,19 @@ bool ContactsManager::have_input_peer_channel(const Channel *c, ChannelId channe
if (access_rights == AccessRights::Know) {
return true;
}
if (c->status.is_creator()) {
if (c->status.is_administrator()) {
return true;
}
if (c->status.is_banned()) {
return false;
}
if (c->status.is_member()) {
return true;
}
bool is_public = is_channel_public(c);
if (access_rights == AccessRights::Read) {
if (!c->username.empty() || c->has_location) {
if (is_public) {
return true;
}
if (!from_linked) {
@ -4076,22 +4085,19 @@ bool ContactsManager::have_input_peer_channel(const Channel *c, ChannelId channe
return true;
}
}
if (dialog_access_by_invite_link_.count(DialogId(channel_id))) {
if (!from_linked && dialog_access_by_invite_link_.count(DialogId(channel_id))) {
return true;
}
} else {
if (!from_linked && c->is_megagroup) {
if (!from_linked && c->is_megagroup && !td_->auth_manager_->is_bot()) {
auto linked_channel_id = get_linked_channel_id(channel_id);
if (linked_channel_id.is_valid()) {
return !c->username.empty() || c->has_location ||
return is_public ||
have_input_peer_channel(get_channel(linked_channel_id), linked_channel_id, AccessRights::Read, true);
}
}
}
if (!c->status.is_member()) {
return false;
}
return true;
return false;
}
bool ContactsManager::have_input_encrypted_peer(SecretChatId secret_chat_id, AccessRights access_rights) const {
@ -7173,7 +7179,7 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
if (is_received || !user->phone_.empty()) {
on_update_user_phone_number(u, user_id, std::move(user->phone_));
}
if (is_received || u->need_apply_min_photo) {
if (is_received || u->need_apply_min_photo || !u->is_received) {
on_update_user_photo(u, user_id, std::move(user->photo_), source);
}
if (is_received) {

View File

@ -3949,8 +3949,10 @@ bool need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId di
}
switch (dialog_id.get_type()) {
case DialogType::User:
return !contacts_manager->is_user_bot(dialog_id.get_user_id());
case DialogType::User: {
auto user_id = dialog_id.get_user_id();
return user_id == ContactsManager::get_replies_bot_user_id() || !contacts_manager->is_user_bot(user_id);
}
case DialogType::SecretChat: {
auto user_id = contacts_manager->get_secret_chat_user_id(dialog_id.get_secret_chat_id());
return !user_id.is_valid() || !contacts_manager->is_user_bot(user_id);

View File

@ -61,6 +61,10 @@ MessageReplyInfo::MessageReplyInfo(tl_object_ptr<telegram_api::messageReplies> &
}
bool MessageReplyInfo::need_update_to(const MessageReplyInfo &other) const {
if (other.is_empty() && !is_empty()) {
// ignore updates to empty reply info, because we will hide the info ourselves
// return true;
}
if (other.pts < pts) {
return false;
}

View File

@ -4512,6 +4512,7 @@ void MessagesManager::Message::store(StorerT &storer) const {
bool has_top_thread_message_id = top_thread_message_id.is_valid();
bool has_thread_draft_message = thread_draft_message != nullptr;
bool has_local_thread_message_ids = !local_thread_message_ids.empty();
bool has_linked_top_thread_message_id = linked_top_thread_message_id.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(is_channel_post);
STORE_FLAG(is_outgoing);
@ -4567,6 +4568,7 @@ void MessagesManager::Message::store(StorerT &storer) const {
STORE_FLAG(has_top_thread_message_id);
STORE_FLAG(has_thread_draft_message);
STORE_FLAG(has_local_thread_message_ids);
STORE_FLAG(has_linked_top_thread_message_id);
END_STORE_FLAGS();
}
@ -4666,6 +4668,9 @@ void MessagesManager::Message::store(StorerT &storer) const {
if (has_local_thread_message_ids) {
store(local_thread_message_ids, storer);
}
if (has_linked_top_thread_message_id) {
store(linked_top_thread_message_id, storer);
}
store_message_content(content.get(), storer);
if (has_reply_markup) {
store(reply_markup, storer);
@ -4706,6 +4711,7 @@ void MessagesManager::Message::parse(ParserT &parser) {
bool has_top_thread_message_id = false;
bool has_thread_draft_message = false;
bool has_local_thread_message_ids = false;
bool has_linked_top_thread_message_id = false;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_channel_post);
PARSE_FLAG(is_outgoing);
@ -4761,6 +4767,7 @@ void MessagesManager::Message::parse(ParserT &parser) {
PARSE_FLAG(has_top_thread_message_id);
PARSE_FLAG(has_thread_draft_message);
PARSE_FLAG(has_local_thread_message_ids);
PARSE_FLAG(has_linked_top_thread_message_id);
END_PARSE_FLAGS();
}
@ -4866,6 +4873,9 @@ void MessagesManager::Message::parse(ParserT &parser) {
if (has_local_thread_message_ids) {
parse(local_thread_message_ids, parser);
}
if (has_linked_top_thread_message_id) {
parse(linked_top_thread_message_id, parser);
}
parse_message_content(content, parser);
if (has_reply_markup) {
parse(reply_markup, parser);
@ -6363,7 +6373,8 @@ void MessagesManager::on_update_read_message_comments(DialogId dialog_id, Messag
}
auto m = get_message_force(d, message_id, "on_update_read_message_comments");
if (m == nullptr || !m->message_id.is_server()) {
if (m == nullptr || !m->message_id.is_server() || m->top_thread_message_id != m->message_id ||
!is_active_message_reply_info(dialog_id, m->reply_info)) {
return;
}
if (m->reply_info.update_max_message_ids(max_message_id, last_read_inbox_message_id, last_read_outbox_message_id)) {
@ -6526,8 +6537,13 @@ bool MessagesManager::is_active_message_reply_info(DialogId dialog_id, const Mes
bool MessagesManager::is_visible_message_reply_info(DialogId dialog_id, const Message *m) const {
CHECK(m != nullptr);
return m->message_id.is_valid() && m->message_id.is_server() && !m->had_reply_markup && m->reply_markup == nullptr &&
is_active_message_reply_info(dialog_id, m->reply_info);
if (!m->message_id.is_valid() || !m->message_id.is_server()) {
return false;
}
if (is_broadcast_channel(dialog_id) && (m->had_reply_markup || m->reply_markup != nullptr)) {
return false;
}
return is_active_message_reply_info(dialog_id, m->reply_info);
}
td_api::object_ptr<td_api::messageInteractionInfo> MessagesManager::get_message_interaction_info_object(
@ -6607,6 +6623,10 @@ bool MessagesManager::update_message_interaction_info(DialogId dialog_id, Messag
m->reply_info = std::move(reply_info);
need_update |=
m->message_id.is_valid() && m->message_id.is_server() && !m->had_reply_markup && m->reply_markup == nullptr;
if (!m->top_thread_message_id.is_valid() && !is_broadcast_channel(dialog_id) &&
is_visible_message_reply_info(dialog_id, m)) {
m->top_thread_message_id = m->message_id;
}
}
if (need_update) {
send_update_message_interaction_info(dialog_id, m);
@ -6978,12 +6998,9 @@ void MessagesManager::on_user_dialog_action(DialogId dialog_id, MessageId top_th
progress <= prev_progress) {
return;
}
if (top_thread_message_id != prev_top_thread_message_id) {
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateUserChatAction>(
dialog_id.get(), prev_top_thread_message_id.get(),
td_->contacts_manager_->get_user_id_object(user_id, "on_user_dialog_action"),
make_tl_object<td_api::chatActionCancel>()));
if (top_thread_message_id != prev_top_thread_message_id && prev_top_thread_message_id.is_valid()) {
send_update_user_chat_action(dialog_id, prev_top_thread_message_id, user_id,
td_api::make_object<td_api::chatActionCancel>());
}
if (active_actions.size() == 1u) {
LOG(DEBUG) << "Set action timeout in " << dialog_id;
@ -6992,11 +7009,10 @@ void MessagesManager::on_user_dialog_action(DialogId dialog_id, MessageId top_th
}
if (!G()->shared_config().get_option_boolean("ignore_update_user_chat_action")) {
LOG(DEBUG) << "Send action of " << user_id << " in " << dialog_id << ": " << to_string(action);
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateUserChatAction>(
dialog_id.get(), top_thread_message_id.get(),
td_->contacts_manager_->get_user_id_object(user_id, "on_user_dialog_action"), std::move(action)));
if (top_thread_message_id.is_valid()) {
send_update_user_chat_action(dialog_id, MessageId(), user_id, copy_chat_action_object(action));
}
send_update_user_chat_action(dialog_id, top_thread_message_id, user_id, std::move(action));
}
}
@ -9205,6 +9221,8 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c
// anyway pretend that there is no more messages
first_added_message_id = MessageId::min();
}
bool can_be_in_different_dialog = top_thread_message_id.is_valid() && is_broadcast_channel(dialog_id);
DialogId real_dialog_id;
Dialog *d = get_dialog(dialog_id);
CHECK(d != nullptr);
for (auto &message : messages) {
@ -9216,13 +9234,26 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c
}
if (new_full_message_id.get_dialog_id() != dialog_id) {
LOG(ERROR) << "Receive " << new_full_message_id << " instead of a message in " << dialog_id;
total_count--;
continue;
if (!can_be_in_different_dialog) {
LOG(ERROR) << "Receive " << new_full_message_id << " instead of a message in " << dialog_id;
total_count--;
continue;
} else {
if (!real_dialog_id.is_valid()) {
real_dialog_id = new_full_message_id.get_dialog_id();
found_dialog_messages_dialog_id_[random_id] = real_dialog_id;
} else if (new_full_message_id.get_dialog_id() != real_dialog_id) {
LOG(ERROR) << "Receive " << new_full_message_id << " instead of a message in " << real_dialog_id << " or "
<< dialog_id;
total_count--;
continue;
}
}
}
auto message_id = new_full_message_id.get_message_id();
if (filter == MessageSearchFilter::UnreadMention && message_id <= d->last_read_all_mentions_message_id) {
if (filter == MessageSearchFilter::UnreadMention && message_id <= d->last_read_all_mentions_message_id &&
!real_dialog_id.is_valid()) {
total_count--;
continue;
}
@ -11527,6 +11558,7 @@ void MessagesManager::on_message_ttl_expired_impl(Dialog *d, Message *m) {
m->reply_to_message_id = MessageId();
m->reply_in_dialog_id = DialogId();
m->top_thread_message_id = MessageId();
m->linked_top_thread_message_id = MessageId();
m->is_content_secret = false;
}
@ -12790,6 +12822,7 @@ std::pair<DialogId, unique_ptr<MessagesManager::Message>> MessagesManager::creat
message->reply_to_message_id = MessageId();
message->reply_in_dialog_id = DialogId();
message->top_thread_message_id = MessageId();
message->linked_top_thread_message_id = MessageId();
}
if (message_info.media_album_id != 0) {
@ -16059,7 +16092,7 @@ void MessagesManager::get_message_thread(DialogId dialog_id, MessageId message_i
ChannelId message_thread_channel_id;
MessageId top_thread_message_id;
if (m->reply_info.is_comment) {
if (!is_active_message_reply_info(dialog_id, m->reply_info)) {
if (!is_visible_message_reply_info(dialog_id, m)) {
return promise.set_error(Status::Error(400, "Message has no comments"));
}
message_thread_channel_id = m->reply_info.channel_id;
@ -16123,6 +16156,23 @@ void MessagesManager::on_get_discussion_message(DialogId dialog_id, MessageId me
}
result.message_ids.push_back(full_message_id.get_message_id());
}
if (expected_dialog_id != dialog_id && m->reply_info.is_comment && !result.message_ids.empty() &&
m->linked_top_thread_message_id != result.message_ids.back()) {
auto linked_d = get_dialog_force(expected_dialog_id);
CHECK(linked_d != nullptr);
auto linked_message_id = result.message_ids.back();
Message *linked_m = get_message_force(linked_d, linked_message_id, "on_get_discussion_message");
CHECK(linked_m != nullptr && linked_m->message_id.is_server());
if (linked_m->top_thread_message_id == linked_m->message_id &&
is_active_message_reply_info(expected_dialog_id, linked_m->reply_info)) {
if (m->linked_top_thread_message_id.is_valid()) {
LOG(ERROR) << "Comment message identifier for " << message_id << " in " << dialog_id << " changed from "
<< m->linked_top_thread_message_id << " to " << linked_message_id;
}
m->linked_top_thread_message_id = linked_message_id;
on_dialog_updated(dialog_id, "on_get_discussion_message");
}
}
result.dialog_id = expected_dialog_id;
promise.set_value(std::move(result));
}
@ -19309,7 +19359,7 @@ tl_object_ptr<td_api::messages> MessagesManager::get_dialog_history(DialogId dia
return nullptr;
}
if (offset < -limit) {
promise.set_error(Status::Error(5, "Parameter offset must not be less than -limit"));
promise.set_error(Status::Error(5, "Parameter offset must be greater than or equal to -limit"));
return nullptr;
}
bool is_limit_increased = false;
@ -19707,6 +19757,202 @@ void MessagesManager::on_read_history_finished(DialogId dialog_id, MessageId top
}
}
template <class T, class It>
vector<MessageId> MessagesManager::get_message_history_slice(const T &begin, It it, const T &end,
MessageId from_message_id, int32 offset, int32 limit) {
int32 left_offset = -offset;
int32 left_limit = limit + offset;
while (left_offset > 0 && it != end) {
++it;
left_offset--;
left_limit++;
}
vector<MessageId> message_ids;
while (left_limit > 0 && it != begin) {
--it;
left_limit--;
message_ids.push_back(*it);
}
return message_ids;
}
std::pair<DialogId, vector<MessageId>> MessagesManager::get_message_thread_history(
DialogId dialog_id, MessageId message_id, MessageId from_message_id, int32 offset, int32 limit, int64 &random_id,
Promise<Unit> &&promise) {
if (limit <= 0) {
promise.set_error(Status::Error(3, "Parameter limit must be positive"));
return {};
}
if (limit > MAX_GET_HISTORY) {
limit = MAX_GET_HISTORY;
}
if (offset > 0) {
promise.set_error(Status::Error(5, "Parameter offset must be non-positive"));
return {};
}
if (offset <= -MAX_GET_HISTORY) {
promise.set_error(Status::Error(5, "Parameter offset must be greater than -100"));
return {};
}
if (offset < -limit) {
promise.set_error(Status::Error(5, "Parameter offset must be greater than or equal to -limit"));
return {};
}
bool is_limit_increased = false;
if (limit == -offset) {
limit++;
is_limit_increased = true;
}
CHECK(0 < limit && limit <= MAX_GET_HISTORY);
CHECK(-limit < offset && offset <= 0);
Dialog *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(5, "Can't access the chat"));
return {};
}
if (dialog_id.get_type() != DialogType::Channel) {
promise.set_error(Status::Error(400, "Can't get message thread history in the chat"));
return {};
}
if (from_message_id == MessageId() || from_message_id.get() > MessageId::max().get()) {
from_message_id = MessageId::max();
}
if (!from_message_id.is_valid()) {
promise.set_error(Status::Error(3, "Parameter from_message_id must be identifier of the chat message or 0"));
return {};
}
DialogId message_thread_dialog_id;
MessageId top_thread_message_id;
{
Message *m = get_message_force(d, message_id, "get_message_thread_history 1");
if (m == nullptr) {
promise.set_error(Status::Error(400, "Message not found"));
return {};
}
if (m->reply_info.is_comment) {
if (!is_visible_message_reply_info(dialog_id, m)) {
promise.set_error(Status::Error(400, "Message has no comments"));
return {};
}
if (!m->linked_top_thread_message_id.is_valid()) {
get_message_thread(
dialog_id, message_id,
PromiseCreator::lambda([promise = std::move(promise)](Result<MessageThreadInfo> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(Unit());
}
}));
return {};
}
message_thread_dialog_id = DialogId(m->reply_info.channel_id);
top_thread_message_id = m->linked_top_thread_message_id;
} else {
if (!m->top_thread_message_id.is_valid()) {
promise.set_error(Status::Error(400, "Message has no thread"));
return {};
}
message_thread_dialog_id = dialog_id;
top_thread_message_id = m->top_thread_message_id;
}
}
CHECK(top_thread_message_id.is_valid());
if (random_id != 0) {
// request has already been sent before
auto it = found_dialog_messages_.find(random_id);
CHECK(it != found_dialog_messages_.end());
auto result = std::move(it->second.second);
found_dialog_messages_.erase(it);
auto dialog_id_it = found_dialog_messages_dialog_id_.find(random_id);
if (dialog_id_it != found_dialog_messages_dialog_id_.end()) {
dialog_id = dialog_id_it->second;
found_dialog_messages_dialog_id_.erase(dialog_id_it);
d = get_dialog(dialog_id);
CHECK(d != nullptr);
}
if (dialog_id != message_thread_dialog_id) {
promise.set_error(Status::Error(500, "Receive messages in an unexpected chat"));
return {};
}
auto yet_unsent_it = d->yet_unsent_thread_message_ids.find(top_thread_message_id);
if (yet_unsent_it != d->yet_unsent_thread_message_ids.end()) {
const std::set<MessageId> &message_ids = yet_unsent_it->second;
auto merge_message_ids = get_message_history_slice(message_ids.begin(), message_ids.lower_bound(from_message_id),
message_ids.end(), from_message_id, offset, limit);
vector<MessageId> new_result(result.size() + merge_message_ids.size());
std::merge(result.begin(), result.end(), merge_message_ids.begin(), merge_message_ids.end(), new_result.begin(),
std::greater<>());
result = std::move(new_result);
}
Message *top_m = get_message_force(d, top_thread_message_id, "get_message_thread_history 2");
if (top_m != nullptr && !top_m->local_thread_message_ids.empty()) {
vector<MessageId> &message_ids = top_m->local_thread_message_ids;
vector<MessageId> merge_message_ids;
while (true) {
merge_message_ids = get_message_history_slice(
message_ids.begin(), std::lower_bound(message_ids.begin(), message_ids.end(), from_message_id),
message_ids.end(), from_message_id, offset, limit);
bool found_deleted = false;
for (auto local_message_id : merge_message_ids) {
Message *local_m = get_message_force(d, local_message_id, "get_message_thread_history 3");
if (local_m == nullptr) {
auto local_it = std::lower_bound(message_ids.begin(), message_ids.end(), local_message_id);
CHECK(local_it != message_ids.end() && *local_it == local_message_id);
message_ids.erase(local_it);
found_deleted = true;
}
}
if (!found_deleted) {
break;
}
on_message_changed(d, top_m, false, "get_message_thread_history");
}
vector<MessageId> new_result(result.size() + merge_message_ids.size());
std::merge(result.begin(), result.end(), merge_message_ids.begin(), merge_message_ids.end(), new_result.begin(),
std::greater<>());
result = std::move(new_result);
}
if (is_limit_increased) {
limit--;
}
std::reverse(result.begin(), result.end());
result = get_message_history_slice(result.begin(), std::lower_bound(result.begin(), result.end(), from_message_id),
result.end(), from_message_id, offset, limit);
LOG(INFO) << "Return " << result.size() << " messages in result to getMessageThreadHistory";
promise.set_value(Unit());
return {dialog_id, std::move(result)};
}
do {
random_id = Random::secure_int64();
} while (random_id == 0 || found_dialog_messages_.find(random_id) != found_dialog_messages_.end());
found_dialog_messages_[random_id]; // reserve place for result
td_->create_handler<SearchMessagesQuery>(std::move(promise))
->send(dialog_id, string(), UserId(), nullptr, from_message_id.get_next_server_message_id(), offset, limit,
MessageSearchFilter::Empty, message_id, random_id);
return {};
}
std::pair<int32, vector<MessageId>> MessagesManager::search_dialog_messages(
DialogId dialog_id, const string &query, UserId sender_user_id, MessageId from_message_id, int32 offset,
int32 limit, MessageSearchFilter filter, MessageId top_thread_message_id, int64 &random_id, bool use_db,
@ -19715,6 +19961,7 @@ std::pair<int32, vector<MessageId>> MessagesManager::search_dialog_messages(
// request has already been sent before
auto it = found_dialog_messages_.find(random_id);
if (it != found_dialog_messages_.end()) {
CHECK(found_dialog_messages_dialog_id_.count(random_id) == 0);
auto result = std::move(it->second);
found_dialog_messages_.erase(it);
promise.set_value(Unit());
@ -20337,7 +20584,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo
return promise.set_error(Status::Error(500, "Request aborted"));
}
if (r_messages.is_error()) {
LOG(ERROR) << r_messages.error();
LOG(ERROR) << "Failed to get messages from the database: " << r_messages.error();
if (first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat &&
filter != MessageSearchFilter::FailedToSend) {
found_dialog_messages_.erase(random_id);
@ -20773,6 +21020,7 @@ int32 MessagesManager::get_dialog_message_count(DialogId dialog_id, MessageSearc
CHECK(it != found_dialog_messages_.end());
auto result = std::move(it->second);
found_dialog_messages_.erase(it);
CHECK(found_dialog_messages_dialog_id_.count(random_id) == 0);
promise.set_value(Unit());
return result.first;
}
@ -21553,8 +21801,9 @@ tl_object_ptr<td_api::message> MessagesManager::get_message_object(DialogId dial
bool can_be_edited = for_event_log ? false : can_edit_message(dialog_id, m, false, td_->auth_manager_->is_bot());
bool can_be_forwarded = for_event_log ? false : can_forward_message(dialog_id, m);
bool can_get_statistics = for_event_log ? false : can_get_message_statistics(dialog_id, m);
bool can_get_message_thread =
for_event_log || is_scheduled ? false : !m->reply_info.is_empty() || m->top_thread_message_id.is_valid();
bool can_get_message_thread = for_event_log || is_scheduled ? false
: is_visible_message_reply_info(dialog_id, m) ||
m->top_thread_message_id.is_valid();
auto via_bot_user_id = td_->contacts_manager_->get_user_id_object(m->via_bot_user_id, "via_bot_user_id");
auto media_album_id = for_event_log ? static_cast<int64>(0) : m->media_album_id;
auto reply_to_message_id = for_event_log ? static_cast<int64>(0) : m->reply_to_message_id.get();
@ -27067,6 +27316,21 @@ void MessagesManager::send_update_chat_has_scheduled_messages(Dialog *d, bool fr
td_api::make_object<td_api::updateChatHasScheduledMessages>(d->dialog_id.get(), has_scheduled_messages));
}
void MessagesManager::send_update_user_chat_action(DialogId dialog_id, MessageId top_thread_message_id, UserId user_id,
td_api::object_ptr<td_api::ChatAction> action) {
if (td_->auth_manager_->is_bot()) {
return;
}
LOG(DEBUG) << "Send action of " << user_id << " in thread of " << top_thread_message_id << " in " << dialog_id << ": "
<< to_string(action);
send_closure(
G()->td(), &Td::send_update,
make_tl_object<td_api::updateUserChatAction>(
dialog_id.get(), top_thread_message_id.get(),
td_->contacts_manager_->get_user_id_object(user_id, "send_update_user_chat_action"), std::move(action)));
}
void MessagesManager::on_send_message_get_quick_ack(int64 random_id) {
auto it = being_sent_messages_.find(random_id);
if (it == being_sent_messages_.end()) {
@ -28698,6 +28962,52 @@ bool MessagesManager::is_dialog_action_unneeded(DialogId dialog_id) const {
return false;
}
tl_object_ptr<td_api::ChatAction> MessagesManager::copy_chat_action_object(
const tl_object_ptr<td_api::ChatAction> &action) {
CHECK(action != nullptr);
switch (action->get_id()) {
case td_api::chatActionCancel::ID:
return make_tl_object<td_api::chatActionCancel>();
case td_api::chatActionTyping::ID:
return make_tl_object<td_api::chatActionTyping>();
case td_api::chatActionRecordingVideo::ID:
return make_tl_object<td_api::chatActionRecordingVideo>();
case td_api::chatActionUploadingVideo::ID: {
auto progress = static_cast<const td_api::chatActionUploadingVideo &>(*action).progress_;
return make_tl_object<td_api::chatActionUploadingVideo>(progress);
}
case td_api::chatActionRecordingVoiceNote::ID:
return make_tl_object<td_api::chatActionRecordingVoiceNote>();
case td_api::chatActionUploadingVoiceNote::ID: {
auto progress = static_cast<const td_api::chatActionUploadingVoiceNote &>(*action).progress_;
return make_tl_object<td_api::chatActionUploadingVoiceNote>(progress);
}
case td_api::chatActionUploadingPhoto::ID: {
auto progress = static_cast<const td_api::chatActionUploadingPhoto &>(*action).progress_;
return make_tl_object<td_api::chatActionUploadingPhoto>(progress);
}
case td_api::chatActionUploadingDocument::ID: {
auto progress = static_cast<const td_api::chatActionUploadingDocument &>(*action).progress_;
return make_tl_object<td_api::chatActionUploadingDocument>(progress);
}
case td_api::chatActionChoosingLocation::ID:
return make_tl_object<td_api::chatActionChoosingLocation>();
case td_api::chatActionChoosingContact::ID:
return make_tl_object<td_api::chatActionChoosingContact>();
case td_api::chatActionStartPlayingGame::ID:
return make_tl_object<td_api::chatActionStartPlayingGame>();
case td_api::chatActionRecordingVideoNote::ID:
return make_tl_object<td_api::chatActionRecordingVideoNote>();
case td_api::chatActionUploadingVideoNote::ID: {
auto progress = static_cast<const td_api::chatActionUploadingVideoNote &>(*action).progress_;
return make_tl_object<td_api::chatActionUploadingVideoNote>(progress);
}
default:
UNREACHABLE();
return nullptr;
}
}
void MessagesManager::send_dialog_action(DialogId dialog_id, MessageId top_thread_message_id,
const tl_object_ptr<td_api::ChatAction> &action, Promise<Unit> &&promise) {
if (action == nullptr) {
@ -30332,6 +30642,10 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
}
}
}
if (!message->top_thread_message_id.is_valid() && !is_broadcast_channel(dialog_id) &&
is_visible_message_reply_info(dialog_id, message.get())) {
message->top_thread_message_id = message_id;
}
if (!message_id.is_scheduled() && message_id <= d->last_clear_history_message_id) {
LOG(INFO) << "Skip adding cleared " << message_id << " to " << dialog_id << " from " << source;

View File

@ -701,6 +701,11 @@ class MessagesManager : public Actor {
int32 limit, int left_tries, bool only_local,
Promise<Unit> &&promise);
std::pair<DialogId, vector<MessageId>> get_message_thread_history(DialogId dialog_id, MessageId message_id,
MessageId from_message_id, int32 offset,
int32 limit, int64 &random_id,
Promise<Unit> &&promise);
std::pair<int32, vector<MessageId>> search_dialog_messages(DialogId dialog_id, const string &query,
UserId sender_user_id, MessageId from_message_id,
int32 offset, int32 limit, MessageSearchFilter filter,
@ -1035,6 +1040,7 @@ class MessagesManager : public Actor {
int64 reply_to_random_id = 0; // for send_message
DialogId reply_in_dialog_id;
MessageId top_thread_message_id;
MessageId linked_top_thread_message_id;
vector<MessageId> local_thread_message_ids;
UserId via_bot_user_id;
@ -1986,6 +1992,10 @@ class MessagesManager : public Actor {
void on_update_dialog_online_member_count_timeout(DialogId dialog_id);
template <class T, class It>
vector<MessageId> get_message_history_slice(const T &begin, It it, const T &end, MessageId from_message_id,
int32 offset, int32 limit);
void preload_newer_messages(const Dialog *d, MessageId max_message_id);
void preload_older_messages(const Dialog *d, MessageId min_message_id);
@ -2205,6 +2215,9 @@ class MessagesManager : public Actor {
void send_update_chat_has_scheduled_messages(Dialog *d, bool from_deletion);
void send_update_user_chat_action(DialogId dialog_id, MessageId top_thread_message_id, UserId user_id,
td_api::object_ptr<td_api::ChatAction> action);
void repair_dialog_action_bar(Dialog *d, const char *source);
void hide_dialog_action_bar(Dialog *d);
@ -2340,6 +2353,8 @@ class MessagesManager : public Actor {
bool update_dialog_silent_send_message(Dialog *d, bool silent_send_message);
static tl_object_ptr<td_api::ChatAction> copy_chat_action_object(const tl_object_ptr<td_api::ChatAction> &action);
bool is_dialog_action_unneeded(DialogId dialog_id) const;
void on_send_dialog_action_timeout(DialogId dialog_id);
@ -3056,7 +3071,8 @@ class MessagesManager : public Actor {
std::unordered_map<int64, FullMessageId> get_dialog_message_by_date_results_;
std::unordered_map<int64, std::pair<int32, vector<MessageId>>>
found_dialog_messages_; // random_id -> [total_count, [message_id]...]
found_dialog_messages_; // random_id -> [total_count, [message_id]...]
std::unordered_map<int64, DialogId> found_dialog_messages_dialog_id_; // random_id -> dialog_id
std::unordered_map<int64, std::pair<int32, vector<FullMessageId>>>
found_messages_; // random_id -> [total_count, [full_message_id]...]
std::unordered_map<int64, std::pair<int32, vector<FullMessageId>>>

View File

@ -1398,6 +1398,39 @@ class GetChatHistoryRequest : public RequestActor<> {
}
};
class GetMessageThreadHistoryRequest : public RequestActor<> {
DialogId dialog_id_;
MessageId message_id_;
MessageId from_message_id_;
int32 offset_;
int32 limit_;
int64 random_id_;
std::pair<DialogId, vector<MessageId>> messages_;
void do_run(Promise<Unit> &&promise) override {
messages_ = td->messages_manager_->get_message_thread_history(dialog_id_, message_id_, from_message_id_, offset_,
limit_, random_id_, std::move(promise));
}
void do_send_result() override {
send_result(td->messages_manager_->get_messages_object(-1, messages_.first, messages_.second));
}
public:
GetMessageThreadHistoryRequest(ActorShared<Td> td, uint64 request_id, int64 dialog_id, int64 message_id,
int64 from_message_id, int32 offset, int32 limit)
: RequestActor(std::move(td), request_id)
, dialog_id_(dialog_id)
, message_id_(message_id)
, from_message_id_(from_message_id)
, offset_(offset)
, limit_(limit)
, random_id_(0) {
set_tries(3);
}
};
class SearchChatMessagesRequest : public RequestActor<> {
DialogId dialog_id_;
string query_;
@ -3096,8 +3129,9 @@ void Td::on_online_updated(bool force, bool send_update) {
update_status_query_ = create_handler<UpdateStatusQuery>()->send(!is_online_);
}
if (is_online_) {
alarm_timeout_.set_timeout_in(ONLINE_ALARM_ID,
G()->shared_config().get_option_integer("online_update_period_ms", 210000) * 1e-3);
alarm_timeout_.set_timeout_in(
ONLINE_ALARM_ID,
static_cast<double>(G()->shared_config().get_option_integer("online_update_period_ms", 210000)) * 1e-3);
} else {
alarm_timeout_.cancel_timeout(ONLINE_ALARM_ID);
}
@ -5506,6 +5540,12 @@ void Td::on_request(uint64 id, const td_api::deleteChatHistory &request) {
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getMessageThreadHistory &request) {
CHECK_IS_USER();
CREATE_REQUEST(GetMessageThreadHistoryRequest, request.chat_id_, request.message_id_, request.from_message_id_,
request.offset_, request.limit_);
}
void Td::on_request(uint64 id, td_api::searchChatMessages &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.query_);

View File

@ -237,7 +237,7 @@ class Td final : public NetQueryCallback {
static td_api::object_ptr<td_api::Object> static_request(td_api::object_ptr<td_api::Function> function);
private:
static constexpr const char *TDLIB_VERSION = "1.6.8";
static constexpr const char *TDLIB_VERSION = "1.6.9";
static constexpr int64 ONLINE_ALARM_ID = 0;
static constexpr int64 PING_SERVER_ALARM_ID = -1;
static constexpr int32 PING_SERVER_TIMEOUT = 300;
@ -578,6 +578,8 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, const td_api::deleteChatHistory &request);
void on_request(uint64 id, const td_api::getMessageThreadHistory &request);
void on_request(uint64 id, td_api::searchChatMessages &request);
void on_request(uint64 id, td_api::searchSecretMessages &request);

View File

@ -1058,6 +1058,12 @@ void UpdatesManager::on_get_difference(tl_object_ptr<telegram_api::updates_Diffe
auto difference = move_tl_object_as<telegram_api::updates_differenceEmpty>(difference_ptr);
set_date(difference->date_, false, "on_get_difference_empty");
seq_ = difference->seq_;
if (!pending_seq_updates_.empty()) {
LOG(ERROR) << "Drop " << pending_seq_updates_.size() << " pending seq updates after receive empty difference";
}
if (!pending_qts_updates_.empty()) {
LOG(ERROR) << "Drop " << pending_qts_updates_.size() << " pending qts updates after receive empty difference";
}
break;
}
case telegram_api::updates_difference::ID: {

View File

@ -1821,13 +1821,17 @@ class CliClient final : public Actor {
}
send_request(td_api::make_object<td_api::getGroupsInCommon>(as_user_id(user_id), as_chat_id(offset_chat_id),
to_integer<int32>(limit)));
} else if (op == "gh" || op == "GetHistory" || op == "ghl") {
} else if (op == "gh" || op == "GetHistory" || op == "ghl" || op == "gmth") {
string chat_id;
string thread_message_id;
string from_message_id;
string offset;
string limit;
std::tie(chat_id, args) = split(args);
if (op == "gmth") {
std::tie(thread_message_id, args) = split(args);
}
std::tie(from_message_id, args) = split(args);
if (from_message_id.empty()) {
from_message_id = "0";
@ -1842,6 +1846,10 @@ class CliClient final : public Actor {
}
if (!args.empty()) {
LOG(ERROR) << "Wrong parameters to function getChatHistory specified";
} else if (op == "gmth") {
send_request(td_api::make_object<td_api::getMessageThreadHistory>(
as_chat_id(chat_id), as_message_id(thread_message_id), as_message_id(from_message_id),
to_integer<int32>(offset), to_integer<int32>(limit)));
} else {
send_request(td_api::make_object<td_api::getChatHistory>(as_chat_id(chat_id), as_message_id(from_message_id),
to_integer<int32>(offset), to_integer<int32>(limit),

View File

@ -6,6 +6,8 @@
//
#pragma once
#include "td/telegram/Global.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"

View File

@ -9,15 +9,19 @@
#include "td/utils/logging.h"
#include "td/utils/PathView.h"
#if TD_PORT_WINDOWS && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#if TD_PORT_WINDOWS
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#include "td/utils/port/wstring_convert.h"
#endif
#endif
#include <unordered_map>
#if TD_PORT_WINDOWS && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#if TD_PORT_WINDOWS
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#include <shellapi.h>
#endif
#endif
namespace td {
@ -70,7 +74,8 @@ void OptionParser::add_check(std::function<Status()> check) {
}
Result<vector<char *>> OptionParser::run(int argc, char *argv[], int expected_non_option_count) {
#if TD_PORT_WINDOWS && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#if TD_PORT_WINDOWS
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
LPWSTR *utf16_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (utf16_argv == nullptr) {
return Status::Error("Failed to parse command line");
@ -83,6 +88,7 @@ Result<vector<char *>> OptionParser::run(int argc, char *argv[], int expected_no
}
LocalFree(utf16_argv);
argv = &args[0];
#endif
#endif
std::unordered_map<char, const Option *> short_options;

View File

@ -548,7 +548,7 @@ void aes_cbc_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlic
Evp evp;
evp.init_encrypt_cbc(aes_key);
evp.init_iv(aes_iv);
evp.encrypt(from.ubegin(), to.ubegin(), from.size());
evp.encrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
aes_iv.copy_from(to.substr(from.size() - 16));
}
@ -560,7 +560,7 @@ void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlic
evp.init_decrypt_cbc(aes_key);
evp.init_iv(aes_iv);
aes_iv.copy_from(from.substr(from.size() - 16));
evp.decrypt(from.ubegin(), to.ubegin(), from.size());
evp.decrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
}
struct AesCbcState::Impl {
@ -591,7 +591,7 @@ void AesCbcState::encrypt(Slice from, MutableSlice to) {
} else {
CHECK(is_encrypt_);
}
ctx_->evp_.encrypt(from.ubegin(), to.ubegin(), from.size());
ctx_->evp_.encrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
raw_.iv.as_mutable_slice().copy_from(to.substr(from.size() - 16));
}
@ -611,7 +611,7 @@ void AesCbcState::decrypt(Slice from, MutableSlice to) {
CHECK(!is_encrypt_);
}
raw_.iv.as_mutable_slice().copy_from(from.substr(from.size() - 16));
ctx_->evp_.decrypt(from.ubegin(), to.ubegin(), from.size());
ctx_->evp_.decrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
}
struct AesCtrState::Impl {
@ -633,7 +633,7 @@ void AesCtrState::init(Slice key, Slice iv) {
void AesCtrState::encrypt(Slice from, MutableSlice to) {
CHECK(from.size() <= to.size());
ctx_->evp_.encrypt(from.ubegin(), to.ubegin(), from.size());
ctx_->evp_.encrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
}
void AesCtrState::decrypt(Slice from, MutableSlice to) {

View File

@ -183,7 +183,7 @@ class messages_sendEncrypted final {
BufferSlice data_;
messages_sendEncrypted() = default;
static const int32 ID = -1451792525;
static const int32 ID = 1157265941;
explicit messages_sendEncrypted(TlBufferParser &p)
: flags_(TlFetchInt::parse(p))