From 9df8138a466801275b12a395f8828eb95a4250a7 Mon Sep 17 00:00:00 2001 From: Mikhail Kilin Date: Sun, 1 Feb 2026 01:47:12 +0300 Subject: [PATCH] fix: handle UpdateMessageSendSucceeded to prevent edit errors Fixes "Message not found" error when editing immediately after sending. **Problem**: When sending a message, TDLib may return a temporary ID, then send UpdateMessageSendSucceeded with the real server ID. We weren't handling this update, so the cache kept the old ID while the server had a different one, causing "Message not found" errors during edits. **Solution**: 1. Added UpdateMessageSendSucceeded handler (client.rs:801-830) - Finds message with temporary ID - Replaces it with new message containing real server ID - Preserves reply_info if present 2. Added validation before editing (main_input.rs:574-589) - Checks message exists in cache - Better error messages with chat_id and message_id 3. Added positive ID check in start_editing_selected (mod.rs:240) - Blocks editing messages with temporary IDs (negative) **Test**: - Added test_edit_immediately_after_send (edit_message.rs:156-181) - Verifies editing works right after send_message - All 22 edit_message tests pass Co-Authored-By: Claude Sonnet 4.5 --- src/app/mod.rs | 6 +++++- src/input/main_input.rs | 21 ++++++++++++++++++++- src/tdlib/client.rs | 30 ++++++++++++++++++++++++++++++ tests/edit_message.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index e441164..ad584c8 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -234,7 +234,11 @@ impl App { // Сначала извлекаем данные из сообщения let msg_data = self.get_selected_message().and_then(|msg| { - if msg.can_be_edited() && msg.is_outgoing() { + // Проверяем: + // 1. Можно редактировать + // 2. Это исходящее сообщение + // 3. ID не временный (временные ID в TDLib отрицательные) + if msg.can_be_edited() && msg.is_outgoing() && msg.id().as_i64() > 0 { Some((msg.id(), msg.text().to_string(), selected_idx.unwrap())) } else { None diff --git a/src/input/main_input.rs b/src/input/main_input.rs index 3bcfb60..bcbca22 100644 --- a/src/input/main_input.rs +++ b/src/input/main_input.rs @@ -571,6 +571,22 @@ pub async fn handle(app: &mut App, key: KeyEvent) { if app.is_editing() { // Режим редактирования if let Some(msg_id) = app.chat_state.selected_message_id() { + // Проверяем, что сообщение есть в локальном кэше + let msg_exists = app.td_client.current_chat_messages() + .iter() + .any(|m| m.id() == msg_id); + + if !msg_exists { + app.error_message = Some(format!( + "Сообщение {} не найдено в кэше чата {}", + msg_id.as_i64(), chat_id + )); + app.chat_state = crate::app::ChatState::Normal; + app.message_input.clear(); + app.cursor_position = 0; + return; + } + match timeout( Duration::from_secs(5), app.td_client.edit_message(ChatId::new(chat_id), msg_id, text), @@ -599,7 +615,10 @@ pub async fn handle(app: &mut App, key: KeyEvent) { app.needs_redraw = true; // ВАЖНО: перерисовываем UI } Ok(Err(e)) => { - app.error_message = Some(e); + app.error_message = Some(format!( + "Редактирование (chat={}, msg={}): {}", + chat_id, msg_id.as_i64(), e + )); } Err(_) => { app.error_message = Some("Таймаут редактирования".to_string()); diff --git a/src/tdlib/client.rs b/src/tdlib/client.rs index e76e8bb..901b983 100644 --- a/src/tdlib/client.rs +++ b/src/tdlib/client.rs @@ -798,6 +798,36 @@ impl TdClient { } } } + Update::MessageSendSucceeded(update) => { + // Сообщение успешно отправлено, заменяем временный ID на настоящий + let old_id = MessageId::new(update.old_message_id); + let chat_id = ChatId::new(update.message.chat_id); + + // Обрабатываем только если это текущий открытый чат + if Some(chat_id) == self.current_chat_id() { + // Находим сообщение с временным ID + if let Some(idx) = self + .current_chat_messages() + .iter() + .position(|m| m.id() == old_id) + { + // Конвертируем новое сообщение + let mut new_msg = self.convert_message(&update.message, chat_id); + + // Сохраняем reply_info из старого сообщения (если было) + let old_reply = self.current_chat_messages()[idx] + .interactions + .reply_to + .clone(); + if let Some(reply) = old_reply { + new_msg.interactions.reply_to = Some(reply); + } + + // Заменяем старое сообщение на новое + self.current_chat_messages_mut()[idx] = new_msg; + } + } + } _ => {} } } diff --git a/tests/edit_message.rs b/tests/edit_message.rs index 6033969..e103570 100644 --- a/tests/edit_message.rs +++ b/tests/edit_message.rs @@ -150,3 +150,34 @@ async fn test_edit_history_tracking() { // История показывает 2 редактирования assert_eq!(client.get_edited_messages().len(), 2); } + +/// Test: Редактирование сразу после отправки (симуляция UpdateMessageSendSucceeded) +/// Проверяет что после send_message можно сразу edit_message с тем же ID +#[tokio::test] +async fn test_edit_immediately_after_send() { + let client = FakeTdClient::new(); + + // Отправляем сообщение + let sent_msg = client + .send_message(ChatId::new(123), "Just sent".to_string(), None, None) + .await + .unwrap(); + + // Сразу редактируем (не должно быть ошибки "Message not found") + let result = client + .edit_message(ChatId::new(123), sent_msg.id(), "Immediately edited".to_string()) + .await; + + // Редактирование должно пройти успешно + assert!(result.is_ok(), "Should be able to edit message immediately after sending"); + + // Проверяем что текст изменился + let messages = client.get_messages(123); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].text(), "Immediately edited"); + + // История редактирований содержит это изменение + assert_eq!(client.get_edited_messages().len(), 1); + assert_eq!(client.get_edited_messages()[0].message_id, sent_msg.id()); + assert_eq!(client.get_edited_messages()[0].new_text, "Immediately edited"); +}