// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/MessageId.h" #include "td/utils/algorithm.h" #include "td/utils/logging.h" #include "td/utils/misc.h" namespace td { MessageId::MessageId(ScheduledServerMessageId server_message_id, int32 send_date, bool force) { if (send_date <= (1 << 30)) { LOG(ERROR) << "Scheduled message send date " << send_date << " is in the past"; return; } if (!server_message_id.is_valid() && !force) { LOG(ERROR) << "Scheduled message ID " << server_message_id.get() << " is invalid"; return; } id = (static_cast(send_date - (1 << 30)) << 21) | (static_cast(server_message_id.get()) << 3) | SCHEDULED_MASK; } MessageId MessageId::get_message_id(const telegram_api::Message *message_ptr, bool is_scheduled) { CHECK(message_ptr != nullptr); switch (message_ptr->get_id()) { case telegram_api::messageEmpty::ID: { auto message = static_cast(message_ptr); return is_scheduled ? MessageId() : MessageId(ServerMessageId(message->id_)); } case telegram_api::message::ID: { auto message = static_cast(message_ptr); return is_scheduled ? MessageId(ScheduledServerMessageId(message->id_), message->date_) : MessageId(ServerMessageId(message->id_)); } case telegram_api::messageService::ID: { auto message = static_cast(message_ptr); return is_scheduled ? MessageId(ScheduledServerMessageId(message->id_), message->date_) : MessageId(ServerMessageId(message->id_)); } default: UNREACHABLE(); return MessageId(); } } MessageId MessageId::get_message_id(const tl_object_ptr &message_ptr, bool is_scheduled) { return get_message_id(message_ptr.get(), is_scheduled); } MessageId MessageId::get_max_message_id(const vector> &messages) { MessageId max_message_id; for (auto &message : messages) { auto message_id = get_message_id(message, false); if (message_id > max_message_id) { max_message_id = message_id; } } return max_message_id; } vector MessageId::get_message_ids(const vector &input_message_ids) { return transform(input_message_ids, [](int64 input_message_id) { return MessageId(input_message_id); }); } vector MessageId::get_server_message_ids(const vector &message_ids) { return transform(message_ids, [](MessageId message_id) { return message_id.get_server_message_id().get(); }); } vector MessageId::get_scheduled_server_message_ids(const vector &message_ids) { return transform(message_ids, [](MessageId message_id) { return message_id.get_scheduled_server_message_id().get(); }); } bool MessageId::is_valid() const { if (id <= 0 || id > max().get()) { return false; } if ((id & FULL_TYPE_MASK) == 0) { return true; } int32 type = (id & TYPE_MASK); return type == TYPE_YET_UNSENT || type == TYPE_LOCAL; } bool MessageId::is_valid_scheduled() const { if (id <= 0 || id > (static_cast(1) << 51)) { return false; } int32 type = (id & TYPE_MASK); return type == SCHEDULED_MASK || type == (SCHEDULED_MASK | TYPE_YET_UNSENT) || type == (SCHEDULED_MASK | TYPE_LOCAL); } bool MessageId::is_valid_sponsored() const { if (id <= max().get() || id > (static_cast(1) << 51)) { return false; } int32 type = (id & TYPE_MASK); return type == TYPE_LOCAL; } MessageType MessageId::get_type() const { if (id <= 0 || id > (static_cast(1) << 51)) { return MessageType::None; } if (is_scheduled()) { switch (id & TYPE_MASK) { case SCHEDULED_MASK | TYPE_YET_UNSENT: return MessageType::YetUnsent; case SCHEDULED_MASK | TYPE_LOCAL: return MessageType::Local; case SCHEDULED_MASK: return MessageType::Server; default: return MessageType::None; } } if ((id & FULL_TYPE_MASK) == 0) { return MessageType::Server; } switch (id & TYPE_MASK) { case TYPE_YET_UNSENT: return MessageType::YetUnsent; case TYPE_LOCAL: return MessageType::Local; default: return MessageType::None; } } ServerMessageId MessageId::get_server_message_id_force() const { CHECK(!is_scheduled()); return ServerMessageId(narrow_cast(id >> SERVER_ID_SHIFT)); } MessageId MessageId::get_next_message_id(MessageType type) const { if (is_scheduled()) { CHECK(is_valid_scheduled()); auto current_type = get_type(); if (static_cast(current_type) < static_cast(type)) { return MessageId(id - static_cast(current_type) + static_cast(type)); } int64 base_id = (id & ~TYPE_MASK) + TYPE_MASK + 1 + SCHEDULED_MASK; switch (type) { case MessageType::Server: return MessageId(base_id); case MessageType::YetUnsent: return MessageId(base_id + TYPE_YET_UNSENT); case MessageType::Local: return MessageId(base_id + TYPE_LOCAL); case MessageType::None: default: UNREACHABLE(); return MessageId(); } } switch (type) { case MessageType::Server: if (is_server()) { return MessageId(ServerMessageId(get_server_message_id().get() + 1)); } return get_next_server_message_id(); case MessageType::YetUnsent: return MessageId(((id + TYPE_MASK + 1 - TYPE_YET_UNSENT) & ~TYPE_MASK) + TYPE_YET_UNSENT); case MessageType::Local: return MessageId(((id + TYPE_MASK + 1 - TYPE_LOCAL) & ~TYPE_MASK) + TYPE_LOCAL); case MessageType::None: default: UNREACHABLE(); return MessageId(); } } StringBuilder &operator<<(StringBuilder &string_builder, MessageId message_id) { if (message_id.is_scheduled()) { string_builder << "scheduled "; if (!message_id.is_valid_scheduled()) { return string_builder << "invalid message " << message_id.get(); } if (message_id.is_scheduled_server()) { return string_builder << "server message " << message_id.get_scheduled_server_message_id_force().get(); } if (message_id.is_local()) { return string_builder << "local message " << message_id.get_scheduled_server_message_id_force().get(); } if (message_id.is_yet_unsent()) { return string_builder << "yet unsent message " << message_id.get_scheduled_server_message_id_force().get(); } return string_builder << "bugged message " << message_id.get(); } if (!message_id.is_valid()) { return string_builder << "invalid message " << message_id.get(); } if (message_id.is_server()) { return string_builder << "server message " << message_id.get_server_message_id_force().get(); } if (message_id.is_local()) { return string_builder << "local message " << message_id.get_server_message_id_force().get() << '.' << (message_id.get() & MessageId::FULL_TYPE_MASK); } if (message_id.is_yet_unsent()) { return string_builder << "yet unsent message " << message_id.get_server_message_id_force().get() << '.' << (message_id.get() & MessageId::FULL_TYPE_MASK); } return string_builder << "bugged message " << message_id.get(); } } // namespace td