deltachat-rpc-client 1.157.2__tar.gz → 1.157.3__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 (31) hide show
  1. {deltachat_rpc_client-1.157.2/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-1.157.3}/PKG-INFO +1 -1
  2. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/pyproject.toml +1 -1
  3. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/account.py +18 -32
  4. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/pytestplugin.py +48 -0
  5. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3/src/deltachat_rpc_client.egg-info}/PKG-INFO +1 -1
  6. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_iroh_webxdc.py +6 -15
  7. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_key_transfer.py +3 -0
  8. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_securejoin.py +3 -8
  9. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_something.py +40 -33
  10. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_webxdc.py +3 -9
  11. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/LICENSE +0 -0
  12. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/README.md +0 -0
  13. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/setup.cfg +0 -0
  14. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/__init__.py +0 -0
  15. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/_utils.py +0 -0
  16. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/chat.py +0 -0
  17. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/client.py +0 -0
  18. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/const.py +0 -0
  19. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/contact.py +0 -0
  20. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/deltachat.py +0 -0
  21. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/events.py +0 -0
  22. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/message.py +0 -0
  23. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/py.typed +0 -0
  24. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client/rpc.py +0 -0
  25. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client.egg-info/SOURCES.txt +0 -0
  26. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
  27. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
  28. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
  29. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_account_events.py +0 -0
  30. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_chatlist_events.py +0 -0
  31. {deltachat_rpc_client-1.157.2 → deltachat_rpc_client-1.157.3}/tests/test_vcard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: deltachat-rpc-client
3
- Version: 1.157.2
3
+ Version: 1.157.3
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.157.2"
7
+ version = "1.157.3"
8
8
  description = "Python client for Delta Chat core JSON-RPC interface"
9
9
  classifiers = [
10
10
  "Development Status :: 5 - Production/Stable",
@@ -1,8 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from pathlib import Path
5
- from tempfile import TemporaryDirectory
6
4
  from typing import TYPE_CHECKING, Optional, Union
7
5
  from warnings import warn
8
6
 
@@ -28,9 +26,12 @@ class Account:
28
26
  def _rpc(self) -> "Rpc":
29
27
  return self.manager.rpc
30
28
 
31
- def wait_for_event(self) -> AttrDict:
29
+ def wait_for_event(self, event_type=None) -> AttrDict:
32
30
  """Wait until the next event and return it."""
33
- return AttrDict(self._rpc.wait_for_event(self.id))
31
+ while True:
32
+ next_event = AttrDict(self._rpc.wait_for_event(self.id))
33
+ if event_type is None or next_event.kind == event_type:
34
+ return next_event
34
35
 
35
36
  def clear_all_events(self):
36
37
  """Removes all queued-up events for a given account. Useful for tests."""
@@ -41,14 +42,14 @@ class Account:
41
42
  self._rpc.remove_account(self.id)
42
43
 
43
44
  def clone(self) -> "Account":
44
- """Clone given account."""
45
- with TemporaryDirectory() as tmp_dir:
46
- tmp_path = Path(tmp_dir)
47
- self.export_backup(tmp_path)
48
- files = list(tmp_path.glob("*.tar"))
49
- new_account = self.manager.add_account()
50
- new_account.import_backup(files[0])
51
- return new_account
45
+ """Clone given account.
46
+ This uses backup-transfer via iroh, i.e. the 'Add second device' feature."""
47
+ future = self._rpc.provide_backup.future(self.id)
48
+ qr = self._rpc.get_backup_qr(self.id)
49
+ new_account = self.manager.add_account()
50
+ new_account._rpc.get_backup(new_account.id, qr)
51
+ future()
52
+ return new_account
52
53
 
53
54
  def start_io(self) -> None:
54
55
  """Start the account I/O."""
@@ -112,10 +113,7 @@ class Account:
112
113
  def bring_online(self):
113
114
  """Start I/O and wait until IMAP becomes IDLE."""
114
115
  self.start_io()
115
- while True:
116
- event = self.wait_for_event()
117
- if event.kind == EventType.IMAP_INBOX_IDLE:
118
- break
116
+ self.wait_for_event(EventType.IMAP_INBOX_IDLE)
119
117
 
120
118
  def create_contact(self, obj: Union[int, str, Contact], name: Optional[str] = None) -> Contact:
121
119
  """Create a new Contact or return an existing one.
@@ -334,24 +332,15 @@ class Account:
334
332
 
335
333
  def wait_for_incoming_msg_event(self):
336
334
  """Wait for incoming message event and return it."""
337
- while True:
338
- event = self.wait_for_event()
339
- if event.kind == EventType.INCOMING_MSG:
340
- return event
335
+ return self.wait_for_event(EventType.INCOMING_MSG)
341
336
 
342
337
  def wait_for_msgs_changed_event(self):
343
338
  """Wait for messages changed event and return it."""
344
- while True:
345
- event = self.wait_for_event()
346
- if event.kind == EventType.MSGS_CHANGED:
347
- return event
339
+ return self.wait_for_event(EventType.MSGS_CHANGED)
348
340
 
349
341
  def wait_for_msgs_noticed_event(self):
350
342
  """Wait for messages noticed event and return it."""
351
- while True:
352
- event = self.wait_for_event()
353
- if event.kind == EventType.MSGS_NOTICED:
354
- return event
343
+ return self.wait_for_event(EventType.MSGS_NOTICED)
355
344
 
356
345
  def wait_for_incoming_msg(self):
357
346
  """Wait for incoming message and return it.
@@ -372,10 +361,7 @@ class Account:
372
361
  break
373
362
 
374
363
  def wait_for_reactions_changed(self):
375
- while True:
376
- event = self.wait_for_event()
377
- if event.kind == EventType.REACTIONS_CHANGED:
378
- return event
364
+ return self.wait_for_event(EventType.REACTIONS_CHANGED)
379
365
 
380
366
  def get_fresh_messages_in_arrival_order(self) -> list[Message]:
381
367
  """Return fresh messages list sorted in the order of their arrival, with ascending IDs."""
@@ -4,6 +4,7 @@ import os
4
4
  import random
5
5
  from typing import AsyncGenerator, Optional
6
6
 
7
+ import py
7
8
  import pytest
8
9
 
9
10
  from . import Account, AttrDict, Bot, Chat, Client, DeltaChat, EventType, Message
@@ -124,3 +125,50 @@ def rpc(tmp_path) -> AsyncGenerator:
124
125
  @pytest.fixture
125
126
  def acfactory(rpc) -> AsyncGenerator:
126
127
  return ACFactory(DeltaChat(rpc))
128
+
129
+
130
+ @pytest.fixture
131
+ def data():
132
+ """Test data."""
133
+
134
+ class Data:
135
+ def __init__(self) -> None:
136
+ for path in reversed(py.path.local(__file__).parts()):
137
+ datadir = path.join("test-data")
138
+ if datadir.isdir():
139
+ self.path = datadir
140
+ return
141
+ raise Exception("Data path cannot be found")
142
+
143
+ def get_path(self, bn):
144
+ """return path of file or None if it doesn't exist."""
145
+ fn = os.path.join(self.path, *bn.split("/"))
146
+ assert os.path.exists(fn)
147
+ return fn
148
+
149
+ def read_path(self, bn, mode="r"):
150
+ fn = self.get_path(bn)
151
+ if fn is not None:
152
+ with open(fn, mode) as f:
153
+ return f.read()
154
+ return None
155
+
156
+ return Data()
157
+
158
+
159
+ @pytest.fixture
160
+ def log():
161
+ """Log printer fixture."""
162
+
163
+ class Printer:
164
+ def section(self, msg: str) -> None:
165
+ print()
166
+ print("=" * 10, msg, "=" * 10)
167
+
168
+ def step(self, msg: str) -> None:
169
+ print("-" * 5, "step " + msg, "-" * 5)
170
+
171
+ def indent(self, msg: str) -> None:
172
+ print(" " + msg)
173
+
174
+ return Printer()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: deltachat-rpc-client
3
- Version: 1.157.2
3
+ Version: 1.157.3
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
@@ -175,17 +175,11 @@ def test_no_duplicate_messages(acfactory, path_to_webxdc):
175
175
 
176
176
  threading.Thread(target=thread_run, daemon=True).start()
177
177
 
178
- while 1:
179
- event = ac2.wait_for_event()
180
- if event.kind == EventType.WEBXDC_REALTIME_DATA:
181
- n = int(bytes(event.data).decode())
182
- break
178
+ event = ac2.wait_for_event(EventType.WEBXDC_REALTIME_DATA)
179
+ n = int(bytes(event.data).decode())
183
180
 
184
- while 1:
185
- event = ac2.wait_for_event()
186
- if event.kind == EventType.WEBXDC_REALTIME_DATA:
187
- assert int(bytes(event.data).decode()) > n
188
- break
181
+ event = ac2.wait_for_event(EventType.WEBXDC_REALTIME_DATA)
182
+ assert int(bytes(event.data).decode()) > n
189
183
 
190
184
 
191
185
  def test_no_reordering(acfactory, path_to_webxdc):
@@ -229,8 +223,5 @@ def test_advertisement_after_chatting(acfactory, path_to_webxdc):
229
223
  ac2_hello_msg_snapshot.chat.accept()
230
224
 
231
225
  ac2_webxdc_msg.send_webxdc_realtime_advertisement()
232
- while 1:
233
- event = ac1.wait_for_event()
234
- if event.kind == EventType.WEBXDC_REALTIME_ADVERTISEMENT_RECEIVED:
235
- assert event.msg_id == ac1_webxdc_msg.id
236
- break
226
+ event = ac1.wait_for_event(EventType.WEBXDC_REALTIME_ADVERTISEMENT_RECEIVED)
227
+ assert event.msg_id == ac1_webxdc_msg.id
@@ -21,6 +21,7 @@ def test_autocrypt_setup_message_key_transfer(acfactory):
21
21
  alice2.set_config("addr", alice1.get_config("addr"))
22
22
  alice2.set_config("mail_pw", alice1.get_config("mail_pw"))
23
23
  alice2.configure()
24
+ alice2.bring_online()
24
25
 
25
26
  setup_code = alice1.initiate_autocrypt_key_transfer()
26
27
  msg = wait_for_autocrypt_setup_message(alice2)
@@ -34,10 +35,12 @@ def test_autocrypt_setup_message_key_transfer(acfactory):
34
35
 
35
36
  def test_ac_setup_message_twice(acfactory):
36
37
  alice1 = acfactory.get_online_account()
38
+
37
39
  alice2 = acfactory.get_unconfigured_account()
38
40
  alice2.set_config("addr", alice1.get_config("addr"))
39
41
  alice2.set_config("mail_pw", alice1.get_config("mail_pw"))
40
42
  alice2.configure()
43
+ alice2.bring_online()
41
44
 
42
45
  # Send the first Autocrypt Setup Message and ignore it.
43
46
  _setup_code = alice1.initiate_autocrypt_key_transfer()
@@ -76,17 +76,11 @@ def test_qr_securejoin(acfactory, protect):
76
76
  bob.secure_join(qr_code)
77
77
 
78
78
  # Alice deletes "vg-request".
79
- while True:
80
- event = alice.wait_for_event()
81
- if event["kind"] == "ImapMessageDeleted":
82
- break
79
+ alice.wait_for_event(EventType.IMAP_MESSAGE_DELETED)
83
80
  alice.wait_for_securejoin_inviter_success()
84
81
  # Bob deletes "vg-auth-required", Alice deletes "vg-request-with-auth".
85
82
  for ac in [alice, bob]:
86
- while True:
87
- event = ac.wait_for_event()
88
- if event["kind"] == "ImapMessageDeleted":
89
- break
83
+ ac.wait_for_event(EventType.IMAP_MESSAGE_DELETED)
90
84
  bob.wait_for_securejoin_joiner_success()
91
85
 
92
86
  # Test that Alice verified Bob's profile.
@@ -463,6 +457,7 @@ def test_qr_new_group_unblocked(acfactory):
463
457
  assert ac2_msg.chat.get_basic_snapshot().is_contact_request
464
458
 
465
459
 
460
+ @pytest.mark.skip(reason="AEAP is disabled for now")
466
461
  def test_aeap_flow_verified(acfactory):
467
462
  """Test that a new address is added to a contact when it changes its address."""
468
463
  ac1, ac2 = acfactory.get_online_accounts(2)
@@ -110,12 +110,9 @@ def test_account(acfactory) -> None:
110
110
  alice_chat_bob = alice_contact_bob.create_chat()
111
111
  alice_chat_bob.send_text("Hello!")
112
112
 
113
- while True:
114
- event = bob.wait_for_event()
115
- if event.kind == EventType.INCOMING_MSG:
116
- chat_id = event.chat_id
117
- msg_id = event.msg_id
118
- break
113
+ event = bob.wait_for_incoming_msg_event()
114
+ chat_id = event.chat_id
115
+ msg_id = event.msg_id
119
116
 
120
117
  message = bob.get_message_by_id(msg_id)
121
118
  snapshot = message.get_snapshot()
@@ -287,6 +284,31 @@ def test_message(acfactory) -> None:
287
284
  assert reactions == snapshot.reactions
288
285
 
289
286
 
287
+ def test_selfavatar_sync(acfactory, data, log) -> None:
288
+ alice = acfactory.get_online_account()
289
+
290
+ log.section("Alice adds a second device")
291
+ alice2 = alice.clone()
292
+
293
+ log.section("Second device goes online")
294
+ alice2.start_io()
295
+
296
+ log.section("First device changes avatar")
297
+ image = data.get_path("image/avatar1000x1000.jpg")
298
+ alice.set_config("selfavatar", image)
299
+ avatar_config = alice.get_config("selfavatar")
300
+ avatar_hash = os.path.basename(avatar_config)
301
+ print("Info: avatar hash is ", avatar_hash)
302
+
303
+ log.section("First device receives avatar change")
304
+ alice2.wait_for_event(EventType.SELFAVATAR_CHANGED)
305
+ avatar_config2 = alice2.get_config("selfavatar")
306
+ avatar_hash2 = os.path.basename(avatar_config2)
307
+ print("Info: avatar hash on second device is ", avatar_hash2)
308
+ assert avatar_hash == avatar_hash2
309
+ assert avatar_config != avatar_config2
310
+
311
+
290
312
  def test_reaction_seen_on_another_dev(acfactory) -> None:
291
313
  alice, bob = acfactory.get_online_accounts(2)
292
314
  alice2 = alice.clone()
@@ -305,18 +327,11 @@ def test_reaction_seen_on_another_dev(acfactory) -> None:
305
327
  snapshot.chat.accept()
306
328
  message.send_reaction("😎")
307
329
  for a in [alice, alice2]:
308
- while True:
309
- event = a.wait_for_event()
310
- if event.kind == EventType.INCOMING_REACTION:
311
- break
330
+ a.wait_for_event(EventType.INCOMING_REACTION)
312
331
 
313
332
  alice2.clear_all_events()
314
333
  alice_chat_bob.mark_noticed()
315
- while True:
316
- event = alice2.wait_for_event()
317
- if event.kind == EventType.MSGS_NOTICED:
318
- chat_id = event.chat_id
319
- break
334
+ chat_id = alice2.wait_for_event(EventType.MSGS_NOTICED).chat_id
320
335
  alice2_contact_bob = alice2.get_contact_by_addr(bob_addr)
321
336
  alice2_chat_bob = alice2_contact_bob.create_chat()
322
337
  assert chat_id == alice2_chat_bob.id
@@ -334,16 +349,12 @@ def test_is_bot(acfactory) -> None:
334
349
  alice.set_config("bot", "1")
335
350
  alice_chat_bob.send_text("Hello!")
336
351
 
337
- while True:
338
- event = bob.wait_for_event()
339
- if event.kind == EventType.INCOMING_MSG:
340
- msg_id = event.msg_id
341
- message = bob.get_message_by_id(msg_id)
342
- snapshot = message.get_snapshot()
343
- assert snapshot.chat_id == event.chat_id
344
- assert snapshot.text == "Hello!"
345
- assert snapshot.is_bot
346
- break
352
+ event = bob.wait_for_incoming_msg_event()
353
+ message = bob.get_message_by_id(event.msg_id)
354
+ snapshot = message.get_snapshot()
355
+ assert snapshot.chat_id == event.chat_id
356
+ assert snapshot.text == "Hello!"
357
+ assert snapshot.is_bot
347
358
 
348
359
 
349
360
  def test_bot(acfactory) -> None:
@@ -504,10 +515,7 @@ def test_mdn_doesnt_break_autocrypt(acfactory) -> None:
504
515
 
505
516
  # Alice reads Bob's message.
506
517
  message.mark_seen()
507
- while True:
508
- event = bob.wait_for_event()
509
- if event.kind == EventType.MSG_READ:
510
- break
518
+ bob.wait_for_event(EventType.MSG_READ)
511
519
 
512
520
  # Bob sends a message to Alice, it should also be encrypted.
513
521
  bob_chat_alice.send_text("Hi Alice!")
@@ -677,10 +685,7 @@ def test_markseen_contact_request(acfactory):
677
685
  assert message2.get_snapshot().state == MessageState.IN_FRESH
678
686
 
679
687
  message.mark_seen()
680
- while True:
681
- event = bob2.wait_for_event()
682
- if event.kind == EventType.MSGS_NOTICED:
683
- break
688
+ bob2.wait_for_event(EventType.MSGS_NOTICED)
684
689
  assert message2.get_snapshot().state == MessageState.IN_SEEN
685
690
 
686
691
 
@@ -728,6 +733,8 @@ def test_no_old_msg_is_fresh(acfactory):
728
733
  assert ac1.create_chat(ac2).get_fresh_message_count() == 1
729
734
  assert len(list(ac1.get_fresh_messages())) == 1
730
735
 
736
+ ac1.wait_for_event(EventType.IMAP_INBOX_IDLE)
737
+
731
738
  logging.info("Send a message from ac1_clone to ac2 and check that ac1 marks the first message as 'noticed'")
732
739
  ac1_clone_chat.send_text("Hi back")
733
740
  ev = ac1.wait_for_msgs_noticed_event()
@@ -1,6 +1,3 @@
1
- from deltachat_rpc_client import EventType
2
-
3
-
4
1
  def test_webxdc(acfactory) -> None:
5
2
  alice, bob = acfactory.get_online_accounts(2)
6
3
 
@@ -9,12 +6,9 @@ def test_webxdc(acfactory) -> None:
9
6
  alice_chat_bob = alice_contact_bob.create_chat()
10
7
  alice_chat_bob.send_message(text="Let's play chess!", file="../test-data/webxdc/chess.xdc")
11
8
 
12
- while True:
13
- event = bob.wait_for_event()
14
- if event.kind == EventType.INCOMING_MSG:
15
- bob_chat_alice = bob.get_chat_by_id(event.chat_id)
16
- message = bob.get_message_by_id(event.msg_id)
17
- break
9
+ event = bob.wait_for_incoming_msg_event()
10
+ bob_chat_alice = bob.get_chat_by_id(event.chat_id)
11
+ message = bob.get_message_by_id(event.msg_id)
18
12
 
19
13
  webxdc_info = message.get_webxdc_info()
20
14
  assert webxdc_info == {