From 4623d895288d056568336f7871b1ce2798918287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jagoda=20Estera=20=C5=9Al=C4=85zak?= <128227338+j-g00da@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:47:30 +0200 Subject: [PATCH] feat: Add stock strings for being added/removed from group (#8562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds following stock strings: - You were removed by %1$s. - You were added by %1$s. and implicit equivalents: - You were removed. - You were added. Closes: #8419 Signed-off-by: Jagoda Ślązak --- deltachat-ffi/deltachat.h | 24 +++++++ .../src/deltachat_rpc_client/_utils.py | 2 +- deltachat-rpc-client/tests/test_securejoin.py | 6 +- python/tests/test_0_complex_or_slow.py | 4 +- python/tests/test_1_online.py | 2 +- src/chat/chat_tests.rs | 4 +- src/securejoin/securejoin_tests.rs | 2 +- src/stock_str.rs | 62 ++++++++++++++----- src/sync.rs | 2 +- test-data/golden/test_sync_broadcast_bob | 2 +- test-data/golden/two_group_securejoins | 2 +- .../golden/verified_chats_editor_reordering | 2 +- 12 files changed, 84 insertions(+), 30 deletions(-) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 75ad2544d..3be356ec5 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -7170,6 +7170,30 @@ void dc_event_unref(dc_event_t* event); /// `%1$s` will be replaced by name of the removed contact. #define DC_STR_REMOVE_MEMBER 178 +/// "You were removed by %1$s." +/// +/// `%1$s` will be replaced by name of the contact who did the action. +/// +/// Used in status messages. +#define DC_STR_REMOVE_YOU_BY 179 + +/// "You were added by %1$s." +/// +/// `%1$s` will be replaced by name of the contact who did the action. +/// +/// Used in status messages. +#define DC_STR_ADD_YOU_BY 180 + +/// "You were removed." +/// +/// Used in status messages. +#define DC_STR_REMOVE_YOU 181 + +/// "You were added." +/// +/// Used in status messages. +#define DC_STR_ADD_YOU 182 + /// "Establishing connection, please wait…" /// /// Used as info message. diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/_utils.py b/deltachat-rpc-client/src/deltachat_rpc_client/_utils.py index fffd18b60..92050d520 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/_utils.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/_utils.py @@ -157,7 +157,7 @@ def parse_system_add_remove(text: str) -> Optional[Tuple[str, str, str]]: """ # You removed member a@b. # You added member a@b. - # Member Me (x@y) removed by a@b. + # You were removed by a@b. # Member x@y added by a@b # Member With space (tmp1@x.org) removed by tmp2@x.org. # Member With space (tmp1@x.org) removed by Another member (tmp2@x.org).", diff --git a/deltachat-rpc-client/tests/test_securejoin.py b/deltachat-rpc-client/tests/test_securejoin.py index c667075df..c5eb4b5f7 100644 --- a/deltachat-rpc-client/tests/test_securejoin.py +++ b/deltachat-rpc-client/tests/test_securejoin.py @@ -87,7 +87,7 @@ def test_qr_securejoin(acfactory): assert alice_contact_bob_snapshot.is_verified snapshot = bob.wait_for_incoming_msg().get_snapshot() - assert snapshot.text == "Member Me added by {}.".format(alice.get_config("addr")) + assert snapshot.text == "You were added by {}.".format(alice.get_config("addr")) # Test that Bob verified Alice's profile. bob_contact_alice = bob.create_contact(alice) @@ -623,7 +623,7 @@ def test_securejoin_after_contact_resetup(acfactory) -> None: # ac1 waits for member added message and creates a QR code. snapshot = ac1.wait_for_incoming_msg().get_snapshot() - assert snapshot.text == "Member Me added by {}.".format(ac3.get_config("addr")) + assert snapshot.text == "You were added by {}.".format(ac3.get_config("addr")) ac1_qr_code = snapshot.chat.get_qr_code() # ac2 verifies ac1 @@ -682,7 +682,7 @@ def test_withdraw_securejoin_qr(acfactory): alice.clear_all_events() snapshot = bob.wait_for_incoming_msg().get_snapshot() - assert snapshot.text == "Member Me added by {}.".format(alice.get_config("addr")) + assert snapshot.text == "You were added by {}.".format(alice.get_config("addr")) bob_chat.leave() snapshot = alice.get_message_by_id(alice.wait_for_msgs_changed_event().msg_id).get_snapshot() diff --git a/python/tests/test_0_complex_or_slow.py b/python/tests/test_0_complex_or_slow.py index 151f72247..9b25df187 100644 --- a/python/tests/test_0_complex_or_slow.py +++ b/python/tests/test_0_complex_or_slow.py @@ -224,7 +224,7 @@ def test_see_new_verified_member_after_going_online(acfactory, tmp_path, lp): lp.sec("ac2: sending message") # Message can be sent only after a receipt of "vg-member-added" message. Just wait for - # "Member Me () added by ." message. + # "You were added by ." message. msg_in = ac2._evtracker.wait_next_incoming_message() assert msg_in.is_system_message() msg_out = chat2.send_text("hello") @@ -278,7 +278,7 @@ def test_use_new_verified_group_after_going_online(acfactory, data, tmp_path, lp lp.sec("ac2_offl: going online, checking the 'member added' message") ac2_offl.start_io() - # Receive "Member Me () added by ." message. + # Receive "You were added by ." message. msg_in = ac2_offl._evtracker.wait_next_incoming_message() contact = msg_in.get_sender_contact() assert msg_in.is_system_message() diff --git a/python/tests/test_1_online.py b/python/tests/test_1_online.py index 97ac0bd31..04957d98b 100644 --- a/python/tests/test_1_online.py +++ b/python/tests/test_1_online.py @@ -789,7 +789,7 @@ def test_qr_email_capitalization(acfactory, lp): lp.sec("ac1 joins a group via a QR code") ac1_chat = ac1.qr_join_chat(qr) msg = ac1._evtracker.wait_next_incoming_message() - assert msg.text == "Member Me added by {}.".format(ac3.get_config("addr")) + assert msg.text == "You were added by {}.".format(ac3.get_config("addr")) assert len(ac1_chat.get_contacts()) == 2 lp.sec("ac2 joins a group via a QR code") diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index f0a310b8d..6469753b7 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -449,7 +449,7 @@ async fn test_parallel_member_remove() -> Result<()> { // Test that remove message is rewritten. assert_eq!( bob_received_remove_msg.get_text(), - "Member Me removed by alice@example.org." + "You were removed by alice@example.org." ); Ok(()) @@ -4002,7 +4002,7 @@ async fn test_remove_member_from_broadcast() -> Result<()> { let remove_msg = alice.pop_sent_msg().await; let rcvd = bob.recv_msg(&remove_msg).await; - assert_eq!(rcvd.text, "Member Me removed by alice@example.org."); + assert_eq!(rcvd.text, "You were removed by alice@example.org."); let bob_chat = Chat::load_from_db(bob, bob_chat_id).await?; assert_eq!(bob_chat.is_self_in_chat(bob).await?, false); diff --git a/src/securejoin/securejoin_tests.rs b/src/securejoin/securejoin_tests.rs index a771207c8..1b15b82f5 100644 --- a/src/securejoin/securejoin_tests.rs +++ b/src/securejoin/securejoin_tests.rs @@ -1651,7 +1651,7 @@ async fn test_deduplicate_member_added() -> Result<()> { let bob_rcvd = bob.recv_msg(&sent1).await; assert_eq!(bob_rcvd.chat_id, bob_chat_id); - assert_eq!(bob_rcvd.text, "Member Me added by alice@example.org."); + assert_eq!(bob_rcvd.text, "You were added by alice@example.org."); // Second message is a no-op, so it is trashed. bob.recv_msg_trash(&sent2).await; diff --git a/src/stock_str.rs b/src/stock_str.rs index 67c6263a6..be1eb1e48 100644 --- a/src/stock_str.rs +++ b/src/stock_str.rs @@ -344,6 +344,18 @@ pub enum StockMessage { #[strum(props(fallback = "Member %1$s removed."))] MsgDelMember = 178, + #[strum(props(fallback = "You were removed by %1$s."))] + MsgRemovedBy = 179, + + #[strum(props(fallback = "You were added by %1$s."))] + MsgAddedBy = 180, + + #[strum(props(fallback = "You were removed."))] + MsgRemoved = 181, + + #[strum(props(fallback = "You were added."))] + MsgAdded = 182, + #[strum(props(fallback = "Establishing connection, please wait…"))] SecurejoinWait = 190, @@ -626,7 +638,12 @@ pub(crate) async fn msg_pinned(context: &Context, by_contact: ContactId) -> Stri } } -/// Stock string: `Member %1$s added.`, `You added member %1$s.` or `Member %1$s added by %2$s.`. +/// Stock string, one of: +/// - `Member %1$s added.`, +/// - `You added member %1$s.`, +/// - `Member %1$s added by %2$s.`, +/// - `You were added by %1$s.`, +/// - `You were added.`. /// /// The `added_member` and `by_contact` contacts /// are looked up in the database to get the display names. @@ -636,18 +653,26 @@ pub(crate) async fn msg_add_member_local( by_contact: ContactId, ) -> String { let whom = added_member.get_stock_name(context).await; - if by_contact == ContactId::UNDEFINED { - translated(context, StockMessage::MsgAddMember).replace1(&whom) - } else if by_contact == ContactId::SELF { - translated(context, StockMessage::MsgYouAddMember).replace1(&whom) - } else { - translated(context, StockMessage::MsgAddMemberBy) + match (added_member, by_contact) { + (ContactId::SELF, ContactId::UNDEFINED) => translated(context, StockMessage::MsgAdded), + (ContactId::SELF, _) => translated(context, StockMessage::MsgAddedBy) + .replace1(&by_contact.get_stock_name(context).await), + (_, ContactId::UNDEFINED) => { + translated(context, StockMessage::MsgAddMember).replace1(&whom) + } + (_, ContactId::SELF) => translated(context, StockMessage::MsgYouAddMember).replace1(&whom), + _ => translated(context, StockMessage::MsgAddMemberBy) .replace1(&whom) - .replace2(&by_contact.get_stock_name(context).await) + .replace2(&by_contact.get_stock_name(context).await), } } -/// Stock string: `Member %1$s removed.` or `You removed member %1$s.` or `Member %1$s removed by %2$s.` +/// Stock string, one of: +/// - `Member %1$s removed.`, +/// - `You removed member %1$s.`, +/// - `Member %1$s removed by %2$s.`, +/// - `You were removed by %1$s.`, +/// - `You were removed.`. /// /// The `removed_member` and `by_contact` contacts /// are looked up in the database to get the display names. @@ -657,14 +682,19 @@ pub(crate) async fn msg_del_member_local( by_contact: ContactId, ) -> String { let whom = removed_member.get_stock_name(context).await; - if by_contact == ContactId::UNDEFINED { - translated(context, StockMessage::MsgDelMember).replace1(&whom) - } else if by_contact == ContactId::SELF { - translated(context, StockMessage::MsgYouDelMember).replace1(&whom) - } else { - translated(context, StockMessage::MsgDelMemberBy) + // note: this does not properly handle (SELF, SELF) case, + // as "you left"/"left by" messages are handled by `msg_group_left_local`. + match (removed_member, by_contact) { + (ContactId::SELF, ContactId::UNDEFINED) => translated(context, StockMessage::MsgRemoved), + (ContactId::SELF, _) => translated(context, StockMessage::MsgRemovedBy) + .replace1(&by_contact.get_stock_name(context).await), + (_, ContactId::UNDEFINED) => { + translated(context, StockMessage::MsgDelMember).replace1(&whom) + } + (_, ContactId::SELF) => translated(context, StockMessage::MsgYouDelMember).replace1(&whom), + _ => translated(context, StockMessage::MsgDelMemberBy) .replace1(&whom) - .replace2(&by_contact.get_stock_name(context).await) + .replace2(&by_contact.get_stock_name(context).await), } } diff --git a/src/sync.rs b/src/sync.rs index a9c58a24b..16d2d783d 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -810,7 +810,7 @@ mod tests { let fiona = &tcm.fiona().await; tcm.exec_securejoin_qr(fiona, alice2, &qr).await; let msg = fiona.get_last_msg().await; - assert_eq!(msg.text, "Member Me added by alice@example.org."); + assert_eq!(msg.text, "You were added by alice@example.org."); Ok(()) } diff --git a/test-data/golden/test_sync_broadcast_bob b/test-data/golden/test_sync_broadcast_bob index c62396083..ca3d42efe 100644 --- a/test-data/golden/test_sync_broadcast_bob +++ b/test-data/golden/test_sync_broadcast_bob @@ -6,5 +6,5 @@ Msg#2004: info (Contact#Contact#Info): alice@example.org invited you to join thi Waiting for the device of alice@example.org to reply… [NOTICED][INFO] Msg#2008🔒: (Contact#Contact#2001): You joined the channel. [FRESH][INFO] Msg#2010🔒: (Contact#Contact#2001): hi [FRESH] -Msg#2011🔒: (Contact#Contact#2001): Member Me removed by alice@example.org. [FRESH][INFO] +Msg#2011🔒: (Contact#Contact#2001): You were removed by alice@example.org. [FRESH][INFO] -------------------------------------------------------------------------------- diff --git a/test-data/golden/two_group_securejoins b/test-data/golden/two_group_securejoins index a7a5e627f..1a93c7c19 100644 --- a/test-data/golden/two_group_securejoins +++ b/test-data/golden/two_group_securejoins @@ -5,5 +5,5 @@ Msg#6004: info (Contact#Contact#Info): alice@example.org invited you to join thi Waiting for the device of alice@example.org to reply… [NOTICED][INFO] Msg#6006: info (Contact#Contact#Info): alice@example.org replied, waiting for being added to the group… [NOTICED][INFO] -Msg#6008🔒: (Contact#Contact#6001): Member Me added by alice@example.org. [FRESH][INFO] +Msg#6008🔒: (Contact#Contact#6001): You were added by alice@example.org. [FRESH][INFO] -------------------------------------------------------------------------------- diff --git a/test-data/golden/verified_chats_editor_reordering b/test-data/golden/verified_chats_editor_reordering index 288ed1055..b723de795 100644 --- a/test-data/golden/verified_chats_editor_reordering +++ b/test-data/golden/verified_chats_editor_reordering @@ -7,5 +7,5 @@ Waiting for the device of alice@example.org to reply… [NOTICED][INFO] Msg#3006: info (Contact#Contact#Info): alice@example.org replied, waiting for being added to the group… [NOTICED][INFO] Msg#3008🔒: (Contact#Contact#3002): [FRESH] Msg#3009: info (Contact#Contact#Info): Member bob@example.net added. [NOTICED][INFO] -Msg#3010🔒: (Contact#Contact#3001): Member Me added by alice@example.org. [FRESH][INFO] +Msg#3010🔒: (Contact#Contact#3001): You were added by alice@example.org. [FRESH][INFO] --------------------------------------------------------------------------------