api: Put the chattype into the SecurejoinInviterProgress event (#7181)

Quoting @adbenitez:

> I have been using the SecurejoinInviterProgress event to show a
welcome message when user scan the QR/link of the bot (== starts a chat
with the bot)

> but this have a big problem: in that event all you know is that a
contact completed the secure-join process, you don't know if it was via
certain 1:1 invite link or a group invitation, then a group-invite bot
would send you a help message in 1:1 every time you join a group with it

Since it's easy enough to add this information to the
SecurejoinInviterProgress event, I wrote a PR to do so.
This commit is contained in:
Hocuri
2025-09-09 10:17:53 +02:00
committed by GitHub
parent 82bc1bf0b1
commit 1cc7ce6e27
4 changed files with 79 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
use deltachat::{Event as CoreEvent, EventType as CoreEventType}; use deltachat::{Event as CoreEvent, EventType as CoreEventType};
use num_traits::ToPrimitive;
use serde::Serialize; use serde::Serialize;
use typescript_type_def::TypeDef; use typescript_type_def::TypeDef;
@@ -303,6 +304,11 @@ pub enum EventType {
/// ID of the contact that wants to join. /// ID of the contact that wants to join.
contact_id: u32, contact_id: u32,
/// The type of the joined chat.
/// This can take the same values
/// as `BasicChat.chatType` ([`crate::api::types::chat::BasicChat::chat_type`]).
chat_type: u32,
/// Progress as: /// Progress as:
/// 300=vg-/vc-request received, typically shown as "bob@addr joins". /// 300=vg-/vc-request received, typically shown as "bob@addr joins".
/// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified". /// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified".
@@ -551,9 +557,11 @@ impl From<CoreEventType> for EventType {
}, },
CoreEventType::SecurejoinInviterProgress { CoreEventType::SecurejoinInviterProgress {
contact_id, contact_id,
chat_type,
progress, progress,
} => SecurejoinInviterProgress { } => SecurejoinInviterProgress {
contact_id: contact_id.to_u32(), contact_id: contact_id.to_u32(),
chat_type: chat_type.to_u32().unwrap_or(0),
progress, progress,
}, },
CoreEventType::SecurejoinJoinerProgress { CoreEventType::SecurejoinJoinerProgress {

View File

@@ -5,6 +5,7 @@ use std::path::PathBuf;
use crate::chat::ChatId; use crate::chat::ChatId;
use crate::config::Config; use crate::config::Config;
use crate::constants::Chattype;
use crate::contact::ContactId; use crate::contact::ContactId;
use crate::ephemeral::Timer as EphemeralTimer; use crate::ephemeral::Timer as EphemeralTimer;
use crate::message::MsgId; use crate::message::MsgId;
@@ -272,6 +273,9 @@ pub enum EventType {
/// ID of the contact that wants to join. /// ID of the contact that wants to join.
contact_id: ContactId, contact_id: ContactId,
/// The type of the joined chat.
chat_type: Chattype,
/// Progress as: /// Progress as:
/// 300=vg-/vc-request received, typically shown as "bob@addr joins". /// 300=vg-/vc-request received, typically shown as "bob@addr joins".
/// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified". /// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified".

View File

@@ -32,16 +32,29 @@ use qrinvite::QrInvite;
use crate::token::Namespace; use crate::token::Namespace;
fn inviter_progress(context: &Context, contact_id: ContactId, progress: usize) { fn inviter_progress(
context: &Context,
contact_id: ContactId,
step: &str,
progress: usize,
) -> Result<()> {
logged_debug_assert!( logged_debug_assert!(
context, context,
progress <= 1000, progress <= 1000,
"inviter_progress: contact {contact_id}, progress={progress}, but value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success." "inviter_progress: contact {contact_id}, progress={progress}, but value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success."
); );
let chat_type = match step.get(..3) {
Some("vc-") => Chattype::Single,
Some("vg-") => Chattype::Group,
_ => bail!("Unknown securejoin step {step}"),
};
context.emit_event(EventType::SecurejoinInviterProgress { context.emit_event(EventType::SecurejoinInviterProgress {
contact_id, contact_id,
chat_type,
progress, progress,
}); });
Ok(())
} }
/// Generates a Secure Join QR code. /// Generates a Secure Join QR code.
@@ -310,7 +323,7 @@ pub(crate) async fn handle_securejoin_handshake(
return Ok(HandshakeMessage::Ignore); return Ok(HandshakeMessage::Ignore);
} }
inviter_progress(context, contact_id, 300); inviter_progress(context, contact_id, step, 300)?;
let from_addr = ContactAddress::new(&mime_message.from.addr)?; let from_addr = ContactAddress::new(&mime_message.from.addr)?;
let autocrypt_fingerprint = mime_message.autocrypt_fingerprint.as_deref().unwrap_or(""); let autocrypt_fingerprint = mime_message.autocrypt_fingerprint.as_deref().unwrap_or("");
@@ -405,7 +418,7 @@ pub(crate) async fn handle_securejoin_handshake(
ChatId::create_for_contact(context, contact_id).await?; ChatId::create_for_contact(context, contact_id).await?;
} }
context.emit_event(EventType::ContactsChanged(Some(contact_id))); context.emit_event(EventType::ContactsChanged(Some(contact_id)));
inviter_progress(context, contact_id, 600); inviter_progress(context, contact_id, step, 600)?;
if let Some(group_chat_id) = group_chat_id { if let Some(group_chat_id) = group_chat_id {
// Join group. // Join group.
secure_connection_established( secure_connection_established(
@@ -417,8 +430,8 @@ pub(crate) async fn handle_securejoin_handshake(
.await?; .await?;
chat::add_contact_to_chat_ex(context, Nosync, group_chat_id, contact_id, true) chat::add_contact_to_chat_ex(context, Nosync, group_chat_id, contact_id, true)
.await?; .await?;
inviter_progress(context, contact_id, 800); inviter_progress(context, contact_id, step, 800)?;
inviter_progress(context, contact_id, 1000); inviter_progress(context, contact_id, step, 1000)?;
// IMAP-delete the message to avoid handling it by another device and adding the // IMAP-delete the message to avoid handling it by another device and adding the
// member twice. Another device will know the member's key from Autocrypt-Gossip. // member twice. Another device will know the member's key from Autocrypt-Gossip.
Ok(HandshakeMessage::Done) Ok(HandshakeMessage::Done)
@@ -435,7 +448,7 @@ pub(crate) async fn handle_securejoin_handshake(
.await .await
.context("failed sending vc-contact-confirm message")?; .context("failed sending vc-contact-confirm message")?;
inviter_progress(context, contact_id, 1000); inviter_progress(context, contact_id, step, 1000)?;
Ok(HandshakeMessage::Ignore) // "Done" would delete the message and break multi-device (the key from Autocrypt-header is needed) Ok(HandshakeMessage::Ignore) // "Done" would delete the message and break multi-device (the key from Autocrypt-header is needed)
} }
} }
@@ -555,10 +568,10 @@ pub(crate) async fn observe_securejoin_on_other_device(
ChatId::set_protection_for_contact(context, contact_id, mime_message.timestamp_sent).await?; ChatId::set_protection_for_contact(context, contact_id, mime_message.timestamp_sent).await?;
if step == "vg-member-added" { if step == "vg-member-added" {
inviter_progress(context, contact_id, 800); inviter_progress(context, contact_id, step, 800)?;
} }
if step == "vg-member-added" || step == "vc-contact-confirm" { if step == "vg-member-added" || step == "vc-contact-confirm" {
inviter_progress(context, contact_id, 1000); inviter_progress(context, contact_id, step, 1000)?;
} }
if step == "vg-request-with-auth" || step == "vc-request-with-auth" { if step == "vg-request-with-auth" || step == "vc-request-with-auth" {

View File

@@ -365,6 +365,29 @@ async fn test_setup_contact_bob_knows_alice() -> Result<()> {
alice.recv_msg_trash(&sent).await; alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(alice).await?, true); assert_eq!(contact_bob.is_verified(alice).await?, true);
// Check Alice signalled success via the SecurejoinInviterProgress event.
let event = alice
.evtracker
.get_matching(|evt| {
matches!(
evt,
EventType::SecurejoinInviterProgress { progress: 1000, .. }
)
})
.await;
match event {
EventType::SecurejoinInviterProgress {
contact_id,
chat_type,
progress,
} => {
assert_eq!(contact_id, contact_bob.id);
assert_eq!(chat_type, Chattype::Single);
assert_eq!(progress, 1000);
}
_ => unreachable!(),
}
let sent = alice.pop_sent_msg().await; let sent = alice.pop_sent_msg().await;
let msg = bob.parse_msg(&sent).await; let msg = bob.parse_msg(&sent).await;
assert!(msg.was_encrypted()); assert!(msg.was_encrypted());
@@ -515,6 +538,29 @@ async fn test_secure_join() -> Result<()> {
alice.recv_msg_trash(&sent).await; alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(&alice).await?, true); assert_eq!(contact_bob.is_verified(&alice).await?, true);
// Check Alice signalled success via the SecurejoinInviterProgress event.
let event = alice
.evtracker
.get_matching(|evt| {
matches!(
evt,
EventType::SecurejoinInviterProgress { progress: 1000, .. }
)
})
.await;
match event {
EventType::SecurejoinInviterProgress {
contact_id,
chat_type,
progress,
} => {
assert_eq!(contact_id, contact_bob.id);
assert_eq!(chat_type, Chattype::Group);
assert_eq!(progress, 1000);
}
_ => unreachable!(),
}
let sent = alice.pop_sent_msg().await; let sent = alice.pop_sent_msg().await;
let msg = bob.parse_msg(&sent).await; let msg = bob.parse_msg(&sent).await;
assert!(msg.was_encrypted()); assert!(msg.was_encrypted());