diff --git a/td/telegram/MessageId.cpp b/td/telegram/MessageId.cpp index 6a4f6153..7277fece 100644 --- a/td/telegram/MessageId.cpp +++ b/td/telegram/MessageId.cpp @@ -11,12 +11,12 @@ namespace td { -MessageId::MessageId(ScheduledServerMessageId server_message_id, int32 send_date) { +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()) { + if (!server_message_id.is_valid() && !force) { LOG(ERROR) << "Scheduled message ID " << server_message_id.get() << " is invalid"; return; } @@ -46,6 +46,20 @@ MessageType MessageId::get_type() const { if (id <= 0 || id > max().get()) { 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; } @@ -65,14 +79,37 @@ ServerMessageId MessageId::get_server_message_id_force() const { } MessageId MessageId::get_next_message_id(MessageType type) const { - CHECK(!is_scheduled()); + 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::Local: - return MessageId(((id + TYPE_MASK + 1 - TYPE_LOCAL) & ~TYPE_MASK) + TYPE_LOCAL); 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(); diff --git a/td/telegram/MessageId.h b/td/telegram/MessageId.h index 4834e0eb..44917a32 100644 --- a/td/telegram/MessageId.h +++ b/td/telegram/MessageId.h @@ -18,7 +18,7 @@ namespace td { -enum class MessageType : int32 { None, Server, Local, YetUnsent }; +enum class MessageType : int32 { None, Server, YetUnsent, Local }; class MessageId { int64 id = 0; @@ -55,7 +55,7 @@ class MessageId { : id(static_cast(server_message_id.get()) << SERVER_ID_SHIFT) { } - MessageId(ScheduledServerMessageId server_message_id, int32 send_date); + MessageId(ScheduledServerMessageId server_message_id, int32 send_date, bool force = false); explicit constexpr MessageId(int64 message_id) : id(message_id) { } @@ -131,6 +131,11 @@ class MessageId { return get_scheduled_server_message_id_force(); } + int32 get_scheduled_message_date() const { + CHECK(is_valid_scheduled()); + return static_cast(id >> 21) + (1 << 30); + } + bool operator==(const MessageId &other) const { return id == other.id; } diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 3c76e4ae..1d0024ad 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -16901,7 +16901,8 @@ MessagesManager::Message *MessagesManager::get_message_to_send(Dialog *d, Messag int32 schedule_date = 0; bool is_scheduled = schedule_date != 0; CHECK(d != nullptr); - MessageId message_id = get_next_yet_unsent_message_id(d); // TODO support sending scheduled messages + MessageId message_id = + is_scheduled ? get_next_yet_unsent_scheduled_message_id(d, schedule_date) : get_next_yet_unsent_message_id(d); DialogId dialog_id = d->dialog_id; LOG(INFO) << "Create " << message_id << " in " << dialog_id; @@ -22309,6 +22310,20 @@ MessageId MessagesManager::get_next_local_message_id(Dialog *d) { return get_next_message_id(d, MessageType::Local); } +MessageId MessagesManager::get_next_yet_unsent_scheduled_message_id(const Dialog *d, int32 date) { + CHECK(date > 0); + auto it = MessagesConstIterator(d, MessageId(ScheduledServerMessageId(), date + 1, true)); + int32 prev_date = 0; + if (*it != nullptr) { + prev_date = (*it)->message_id.get_scheduled_message_date(); + } + if (prev_date < date) { + return MessageId(ScheduledServerMessageId(1), date).get_next_message_id(MessageType::YetUnsent); + } + CHECK(*it != nullptr); + return (*it)->message_id.get_next_message_id(MessageType::YetUnsent); +} + void MessagesManager::fail_send_message(FullMessageId full_message_id, int error_code, const string &error_message) { auto dialog_id = full_message_id.get_dialog_id(); Dialog *d = get_dialog(dialog_id); @@ -22331,9 +22346,9 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error // dump_debug_message_op(d, 5); } - MessageId new_message_id; + MessageId new_message_id = + old_message_id.get_next_message_id(MessageType::Local); // trying to not change message place if (!old_message_id.is_scheduled()) { - new_message_id = old_message_id.get_next_message_id(MessageType::Local); // trying to not change message place if (get_message_force(d, new_message_id, "fail_send_message") != nullptr || d->deleted_message_ids.count(new_message_id) || new_message_id <= d->last_clear_history_message_id) { new_message_id = get_next_local_message_id(d); @@ -22341,11 +22356,9 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error d->last_assigned_message_id = new_message_id; } } else { - new_message_id = MessageId(old_message_id.get() + 1); // trying to not change message place - if (get_message_force(d, new_message_id, "fail_send_message") != nullptr || - d->deleted_message_ids.count(new_message_id)) { - // TODO - // new_message_id = get_next_scheduled_local_message_id(d); + while (get_message_force(d, new_message_id, "fail_send_message") != nullptr || + d->deleted_message_ids.count(new_message_id)) { + new_message_id = new_message_id.get_next_message_id(MessageType::Local); } } @@ -25780,11 +25793,11 @@ void MessagesManager::do_delete_message_logevent(const DeleteMessageLogEvent &lo void MessagesManager::attach_message_to_previous(Dialog *d, MessageId message_id, const char *source) { CHECK(d != nullptr); + CHECK(message_id.is_valid()); MessagesIterator it(d, message_id); Message *m = *it; CHECK(m != nullptr); CHECK(m->message_id == message_id); - CHECK(m->message_id.is_valid()); LOG_CHECK(m->have_previous) << d->dialog_id << " " << message_id << " " << source; --it; LOG_CHECK(*it != nullptr) << d->dialog_id << " " << message_id << " " << source; @@ -25798,11 +25811,11 @@ void MessagesManager::attach_message_to_previous(Dialog *d, MessageId message_id void MessagesManager::attach_message_to_next(Dialog *d, MessageId message_id, const char *source) { CHECK(d != nullptr); + CHECK(message_id.is_valid()); MessagesIterator it(d, message_id); Message *m = *it; CHECK(m != nullptr); CHECK(m->message_id == message_id); - CHECK(m->message_id.is_valid()); LOG_CHECK(m->have_next) << d->dialog_id << " " << message_id << " " << source; ++it; LOG_CHECK(*it != nullptr) << d->dialog_id << " " << message_id << " " << source; @@ -28064,7 +28077,8 @@ MessagesManager::Message *MessagesManager::continue_send_message(DialogId dialog auto now = G()->unix_time(); bool is_scheduled = m->message_id.is_scheduled(); - m->message_id = get_next_yet_unsent_message_id(d); // TODO support sending scheduled messages + m->message_id = + is_scheduled ? get_next_yet_unsent_scheduled_message_id(d, m->date) : get_next_yet_unsent_message_id(d); m->random_y = get_random_y(m->message_id); if (!is_scheduled) { m->date = now; @@ -28271,9 +28285,13 @@ void MessagesManager::on_binlog_events(vector &&events) { } auto now = G()->unix_time(); for (auto &m : messages) { - m->message_id = get_next_yet_unsent_message_id(to_dialog); // TODO support sending scheduled messages + bool is_scheduled = m->message_id.is_scheduled(); + m->message_id = is_scheduled ? get_next_yet_unsent_scheduled_message_id(to_dialog, m->date) + : get_next_yet_unsent_message_id(to_dialog); m->random_y = get_random_y(m->message_id); - m->date = now; + if (!is_scheduled) { + m->date = now; + } m->content = dup_message_content(td_, to_dialog_id, m->content.get(), true); m->have_previous = true; m->have_next = true; diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 6e4da50f..aa22abe6 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -1311,7 +1311,9 @@ class MessagesManager : public Actor { public: MessagesIterator() = default; - MessagesIterator(Dialog *d, MessageId message_id) : MessagesIteratorBase(d->messages.get(), message_id) { + MessagesIterator(Dialog *d, MessageId message_id) + : MessagesIteratorBase(message_id.is_scheduled() ? d->scheduled_messages.get() : d->messages.get(), + message_id) { } Message *operator*() const { @@ -1323,7 +1325,9 @@ class MessagesManager : public Actor { public: MessagesConstIterator() = default; - MessagesConstIterator(const Dialog *d, MessageId message_id) : MessagesIteratorBase(d->messages.get(), message_id) { + MessagesConstIterator(const Dialog *d, MessageId message_id) + : MessagesIteratorBase(message_id.is_scheduled() ? d->scheduled_messages.get() : d->messages.get(), + message_id) { } const Message *operator*() const { @@ -2271,6 +2275,8 @@ class MessagesManager : public Actor { static MessageId get_next_yet_unsent_message_id(Dialog *d); + static MessageId get_next_yet_unsent_scheduled_message_id(const Dialog *d, int32 date); + bool add_recently_found_dialog_internal(DialogId dialog_id); bool remove_recently_found_dialog_internal(DialogId dialog_id);