Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions deltachat-jsonrpc/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,13 @@ impl CommandApi {
/// or manually updating the value to avoid getting already
/// processed messages.
///
/// DEPRECATED 2026-04: This returns the message's id as soon as the first part arrives,
/// even if it is not fully downloaded yet.
/// The bot needs to wait for the message to be fully downloaded.
/// Since this is usually not the desired behavior,
/// bots should instead use the #DC_EVENT_INCOMING_MSG / [`types::events::EventType::IncomingMsg`]
/// event for getting notified about new messages.
///
/// [`markseen_msgs`]: Self::markseen_msgs
async fn get_next_msgs(&self, account_id: u32) -> Result<Vec<u32>> {
let ctx = self.get_context(account_id).await?;
Expand Down
7 changes: 7 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,13 @@ ORDER BY m.timestamp DESC,m.id DESC",
///
/// Blocked contacts and chats are excluded,
/// but self-sent messages and contact requests are included in the results.
///
/// DEPRECATED 2026-04: This returns the message's id as soon as the first part arrives,
/// even if it is not fully downloaded yet.
/// The bot needs to wait for the message to be fully downloaded.
/// Since this is usually not the desired behavior,
/// bots should instead use the [`EventType::IncomingMsg`]
/// event for getting notified about new messages.
pub async fn get_next_msgs(&self) -> Result<Vec<MsgId>> {
let last_msg_id = match self.get_config(Config::LastMsgId).await? {
Some(s) => MsgId::new(s.parse()?),
Expand Down
15 changes: 12 additions & 3 deletions src/imap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,10 +730,19 @@ impl Imap {
info!(context, "{message_id:?} is a post-message.");
available_post_msgs.push(message_id.clone());

if download_limit.is_none_or(|download_limit| size <= download_limit) {
download_later.push(message_id.clone());
let is_bot = context.get_config_bool(Config::Bot).await?;
if is_bot && download_limit.is_none_or(|download_limit| size <= download_limit)
{
uids_fetch.push(uid);
uid_message_ids.insert(uid, message_id);
} else {
if download_limit.is_none_or(|download_limit| size <= download_limit) {
// Download later after all the small messages are downloaded,
// so that large messages don't delay receiving small messages
download_later.push(message_id.clone());
}
largest_uid_skipped = Some(uid);
}
largest_uid_skipped = Some(uid);
} else {
info!(context, "{message_id:?} is not a post-message.");
if download_limit.is_none_or(|download_limit| size <= download_limit) {
Expand Down
24 changes: 22 additions & 2 deletions src/receive_imf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,12 @@ UPDATE msgs SET state=? WHERE
let fresh = received_msg.state == MessageState::InFresh
&& mime_parser.is_system_message != SystemMessage::CallAccepted
&& mime_parser.is_system_message != SystemMessage::CallEnded;
let important = mime_parser.incoming && fresh && !is_old_contact_request;
let is_bot = context.get_config_bool(Config::Bot).await?;
let is_pre_message = matches!(mime_parser.pre_message, PreMessageMode::Pre { .. });
let skip_bot_notify = is_bot && is_pre_message;
let important =
mime_parser.incoming && fresh && !is_old_contact_request && !skip_bot_notify;

for msg_id in &received_msg.msg_ids {
chat_id.emit_msg_event(context, *msg_id, important);
}
Expand Down Expand Up @@ -2573,7 +2578,22 @@ WHERE id=?
),
)
.await?;
context.emit_msgs_changed(original_msg.chat_id, original_msg.id);

if context.get_config_bool(Config::Bot).await? {
if original_msg.hidden {
// No need to emit an event about the changed message
} else if !original_msg.chat_id.is_trash() {
let fresh = original_msg.state == MessageState::InFresh;
let important = mime_parser.incoming && fresh;

original_msg
.chat_id
.emit_msg_event(context, original_msg.id, important);
context.new_msgs_notify.notify_one();
}
} else {
context.emit_msgs_changed(original_msg.chat_id, original_msg.id);
}

Ok(())
}
Expand Down
45 changes: 45 additions & 0 deletions src/tests/pre_messages/receiving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ use pretty_assertions::assert_eq;
use crate::EventType;
use crate::chat;
use crate::chat::send_msg;
use crate::config::Config;
use crate::contact;
use crate::download::{DownloadState, PRE_MSG_ATTACHMENT_SIZE_THRESHOLD, PostMsgMetadata};
use crate::message::{Message, MessageState, Viewtype, delete_msgs, markseen_msgs};
use crate::mimeparser::MimeMessage;
use crate::param::Param;
use crate::reaction::{get_msg_reactions, send_reaction};
use crate::receive_imf::receive_imf;
use crate::summary::assert_summary_texts;
use crate::test_utils::TestContextManager;
use crate::tests::pre_messages::util::{
Expand Down Expand Up @@ -795,3 +797,46 @@ async fn test_chatlist_event_on_post_msg_download() -> Result<()> {

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_bot_pre_message_notifications() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
let bob = tcm.bob().await;
bob.set_config_bool(Config::Bot, true).await?;

let alice_group_id = alice.create_group_with_members("test group", &[&bob]).await;

let (pre_message, post_message, _alice_msg_id) = send_large_file_message(
&alice,
alice_group_id,
Viewtype::File,
&vec![0u8; (PRE_MSG_ATTACHMENT_SIZE_THRESHOLD + 1) as usize],
)
.await?;

// Bob receives pre-message
bob.evtracker.clear_events();
receive_imf(&bob, pre_message.payload().as_bytes(), false).await?;

// Verify Bob does NOT get an IncomingMsg event for the pre-message
assert!(
bob.evtracker
.get_matching_opt(&bob, |e| matches!(e, EventType::IncomingMsg { .. }))
.await
.is_none()
);

// Bob receives post-message
receive_imf(&bob, post_message.payload().as_bytes(), false).await?;

// Verify Bob DOES get an IncomingMsg event for the complete message
bob.evtracker
.get_matching(|e| matches!(e, EventType::IncomingMsg { .. }))
.await;

let msg = bob.get_last_msg().await;
assert_eq!(msg.download_state, DownloadState::Done);

Ok(())
}
Loading