deltachat-rpc-client 1.160.0__tar.gz → 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {deltachat_rpc_client-1.160.0/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-2.1.0}/PKG-INFO +1 -1
  2. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/pyproject.toml +1 -1
  3. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/account.py +37 -1
  4. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/const.py +32 -1
  5. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/contact.py +0 -4
  6. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/message.py +6 -1
  7. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0/src/deltachat_rpc_client.egg-info}/PKG-INFO +1 -1
  8. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_securejoin.py +21 -120
  9. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_something.py +55 -3
  10. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/LICENSE +0 -0
  11. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/README.md +0 -0
  12. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/setup.cfg +0 -0
  13. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/__init__.py +0 -0
  14. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/_utils.py +0 -0
  15. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/chat.py +0 -0
  16. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/client.py +0 -0
  17. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/deltachat.py +0 -0
  18. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/events.py +0 -0
  19. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/py.typed +0 -0
  20. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/pytestplugin.py +0 -0
  21. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client/rpc.py +0 -0
  22. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client.egg-info/SOURCES.txt +0 -0
  23. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
  24. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
  25. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
  26. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_account_events.py +0 -0
  27. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_chatlist_events.py +0 -0
  28. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_iroh_webxdc.py +0 -0
  29. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_key_transfer.py +0 -0
  30. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_multidevice.py +0 -0
  31. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_vcard.py +0 -0
  32. {deltachat_rpc_client-1.160.0 → deltachat_rpc_client-2.1.0}/tests/test_webxdc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deltachat-rpc-client
3
- Version: 1.160.0
3
+ Version: 2.1.0
4
4
  Summary: Python client for Delta Chat core JSON-RPC interface
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Intended Audience :: Developers
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deltachat-rpc-client"
7
- version = "1.160.0"
7
+ version = "2.1.0"
8
8
  description = "Python client for Delta Chat core JSON-RPC interface"
9
9
  classifiers = [
10
10
  "Development Status :: 5 - Production/Stable",
@@ -288,10 +288,46 @@ class Account:
288
288
  def create_group(self, name: str, protect: bool = False) -> Chat:
289
289
  """Create a new group chat.
290
290
 
291
- After creation, the group has only self-contact as member and is in unpromoted state.
291
+ After creation,
292
+ the group has only self-contact as member one member (see `SpecialContactId.SELF`)
293
+ and is in _unpromoted_ state.
294
+ This means, you can add or remove members, change the name,
295
+ the group image and so on without messages being sent to all group members.
296
+
297
+ This changes as soon as the first message is sent to the group members
298
+ and the group becomes _promoted_.
299
+ After that, all changes are synced with all group members
300
+ by sending status message.
301
+
302
+ To check, if a chat is still unpromoted, you can look at the `is_unpromoted` property of a chat
303
+ (see `get_full_snapshot()` / `get_basic_snapshot()`).
304
+ This may be useful if you want to show some help for just created groups.
305
+
306
+ :param protect: If set to 1 the function creates group with protection initially enabled.
307
+ Only verified members are allowed in these groups
308
+ and end-to-end-encryption is always enabled.
292
309
  """
293
310
  return Chat(self, self._rpc.create_group_chat(self.id, name, protect))
294
311
 
312
+ def create_broadcast(self, name: str) -> Chat:
313
+ """Create a new **broadcast channel**
314
+ (called "Channel" in the UI).
315
+
316
+ Broadcast channels are similar to groups on the sending device,
317
+ however, recipients get the messages in a read-only chat
318
+ and will not see who the other members are.
319
+
320
+ Called `broadcast` here rather than `channel`,
321
+ because the word "channel" already appears a lot in the code,
322
+ which would make it hard to grep for it.
323
+
324
+ After creation, the chat contains no recipients and is in _unpromoted_ state;
325
+ see `create_group()` for more information on the unpromoted state.
326
+
327
+ Returns the created chat.
328
+ """
329
+ return Chat(self, self._rpc.create_broadcast(self.id, name))
330
+
295
331
  def get_chat_by_id(self, chat_id: int) -> Chat:
296
332
  """Return the Chat instance with the given ID."""
297
333
  return Chat(self, chat_id)
@@ -9,6 +9,7 @@ class ContactFlag(IntEnum):
9
9
  """Bit flags for get_contacts() method."""
10
10
 
11
11
  ADD_SELF = 0x02
12
+ ADDRESS = 0x04
12
13
 
13
14
 
14
15
  class ChatlistFlag(IntEnum):
@@ -90,10 +91,40 @@ class ChatType(IntEnum):
90
91
  """Chat type."""
91
92
 
92
93
  UNDEFINED = 0
94
+
93
95
  SINGLE = 100
96
+ """1:1 chat, i.e. a direct chat with a single contact"""
97
+
94
98
  GROUP = 120
99
+
95
100
  MAILINGLIST = 140
96
- BROADCAST = 160
101
+
102
+ OUT_BROADCAST = 160
103
+ """Outgoing broadcast channel, called "Channel" in the UI.
104
+
105
+ The user can send into this channel,
106
+ and all recipients will receive messages
107
+ in an `IN_BROADCAST`.
108
+
109
+ Called `broadcast` here rather than `channel`,
110
+ because the word "channel" already appears a lot in the code,
111
+ which would make it hard to grep for it.
112
+ """
113
+
114
+ IN_BROADCAST = 165
115
+ """Incoming broadcast channel, called "Channel" in the UI.
116
+
117
+ This channel is read-only,
118
+ and we do not know who the other recipients are.
119
+
120
+ This is similar to a `MAILINGLIST`,
121
+ with the main difference being that
122
+ `IN_BROADCAST`s are encrypted.
123
+
124
+ Called `broadcast` here rather than `channel`,
125
+ because the word "channel" already appears a lot in the code,
126
+ which would make it hard to grep for it.
127
+ """
97
128
 
98
129
 
99
130
  class ChatVisibility(str, Enum):
@@ -37,10 +37,6 @@ class Contact:
37
37
  """Delete contact."""
38
38
  self._rpc.delete_contact(self.account.id, self.id)
39
39
 
40
- def reset_encryption(self) -> None:
41
- """Reset contact encryption."""
42
- self._rpc.reset_contact_encryption(self.account.id, self.id)
43
-
44
40
  def set_name(self, name: str) -> None:
45
41
  """Change the name of this contact."""
46
42
  self._rpc.change_contact_name(self.account.id, self.id, name)
@@ -2,7 +2,7 @@
2
2
 
3
3
  import json
4
4
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, Optional, Union
5
+ from typing import TYPE_CHECKING, List, Optional, Union
6
6
 
7
7
  from ._utils import AttrDict, futuremethod
8
8
  from .const import EventType
@@ -39,6 +39,11 @@ class Message:
39
39
  snapshot["message"] = self
40
40
  return snapshot
41
41
 
42
+ def get_read_receipts(self) -> List[AttrDict]:
43
+ """Get message read receipts."""
44
+ read_receipts = self._rpc.get_message_read_receipts(self.account.id, self.id)
45
+ return [AttrDict(read_receipt) for read_receipt in read_receipts]
46
+
42
47
  def get_reactions(self) -> Optional[AttrDict]:
43
48
  """Get message reactions."""
44
49
  reactions = self._rpc.get_message_reactions(self.account.id, self.id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deltachat-rpc-client
3
- Version: 1.160.0
3
+ Version: 2.1.0
4
4
  Summary: Python client for Delta Chat core JSON-RPC interface
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Intended Audience :: Developers
@@ -1,5 +1,4 @@
1
1
  import logging
2
- import time
3
2
 
4
3
  import pytest
5
4
 
@@ -16,14 +15,14 @@ def test_qr_setup_contact(acfactory, tmp_path) -> None:
16
15
  alice.wait_for_securejoin_inviter_success()
17
16
 
18
17
  # Test that Alice verified Bob's profile.
19
- alice_contact_bob = alice.get_contact_by_addr(bob.get_config("addr"))
18
+ alice_contact_bob = alice.create_contact(bob)
20
19
  alice_contact_bob_snapshot = alice_contact_bob.get_snapshot()
21
20
  assert alice_contact_bob_snapshot.is_verified
22
21
 
23
22
  bob.wait_for_securejoin_joiner_success()
24
23
 
25
24
  # Test that Bob verified Alice's profile.
26
- bob_contact_alice = bob.get_contact_by_addr(alice.get_config("addr"))
25
+ bob_contact_alice = bob.create_contact(alice)
27
26
  bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
28
27
  assert bob_contact_alice_snapshot.is_verified
29
28
 
@@ -84,7 +83,7 @@ def test_qr_securejoin(acfactory, protect):
84
83
  bob.wait_for_securejoin_joiner_success()
85
84
 
86
85
  # Test that Alice verified Bob's profile.
87
- alice_contact_bob = alice.get_contact_by_addr(bob.get_config("addr"))
86
+ alice_contact_bob = alice.create_contact(bob)
88
87
  alice_contact_bob_snapshot = alice_contact_bob.get_snapshot()
89
88
  assert alice_contact_bob_snapshot.is_verified
90
89
 
@@ -93,7 +92,7 @@ def test_qr_securejoin(acfactory, protect):
93
92
  assert snapshot.chat.get_basic_snapshot().is_protected == protect
94
93
 
95
94
  # Test that Bob verified Alice's profile.
96
- bob_contact_alice = bob.get_contact_by_addr(alice.get_config("addr"))
95
+ bob_contact_alice = bob.create_contact(alice)
97
96
  bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
98
97
  assert bob_contact_alice_snapshot.is_verified
99
98
 
@@ -101,7 +100,7 @@ def test_qr_securejoin(acfactory, protect):
101
100
  # Alice observes securejoin protocol and verifies Bob on second device.
102
101
  alice2.start_io()
103
102
  alice2.wait_for_securejoin_inviter_success()
104
- alice2_contact_bob = alice2.get_contact_by_addr(bob.get_config("addr"))
103
+ alice2_contact_bob = alice2.create_contact(bob)
105
104
  alice2_contact_bob_snapshot = alice2_contact_bob.get_snapshot()
106
105
  assert alice2_contact_bob_snapshot.is_verified
107
106
 
@@ -213,72 +212,8 @@ def test_setup_contact_resetup(acfactory) -> None:
213
212
  bob.wait_for_securejoin_joiner_success()
214
213
 
215
214
 
216
- def test_verified_group_recovery(acfactory) -> None:
217
- """Tests verified group recovery by reverifying a member and sending a message in a group."""
218
- ac1, ac2, ac3 = acfactory.get_online_accounts(3)
219
-
220
- logging.info("ac1 creates verified group")
221
- chat = ac1.create_group("Verified group", protect=True)
222
- assert chat.get_basic_snapshot().is_protected
223
-
224
- logging.info("ac2 joins verified group")
225
- qr_code = chat.get_qr_code()
226
- ac2.secure_join(qr_code)
227
- ac2.wait_for_securejoin_joiner_success()
228
-
229
- # ac1 has ac2 directly verified.
230
- ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
231
- assert ac1_contact_ac2.get_snapshot().verifier_id == SpecialContactId.SELF
232
-
233
- logging.info("ac3 joins verified group")
234
- ac3_chat = ac3.secure_join(qr_code)
235
- ac3.wait_for_securejoin_joiner_success()
236
- ac3.wait_for_incoming_msg_event() # Member added
237
-
238
- logging.info("ac2 logs in on a new device")
239
- ac2 = acfactory.resetup_account(ac2)
240
-
241
- logging.info("ac2 reverifies with ac3")
242
- qr_code = ac3.get_qr_code()
243
- ac2.secure_join(qr_code)
244
- ac2.wait_for_securejoin_joiner_success()
245
-
246
- logging.info("ac3 sends a message to the group")
247
- assert len(ac3_chat.get_contacts()) == 3
248
- ac3_chat.send_text("Hi!")
249
-
250
- snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
251
- assert snapshot.text == "Hi!"
252
-
253
- msg_id = ac2.wait_for_incoming_msg_event().msg_id
254
- message = ac2.get_message_by_id(msg_id)
255
- snapshot = message.get_snapshot()
256
- assert snapshot.text == "Hi!"
257
-
258
- # ac1 contact is verified for ac2 because ac3 gossiped ac1 key in the "Hi!" message.
259
- ac1_contact = ac2.get_contact_by_addr(ac1.get_config("addr"))
260
- assert ac1_contact.get_snapshot().is_verified
261
-
262
- # ac2 can write messages to the group.
263
- snapshot.chat.send_text("Works again!")
264
-
265
- snapshot = ac3.get_message_by_id(ac3.wait_for_incoming_msg_event().msg_id).get_snapshot()
266
- assert snapshot.text == "Works again!"
267
-
268
- snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
269
- assert snapshot.text == "Works again!"
270
-
271
- ac1_chat_messages = snapshot.chat.get_messages()
272
- ac2_addr = ac2.get_config("addr")
273
- assert ac1_chat_messages[-2].get_snapshot().text == f"Changed setup for {ac2_addr}"
274
-
275
- # ac2 is now verified by ac3 for ac1
276
- ac1_contact_ac3 = ac1.get_contact_by_addr(ac3.get_config("addr"))
277
- assert ac1_contact_ac2.get_snapshot().verifier_id == ac1_contact_ac3.id
278
-
279
-
280
215
  def test_verified_group_member_added_recovery(acfactory) -> None:
281
- """Tests verified group recovery by reverifiying than removing and adding a member back."""
216
+ """Tests verified group recovery by reverifying then removing and adding a member back."""
282
217
  ac1, ac2, ac3 = acfactory.get_online_accounts(3)
283
218
 
284
219
  logging.info("ac1 creates verified group")
@@ -291,7 +226,7 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
291
226
  ac2.wait_for_securejoin_joiner_success()
292
227
 
293
228
  # ac1 has ac2 directly verified.
294
- ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
229
+ ac1_contact_ac2 = ac1.create_contact(ac2)
295
230
  assert ac1_contact_ac2.get_snapshot().verifier_id == SpecialContactId.SELF
296
231
 
297
232
  logging.info("ac3 joins verified group")
@@ -299,6 +234,8 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
299
234
  ac3.wait_for_securejoin_joiner_success()
300
235
  ac3.wait_for_incoming_msg_event() # Member added
301
236
 
237
+ ac3_contact_ac2_old = ac3.create_contact(ac2)
238
+
302
239
  logging.info("ac2 logs in on a new device")
303
240
  ac2 = acfactory.resetup_account(ac2)
304
241
 
@@ -311,21 +248,10 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
311
248
  assert len(ac3_chat.get_contacts()) == 3
312
249
  ac3_chat.send_text("Hi!")
313
250
 
314
- msg_id = ac2.wait_for_incoming_msg_event().msg_id
315
- message = ac2.get_message_by_id(msg_id)
316
- snapshot = message.get_snapshot()
317
- logging.info("Received message %s", snapshot.text)
318
- assert snapshot.text == "Hi!"
319
-
320
251
  ac1.wait_for_incoming_msg_event() # Hi!
321
252
 
322
- ac3_contact_ac2 = ac3.get_contact_by_addr(ac2.get_config("addr"))
323
- ac3_chat.remove_contact(ac3_contact_ac2)
324
-
325
- msg_id = ac2.wait_for_incoming_msg_event().msg_id
326
- message = ac2.get_message_by_id(msg_id)
327
- snapshot = message.get_snapshot()
328
- assert "removed" in snapshot.text
253
+ ac3_contact_ac2 = ac3.create_contact(ac2)
254
+ ac3_chat.remove_contact(ac3_contact_ac2_old)
329
255
 
330
256
  snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
331
257
  assert "removed" in snapshot.text
@@ -354,19 +280,16 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
354
280
  snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
355
281
  assert snapshot.text == "Works again!"
356
282
 
357
- ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
283
+ ac1_contact_ac2 = ac1.create_contact(ac2)
284
+ ac1_contact_ac3 = ac1.create_contact(ac3)
358
285
  ac1_contact_ac2_snapshot = ac1_contact_ac2.get_snapshot()
359
286
  assert ac1_contact_ac2_snapshot.is_verified
360
- assert ac1_contact_ac2_snapshot.verifier_id == ac1.get_contact_by_addr(ac3.get_config("addr")).id
361
-
362
- # ac2 is now verified by ac3 for ac1
363
- ac1_contact_ac3 = ac1.get_contact_by_addr(ac3.get_config("addr"))
364
- assert ac1_contact_ac2.get_snapshot().verifier_id == ac1_contact_ac3.id
287
+ assert ac1_contact_ac2_snapshot.verifier_id == ac1_contact_ac3.id
365
288
 
366
289
 
367
290
  def test_qr_join_chat_with_pending_bobstate_issue4894(acfactory):
368
291
  """Regression test for
369
- issue <https://github.com/deltachat/deltachat-core-rust/issues/4894>.
292
+ issue <https://github.com/chatmail/core/issues/4894>.
370
293
  """
371
294
  ac1, ac2, ac3, ac4 = acfactory.get_online_accounts(4)
372
295
 
@@ -400,12 +323,12 @@ def test_qr_join_chat_with_pending_bobstate_issue4894(acfactory):
400
323
  logging.info("ac2 now has pending bobstate but ac1 is shutoff")
401
324
 
402
325
  # we meanwhile expect ac3/ac2 verification started in the beginning to have completed
403
- assert ac3.get_contact_by_addr(ac2.get_config("addr")).get_snapshot().is_verified
404
- assert ac2.get_contact_by_addr(ac3.get_config("addr")).get_snapshot().is_verified
326
+ assert ac3.create_contact(ac2).get_snapshot().is_verified
327
+ assert ac2.create_contact(ac3).get_snapshot().is_verified
405
328
 
406
329
  logging.info("ac3: create a verified group VG with ac2")
407
330
  vg = ac3.create_group("ac3-created", protect=True)
408
- vg.add_contact(ac3.get_contact_by_addr(ac2.get_config("addr")))
331
+ vg.add_contact(ac3.create_contact(ac2))
409
332
 
410
333
  # ensure ac2 receives message in VG
411
334
  vg.send_text("hello")
@@ -443,7 +366,7 @@ def test_qr_new_group_unblocked(acfactory):
443
366
  ac1.wait_for_securejoin_inviter_success()
444
367
 
445
368
  ac1_new_chat = ac1.create_group("Another group")
446
- ac1_new_chat.add_contact(ac1.get_contact_by_addr(ac2.get_config("addr")))
369
+ ac1_new_chat.add_contact(ac1.create_contact(ac2))
447
370
  # Receive "Member added" message.
448
371
  ac2.wait_for_incoming_msg_event()
449
372
 
@@ -577,30 +500,8 @@ def test_securejoin_after_contact_resetup(acfactory) -> None:
577
500
 
578
501
  # ac1 resetups the account.
579
502
  ac1 = acfactory.resetup_account(ac1)
580
-
581
- # Loop sending message from ac1 to ac2
582
- # until ac2 accepts new ac1 key.
583
- #
584
- # This may not happen immediately because resetup of ac1
585
- # rewinds "smeared timestamp" so Date: header for messages
586
- # sent by new ac1 are in the past compared to the last Date:
587
- # header sent by old ac1.
588
- while True:
589
- # ac1 sends a message to ac2.
590
- ac1_contact_ac2 = ac1.create_contact(ac2, "")
591
- ac1_chat_ac2 = ac1_contact_ac2.create_chat()
592
- ac1_chat_ac2.send_text("Hello!")
593
-
594
- # ac2 receives a message.
595
- snapshot = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
596
- assert snapshot.text == "Hello!"
597
- logging.info("ac2 received Hello!")
598
-
599
- # ac1 is no longer verified for ac2 as new Autocrypt key is not the same as old verified key.
600
- logging.info("ac2 addr={}, ac1 addr={}".format(ac2.get_config("addr"), ac1.get_config("addr")))
601
- if not ac2_contact_ac1.get_snapshot().is_verified:
602
- break
603
- time.sleep(1)
503
+ ac2_contact_ac1 = ac2.create_contact(ac1, "")
504
+ assert not ac2_contact_ac1.get_snapshot().is_verified
604
505
 
605
506
  # ac1 goes offline.
606
507
  ac1.remove()
@@ -11,7 +11,7 @@ from unittest.mock import MagicMock
11
11
  import pytest
12
12
 
13
13
  from deltachat_rpc_client import Contact, EventType, Message, events
14
- from deltachat_rpc_client.const import DownloadState, MessageState
14
+ from deltachat_rpc_client.const import ChatType, DownloadState, MessageState
15
15
  from deltachat_rpc_client.rpc import JsonRpcError
16
16
 
17
17
 
@@ -170,7 +170,7 @@ def test_account(acfactory) -> None:
170
170
  assert alice.get_size()
171
171
  assert alice.is_configured()
172
172
  assert not alice.get_avatar()
173
- assert alice.get_contact_by_addr(bob_addr) == alice_contact_bob
173
+ assert alice.get_contact_by_addr(bob_addr) is None # There is no address-contact, only key-contact
174
174
  assert alice.get_contacts()
175
175
  assert alice.get_contacts(snapshot=True)
176
176
  assert alice.self_contact
@@ -287,7 +287,6 @@ def test_contact(acfactory) -> None:
287
287
  assert repr(alice_contact_bob)
288
288
  alice_contact_bob.block()
289
289
  alice_contact_bob.unblock()
290
- alice_contact_bob.reset_encryption()
291
290
  alice_contact_bob.set_name("new name")
292
291
  alice_contact_bob.get_encryption_info()
293
292
  snapshot = alice_contact_bob.get_snapshot()
@@ -727,6 +726,26 @@ def test_markseen_contact_request(acfactory):
727
726
  assert message2.get_snapshot().state == MessageState.IN_SEEN
728
727
 
729
728
 
729
+ def test_read_receipt(acfactory):
730
+ """
731
+ Test sending a read receipt and ensure it is attributed to the correct contact.
732
+ """
733
+ alice, bob = acfactory.get_online_accounts(2)
734
+
735
+ alice_chat_bob = alice.create_chat(bob)
736
+ alice_contact_bob = alice.create_contact(bob)
737
+ bob.create_chat(alice) # Accept the chat
738
+
739
+ alice_chat_bob.send_text("Hello Bob!")
740
+ msg = bob.wait_for_incoming_msg()
741
+ msg.mark_seen()
742
+
743
+ read_msg = alice.get_message_by_id(alice.wait_for_event(EventType.MSG_READ).msg_id)
744
+ read_receipts = read_msg.get_read_receipts()
745
+ assert len(read_receipts) == 1
746
+ assert read_receipts[0].contact_id == alice_contact_bob.id
747
+
748
+
730
749
  def test_get_http_response(acfactory):
731
750
  alice = acfactory.new_configured_account()
732
751
  http_response = alice._rpc.get_http_response(alice.id, "https://example.org")
@@ -847,3 +866,36 @@ def test_delete_deltachat_folder(acfactory, direct_imap):
847
866
  assert msg.text == "hello"
848
867
 
849
868
  assert "DeltaChat" in ac1_direct_imap.list_folders()
869
+
870
+
871
+ def test_broadcast(acfactory):
872
+ alice, bob = acfactory.get_online_accounts(2)
873
+
874
+ alice_chat = alice.create_broadcast("My great channel")
875
+ snapshot = alice_chat.get_basic_snapshot()
876
+ assert snapshot.name == "My great channel"
877
+ assert snapshot.is_unpromoted
878
+ assert snapshot.is_encrypted
879
+ assert snapshot.chat_type == ChatType.OUT_BROADCAST
880
+
881
+ alice_contact_bob = alice.create_contact(bob, "Bob")
882
+ alice_chat.add_contact(alice_contact_bob)
883
+
884
+ alice_msg = alice_chat.send_message(text="hello").get_snapshot()
885
+ assert alice_msg.text == "hello"
886
+ assert alice_msg.show_padlock
887
+
888
+ bob_msg = bob.wait_for_incoming_msg().get_snapshot()
889
+ assert bob_msg.text == "hello"
890
+ assert bob_msg.show_padlock
891
+ assert bob_msg.error is None
892
+
893
+ bob_chat = bob.get_chat_by_id(bob_msg.chat_id)
894
+ bob_chat_snapshot = bob_chat.get_basic_snapshot()
895
+ assert bob_chat_snapshot.name == "My great channel"
896
+ assert not bob_chat_snapshot.is_unpromoted
897
+ assert bob_chat_snapshot.is_encrypted
898
+ assert bob_chat_snapshot.chat_type == ChatType.IN_BROADCAST
899
+ assert bob_chat_snapshot.is_contact_request
900
+
901
+ assert not bob_chat.can_send()