deltachat-rpc-client 2.37.0__tar.gz → 2.38.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.
- {deltachat_rpc_client-2.37.0/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-2.38.0}/PKG-INFO +1 -1
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/pyproject.toml +1 -1
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/_utils.py +10 -3
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/client.py +21 -8
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/message.py +8 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/pytestplugin.py +5 -5
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0/src/deltachat_rpc_client.egg-info}/PKG-INFO +1 -1
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_chatlist_events.py +1 -3
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_multitransport.py +23 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_something.py +264 -63
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/LICENSE +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/README.md +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/setup.cfg +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/__init__.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/account.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/chat.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/const.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/contact.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/deltachat.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/events.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/py.typed +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/rpc.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client.egg-info/SOURCES.txt +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_account_events.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_calls.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_cross_core.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_folders.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_iroh_webxdc.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_key_transfer.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_multidevice.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_securejoin.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_vcard.py +0 -0
- {deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/tests/test_webxdc.py +0 -0
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/_utils.py
RENAMED
|
@@ -44,8 +44,13 @@ class AttrDict(dict):
|
|
|
44
44
|
super().__setattr__(attr, val)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def _forever(_event: AttrDict) -> bool:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
|
|
47
51
|
def run_client_cli(
|
|
48
52
|
hooks: Optional[Iterable[Tuple[Callable, Union[type, "EventFilter"]]]] = None,
|
|
53
|
+
until: Callable[[AttrDict], bool] = _forever,
|
|
49
54
|
argv: Optional[list] = None,
|
|
50
55
|
**kwargs,
|
|
51
56
|
) -> None:
|
|
@@ -55,10 +60,11 @@ def run_client_cli(
|
|
|
55
60
|
"""
|
|
56
61
|
from .client import Client
|
|
57
62
|
|
|
58
|
-
_run_cli(Client, hooks, argv, **kwargs)
|
|
63
|
+
_run_cli(Client, until, hooks, argv, **kwargs)
|
|
59
64
|
|
|
60
65
|
|
|
61
66
|
def run_bot_cli(
|
|
67
|
+
until: Callable[[AttrDict], bool] = _forever,
|
|
62
68
|
hooks: Optional[Iterable[Tuple[Callable, Union[type, "EventFilter"]]]] = None,
|
|
63
69
|
argv: Optional[list] = None,
|
|
64
70
|
**kwargs,
|
|
@@ -69,11 +75,12 @@ def run_bot_cli(
|
|
|
69
75
|
"""
|
|
70
76
|
from .client import Bot
|
|
71
77
|
|
|
72
|
-
_run_cli(Bot, hooks, argv, **kwargs)
|
|
78
|
+
_run_cli(Bot, until, hooks, argv, **kwargs)
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
def _run_cli(
|
|
76
82
|
client_type: Type["Client"],
|
|
83
|
+
until: Callable[[AttrDict], bool] = _forever,
|
|
77
84
|
hooks: Optional[Iterable[Tuple[Callable, Union[type, "EventFilter"]]]] = None,
|
|
78
85
|
argv: Optional[list] = None,
|
|
79
86
|
**kwargs,
|
|
@@ -111,7 +118,7 @@ def _run_cli(
|
|
|
111
118
|
kwargs={"email": args.email, "password": args.password},
|
|
112
119
|
)
|
|
113
120
|
configure_thread.start()
|
|
114
|
-
client.
|
|
121
|
+
client.run_until(until)
|
|
115
122
|
|
|
116
123
|
|
|
117
124
|
def extract_addr(text: str) -> str:
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/client.py
RENAMED
|
@@ -14,6 +14,7 @@ from typing import (
|
|
|
14
14
|
|
|
15
15
|
from ._utils import (
|
|
16
16
|
AttrDict,
|
|
17
|
+
_forever,
|
|
17
18
|
parse_system_add_remove,
|
|
18
19
|
parse_system_image_changed,
|
|
19
20
|
parse_system_title_changed,
|
|
@@ -91,19 +92,28 @@ class Client:
|
|
|
91
92
|
|
|
92
93
|
def run_forever(self) -> None:
|
|
93
94
|
"""Process events forever."""
|
|
94
|
-
self.run_until(
|
|
95
|
+
self.run_until(_forever)
|
|
95
96
|
|
|
96
97
|
def run_until(self, func: Callable[[AttrDict], bool]) -> AttrDict:
|
|
97
|
-
"""
|
|
98
|
-
|
|
99
|
-
The callable should accept an AttrDict object representing the
|
|
100
|
-
last processed event. The event is returned when the callable
|
|
101
|
-
evaluates to True.
|
|
102
|
-
"""
|
|
98
|
+
"""Start the event processing loop."""
|
|
103
99
|
self.logger.debug("Listening to incoming events...")
|
|
104
100
|
if self.is_configured():
|
|
105
101
|
self.account.start_io()
|
|
106
102
|
self._process_messages() # Process old messages.
|
|
103
|
+
return self._process_events(until_func=func) # Loop over incoming events
|
|
104
|
+
|
|
105
|
+
def _process_events(
|
|
106
|
+
self,
|
|
107
|
+
until_func: Callable[[AttrDict], bool],
|
|
108
|
+
until_event: EventType = False,
|
|
109
|
+
) -> AttrDict:
|
|
110
|
+
"""Process events until the given callable evaluates to True,
|
|
111
|
+
or until a certain event happens.
|
|
112
|
+
|
|
113
|
+
The until_func callable should accept an AttrDict object representing
|
|
114
|
+
the last processed event. The event is returned when the callable
|
|
115
|
+
evaluates to True.
|
|
116
|
+
"""
|
|
107
117
|
while True:
|
|
108
118
|
event = self.account.wait_for_event()
|
|
109
119
|
event["kind"] = EventType(event.kind)
|
|
@@ -112,10 +122,13 @@ class Client:
|
|
|
112
122
|
if event.kind == EventType.INCOMING_MSG:
|
|
113
123
|
self._process_messages()
|
|
114
124
|
|
|
115
|
-
stop =
|
|
125
|
+
stop = until_func(event)
|
|
116
126
|
if stop:
|
|
117
127
|
return event
|
|
118
128
|
|
|
129
|
+
if event.kind == until_event:
|
|
130
|
+
return event
|
|
131
|
+
|
|
119
132
|
def _on_event(self, event: AttrDict, filter_type: Type[EventFilter] = RawEvent) -> None:
|
|
120
133
|
for hook, evfilter in self._hooks.get(filter_type, []):
|
|
121
134
|
if evfilter.filter(event):
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/message.py
RENAMED
|
@@ -44,6 +44,14 @@ class Message:
|
|
|
44
44
|
read_receipts = self._rpc.get_message_read_receipts(self.account.id, self.id)
|
|
45
45
|
return [AttrDict(read_receipt) for read_receipt in read_receipts]
|
|
46
46
|
|
|
47
|
+
def get_read_receipt_count(self) -> int:
|
|
48
|
+
"""
|
|
49
|
+
Returns count of read receipts on message.
|
|
50
|
+
|
|
51
|
+
This view count is meant as a feedback measure for the channel owner only.
|
|
52
|
+
"""
|
|
53
|
+
return self._rpc.get_message_read_receipt_count(self.account.id, self.id)
|
|
54
|
+
|
|
47
55
|
def get_reactions(self) -> Optional[AttrDict]:
|
|
48
56
|
"""Get message reactions."""
|
|
49
57
|
reactions = self._rpc.get_message_reactions(self.account.id, self.id)
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/pytestplugin.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
import os
|
|
6
7
|
import pathlib
|
|
7
8
|
import platform
|
|
@@ -204,14 +205,13 @@ def log():
|
|
|
204
205
|
|
|
205
206
|
class Printer:
|
|
206
207
|
def section(self, msg: str) -> None:
|
|
207
|
-
|
|
208
|
-
print("=" * 10, msg, "=" * 10)
|
|
208
|
+
logging.info("\n%s %s %s", "=" * 10, msg, "=" * 10)
|
|
209
209
|
|
|
210
210
|
def step(self, msg: str) -> None:
|
|
211
|
-
|
|
211
|
+
logging.info("%s step %s %s", "-" * 5, msg, "-" * 5)
|
|
212
212
|
|
|
213
213
|
def indent(self, msg: str) -> None:
|
|
214
|
-
|
|
214
|
+
logging.info(" " + msg)
|
|
215
215
|
|
|
216
216
|
return Printer()
|
|
217
217
|
|
|
@@ -261,7 +261,7 @@ def get_core_python_env(tmp_path_factory):
|
|
|
261
261
|
envs[core_version] = venv
|
|
262
262
|
python = find_path(venv, "python")
|
|
263
263
|
rpc_server_path = find_path(venv, "deltachat-rpc-server")
|
|
264
|
-
|
|
264
|
+
logging.info(f"Paths:\npython={python}\nrpc_server={rpc_server_path}")
|
|
265
265
|
return python, rpc_server_path
|
|
266
266
|
|
|
267
267
|
return get_versioned_venv
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import base64
|
|
4
|
-
import os
|
|
5
3
|
from typing import TYPE_CHECKING
|
|
6
4
|
|
|
7
5
|
from deltachat_rpc_client import Account, EventType, const
|
|
@@ -129,7 +127,7 @@ def test_download_on_demand(acfactory: ACFactory) -> None:
|
|
|
129
127
|
msg.get_snapshot().chat.accept()
|
|
130
128
|
bob.get_chat_by_id(chat_id).send_message(
|
|
131
129
|
"Hello World, this message is bigger than 5 bytes",
|
|
132
|
-
|
|
130
|
+
file="../test-data/image/screenshot.jpg",
|
|
133
131
|
)
|
|
134
132
|
|
|
135
133
|
message = alice.wait_for_incoming_msg()
|
|
@@ -207,6 +207,29 @@ def test_transport_synchronization(acfactory, log) -> None:
|
|
|
207
207
|
assert ac1_clone.wait_for_incoming_msg().get_snapshot().text == "Hello!"
|
|
208
208
|
|
|
209
209
|
|
|
210
|
+
def test_transport_sync_new_as_primary(acfactory, log) -> None:
|
|
211
|
+
"""Test synchronization of new transport as primary between devices."""
|
|
212
|
+
ac1 = acfactory.get_online_account()
|
|
213
|
+
ac1_clone = ac1.clone()
|
|
214
|
+
ac1_clone.bring_online()
|
|
215
|
+
|
|
216
|
+
qr = acfactory.get_account_qr()
|
|
217
|
+
|
|
218
|
+
ac1.add_transport_from_qr(qr)
|
|
219
|
+
ac1_transports = ac1.list_transports()
|
|
220
|
+
assert len(ac1_transports) == 2
|
|
221
|
+
[transport1, transport2] = ac1_transports
|
|
222
|
+
ac1_clone.wait_for_event(EventType.TRANSPORTS_MODIFIED)
|
|
223
|
+
assert len(ac1_clone.list_transports()) == 2
|
|
224
|
+
assert ac1_clone.get_config("configured_addr") == transport1["addr"]
|
|
225
|
+
|
|
226
|
+
log.section("ac1 changes the primary transport")
|
|
227
|
+
ac1.set_config("configured_addr", transport2["addr"])
|
|
228
|
+
|
|
229
|
+
ac1_clone.wait_for_event(EventType.TRANSPORTS_MODIFIED)
|
|
230
|
+
assert ac1_clone.get_config("configured_addr") == transport2["addr"]
|
|
231
|
+
|
|
232
|
+
|
|
210
233
|
def test_recognize_self_address(acfactory) -> None:
|
|
211
234
|
alice, bob = acfactory.get_online_accounts(2)
|
|
212
235
|
|
|
@@ -5,12 +5,11 @@ import logging
|
|
|
5
5
|
import os
|
|
6
6
|
import socket
|
|
7
7
|
import subprocess
|
|
8
|
-
import time
|
|
9
8
|
from unittest.mock import MagicMock
|
|
10
9
|
|
|
11
10
|
import pytest
|
|
12
11
|
|
|
13
|
-
from deltachat_rpc_client import
|
|
12
|
+
from deltachat_rpc_client import EventType, events
|
|
14
13
|
from deltachat_rpc_client.const import DownloadState, MessageState
|
|
15
14
|
from deltachat_rpc_client.pytestplugin import E2EE_INFO_MSGS
|
|
16
15
|
from deltachat_rpc_client.rpc import JsonRpcError
|
|
@@ -333,7 +332,7 @@ def test_receive_imf_failure(acfactory) -> None:
|
|
|
333
332
|
alice_contact_bob = alice.create_contact(bob, "Bob")
|
|
334
333
|
alice_chat_bob = alice_contact_bob.create_chat()
|
|
335
334
|
|
|
336
|
-
bob.set_config("
|
|
335
|
+
bob.set_config("simulate_receive_imf_error", "1")
|
|
337
336
|
alice_chat_bob.send_text("Hello!")
|
|
338
337
|
event = bob.wait_for_event(EventType.MSGS_CHANGED)
|
|
339
338
|
assert event.chat_id == bob.get_device_chat().id
|
|
@@ -343,18 +342,17 @@ def test_receive_imf_failure(acfactory) -> None:
|
|
|
343
342
|
version = bob.get_info()["deltachat_core_version"]
|
|
344
343
|
assert (
|
|
345
344
|
snapshot.text == "❌ Failed to receive a message:"
|
|
346
|
-
" Condition failed: `!context.get_config_bool(Config::
|
|
345
|
+
" Condition failed: `!context.get_config_bool(Config::SimulateReceiveImfError).await?`."
|
|
347
346
|
f" Core version {version}."
|
|
348
347
|
" Please report this bug to delta@merlinux.eu or https://support.delta.chat/."
|
|
349
348
|
)
|
|
350
349
|
|
|
351
350
|
# The failed message doesn't break the IMAP loop.
|
|
352
|
-
bob.set_config("
|
|
351
|
+
bob.set_config("simulate_receive_imf_error", "0")
|
|
353
352
|
alice_chat_bob.send_text("Hello again!")
|
|
354
353
|
message = bob.wait_for_incoming_msg()
|
|
355
354
|
snapshot = message.get_snapshot()
|
|
356
355
|
assert snapshot.text == "Hello again!"
|
|
357
|
-
assert snapshot.download_state == DownloadState.DONE
|
|
358
356
|
assert snapshot.error is None
|
|
359
357
|
|
|
360
358
|
|
|
@@ -372,17 +370,41 @@ def test_selfavatar_sync(acfactory, data, log) -> None:
|
|
|
372
370
|
alice.set_config("selfavatar", image)
|
|
373
371
|
avatar_config = alice.get_config("selfavatar")
|
|
374
372
|
avatar_hash = os.path.basename(avatar_config)
|
|
375
|
-
|
|
373
|
+
logging.info(f"Avatar hash is {avatar_hash}")
|
|
376
374
|
|
|
377
375
|
log.section("First device receives avatar change")
|
|
378
376
|
alice2.wait_for_event(EventType.SELFAVATAR_CHANGED)
|
|
379
377
|
avatar_config2 = alice2.get_config("selfavatar")
|
|
380
378
|
avatar_hash2 = os.path.basename(avatar_config2)
|
|
381
|
-
|
|
379
|
+
logging.info(f"Avatar hash on second device is {avatar_hash2}")
|
|
382
380
|
assert avatar_hash == avatar_hash2
|
|
383
381
|
assert avatar_config != avatar_config2
|
|
384
382
|
|
|
385
383
|
|
|
384
|
+
def test_dont_move_sync_msgs(acfactory, direct_imap):
|
|
385
|
+
addr, password = acfactory.get_credentials()
|
|
386
|
+
ac1 = acfactory.get_unconfigured_account()
|
|
387
|
+
ac1.set_config("bcc_self", "1")
|
|
388
|
+
ac1.set_config("fix_is_chatmail", "1")
|
|
389
|
+
ac1.add_or_update_transport({"addr": addr, "password": password})
|
|
390
|
+
ac1.bring_online()
|
|
391
|
+
ac1_direct_imap = direct_imap(ac1)
|
|
392
|
+
|
|
393
|
+
ac1_direct_imap.select_folder("Inbox")
|
|
394
|
+
# Sync messages may also be sent during configuration.
|
|
395
|
+
inbox_msg_cnt = len(ac1_direct_imap.get_all_messages())
|
|
396
|
+
|
|
397
|
+
ac1.set_config("displayname", "Alice")
|
|
398
|
+
ac1.wait_for_event(EventType.MSG_DELIVERED)
|
|
399
|
+
ac1.set_config("displayname", "Bob")
|
|
400
|
+
ac1.wait_for_event(EventType.MSG_DELIVERED)
|
|
401
|
+
ac1_direct_imap.select_folder("Inbox")
|
|
402
|
+
assert len(ac1_direct_imap.get_all_messages()) == inbox_msg_cnt + 2
|
|
403
|
+
|
|
404
|
+
ac1_direct_imap.select_folder("DeltaChat")
|
|
405
|
+
assert len(ac1_direct_imap.get_all_messages()) == 0
|
|
406
|
+
|
|
407
|
+
|
|
386
408
|
def test_reaction_seen_on_another_dev(acfactory) -> None:
|
|
387
409
|
alice, bob = acfactory.get_online_accounts(2)
|
|
388
410
|
alice2 = alice.clone()
|
|
@@ -687,60 +709,6 @@ def test_mdn_doesnt_break_autocrypt(acfactory) -> None:
|
|
|
687
709
|
assert snapshot.show_padlock
|
|
688
710
|
|
|
689
711
|
|
|
690
|
-
def test_reaction_to_partially_fetched_msg(acfactory, tmp_path):
|
|
691
|
-
"""See https://github.com/deltachat/deltachat-core-rust/issues/3688 "Partially downloaded
|
|
692
|
-
messages are received out of order".
|
|
693
|
-
|
|
694
|
-
If the Inbox contains X small messages followed by Y large messages followed by Z small
|
|
695
|
-
messages, Delta Chat first downloaded a batch of X+Z messages, and then a batch of Y messages.
|
|
696
|
-
|
|
697
|
-
This bug was discovered by @Simon-Laux while testing reactions PR #3644 and can be reproduced
|
|
698
|
-
with online test as follows:
|
|
699
|
-
- Bob enables download limit and goes offline.
|
|
700
|
-
- Alice sends a large message to Bob and reacts to this message with a thumbs-up.
|
|
701
|
-
- Bob goes online
|
|
702
|
-
- Bob first processes a reaction message and throws it away because there is no corresponding
|
|
703
|
-
message, then processes a partially downloaded message.
|
|
704
|
-
- As a result, Bob does not see a reaction
|
|
705
|
-
"""
|
|
706
|
-
download_limit = 300000
|
|
707
|
-
ac1, ac2 = acfactory.get_online_accounts(2)
|
|
708
|
-
ac1_addr = ac1.get_config("addr")
|
|
709
|
-
chat = ac1.create_chat(ac2)
|
|
710
|
-
ac2.set_config("download_limit", str(download_limit))
|
|
711
|
-
ac2.stop_io()
|
|
712
|
-
|
|
713
|
-
logging.info("sending small+large messages from ac1 to ac2")
|
|
714
|
-
msgs = []
|
|
715
|
-
msgs.append(chat.send_text("hi"))
|
|
716
|
-
path = tmp_path / "large"
|
|
717
|
-
path.write_bytes(os.urandom(download_limit + 1))
|
|
718
|
-
msgs.append(chat.send_file(str(path)))
|
|
719
|
-
for m in msgs:
|
|
720
|
-
m.wait_until_delivered()
|
|
721
|
-
|
|
722
|
-
logging.info("sending a reaction to the large message from ac1 to ac2")
|
|
723
|
-
# TODO: Find the reason of an occasional message reordering on the server (so that the reaction
|
|
724
|
-
# has a lower UID than the previous message). W/a is to sleep for some time to let the reaction
|
|
725
|
-
# have a later INTERNALDATE.
|
|
726
|
-
time.sleep(1.1)
|
|
727
|
-
react_str = "\N{THUMBS UP SIGN}"
|
|
728
|
-
msgs.append(msgs[-1].send_reaction(react_str))
|
|
729
|
-
msgs[-1].wait_until_delivered()
|
|
730
|
-
|
|
731
|
-
ac2.start_io()
|
|
732
|
-
|
|
733
|
-
logging.info("wait for ac2 to receive a reaction")
|
|
734
|
-
msg2 = Message(ac2, ac2.wait_for_reactions_changed().msg_id)
|
|
735
|
-
assert msg2.get_sender_contact().get_snapshot().address == ac1_addr
|
|
736
|
-
assert msg2.get_snapshot().download_state == DownloadState.AVAILABLE
|
|
737
|
-
reactions = msg2.get_reactions()
|
|
738
|
-
contacts = [Contact(ac2, int(i)) for i in reactions.reactions_by_contact]
|
|
739
|
-
assert len(contacts) == 1
|
|
740
|
-
assert contacts[0].get_snapshot().address == ac1_addr
|
|
741
|
-
assert list(reactions.reactions_by_contact.values())[0] == [react_str]
|
|
742
|
-
|
|
743
|
-
|
|
744
712
|
@pytest.mark.parametrize("n_accounts", [3, 2])
|
|
745
713
|
def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
|
|
746
714
|
download_limit = 300000
|
|
@@ -767,14 +735,159 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
|
|
|
767
735
|
path = tmp_path / "large"
|
|
768
736
|
path.write_bytes(os.urandom(download_limit + 1))
|
|
769
737
|
|
|
738
|
+
n_done = 0
|
|
770
739
|
for i in range(10):
|
|
771
740
|
logging.info("Sending message %s", i)
|
|
772
741
|
alice_group.send_file(str(path))
|
|
773
742
|
snapshot = bob.wait_for_incoming_msg().get_snapshot()
|
|
774
|
-
|
|
743
|
+
if snapshot.download_state == DownloadState.DONE:
|
|
744
|
+
n_done += 1
|
|
745
|
+
# Work around lost and reordered pre-messages.
|
|
746
|
+
assert n_done <= 1
|
|
747
|
+
else:
|
|
748
|
+
assert snapshot.download_state == DownloadState.AVAILABLE
|
|
775
749
|
assert snapshot.chat == bob_group
|
|
776
750
|
|
|
777
751
|
|
|
752
|
+
def test_download_small_msg_first(acfactory, tmp_path):
|
|
753
|
+
download_limit = 70000
|
|
754
|
+
|
|
755
|
+
alice, bob0 = acfactory.get_online_accounts(2)
|
|
756
|
+
bob1 = bob0.clone()
|
|
757
|
+
bob1.set_config("download_limit", str(download_limit))
|
|
758
|
+
|
|
759
|
+
chat = alice.create_chat(bob0)
|
|
760
|
+
path = tmp_path / "large_enough"
|
|
761
|
+
path.write_bytes(os.urandom(download_limit + 1))
|
|
762
|
+
# Less than 140K, so sent w/o a pre-message.
|
|
763
|
+
chat.send_file(str(path))
|
|
764
|
+
chat.send_text("hi")
|
|
765
|
+
bob0.create_chat(alice)
|
|
766
|
+
assert bob0.wait_for_incoming_msg().get_snapshot().text == ""
|
|
767
|
+
assert bob0.wait_for_incoming_msg().get_snapshot().text == "hi"
|
|
768
|
+
|
|
769
|
+
bob1.start_io()
|
|
770
|
+
bob1.create_chat(alice)
|
|
771
|
+
assert bob1.wait_for_incoming_msg().get_snapshot().text == "hi"
|
|
772
|
+
assert bob1.wait_for_incoming_msg().get_snapshot().text == ""
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
@pytest.mark.parametrize("delete_chat", [False, True])
|
|
776
|
+
def test_delete_available_msg(acfactory, tmp_path, direct_imap, delete_chat):
|
|
777
|
+
"""
|
|
778
|
+
Tests `DownloadState.AVAILABLE` message deletion on the receiver side.
|
|
779
|
+
Also tests pre- and post-message deletion on the sender side.
|
|
780
|
+
"""
|
|
781
|
+
# Min. UI setting as of v2.35
|
|
782
|
+
download_limit = 163840
|
|
783
|
+
alice, bob = acfactory.get_online_accounts(2)
|
|
784
|
+
bob.set_config("download_limit", str(download_limit))
|
|
785
|
+
# Avoid immediate deletion from the server
|
|
786
|
+
alice.set_config("bcc_self", "1")
|
|
787
|
+
bob.set_config("bcc_self", "1")
|
|
788
|
+
|
|
789
|
+
chat_alice = alice.create_chat(bob)
|
|
790
|
+
path = tmp_path / "large"
|
|
791
|
+
path.write_bytes(os.urandom(download_limit + 1))
|
|
792
|
+
msg_alice = chat_alice.send_file(str(path))
|
|
793
|
+
msg_bob = bob.wait_for_incoming_msg()
|
|
794
|
+
msg_bob_snapshot = msg_bob.get_snapshot()
|
|
795
|
+
assert msg_bob_snapshot.download_state == DownloadState.AVAILABLE
|
|
796
|
+
chat_bob = bob.get_chat_by_id(msg_bob_snapshot.chat_id)
|
|
797
|
+
|
|
798
|
+
# Avoid DeleteMessages sync message
|
|
799
|
+
bob.set_config("bcc_self", "0")
|
|
800
|
+
if delete_chat:
|
|
801
|
+
chat_bob.delete()
|
|
802
|
+
else:
|
|
803
|
+
bob.delete_messages([msg_bob])
|
|
804
|
+
alice.wait_for_event(EventType.SMTP_MESSAGE_SENT)
|
|
805
|
+
alice.wait_for_event(EventType.SMTP_MESSAGE_SENT)
|
|
806
|
+
alice.set_config("bcc_self", "0")
|
|
807
|
+
if delete_chat:
|
|
808
|
+
chat_alice.delete()
|
|
809
|
+
else:
|
|
810
|
+
alice.delete_messages([msg_alice])
|
|
811
|
+
for acc in [bob, alice]:
|
|
812
|
+
if not delete_chat:
|
|
813
|
+
acc.wait_for_event(EventType.MSG_DELETED)
|
|
814
|
+
acc_direct_imap = direct_imap(acc)
|
|
815
|
+
# Messages may be deleted separately
|
|
816
|
+
while True:
|
|
817
|
+
acc.wait_for_event(EventType.IMAP_MESSAGE_DELETED)
|
|
818
|
+
while True:
|
|
819
|
+
event = acc.wait_for_event()
|
|
820
|
+
if event.kind == EventType.INFO and "Close/expunge succeeded." in event.msg:
|
|
821
|
+
break
|
|
822
|
+
if len(acc_direct_imap.get_all_messages()) == 0:
|
|
823
|
+
break
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
def test_delete_fully_downloaded_msg(acfactory, tmp_path, direct_imap):
|
|
827
|
+
alice, bob = acfactory.get_online_accounts(2)
|
|
828
|
+
# Avoid immediate deletion from the server
|
|
829
|
+
bob.set_config("bcc_self", "1")
|
|
830
|
+
|
|
831
|
+
chat_alice = alice.create_chat(bob)
|
|
832
|
+
path = tmp_path / "large"
|
|
833
|
+
# Big enough to be sent with a pre-message
|
|
834
|
+
path.write_bytes(os.urandom(300000))
|
|
835
|
+
chat_alice.send_file(str(path))
|
|
836
|
+
|
|
837
|
+
msg = bob.wait_for_incoming_msg()
|
|
838
|
+
msg_snapshot = msg.get_snapshot()
|
|
839
|
+
assert msg_snapshot.download_state == DownloadState.AVAILABLE
|
|
840
|
+
msgs_changed_event = bob.wait_for_msgs_changed_event()
|
|
841
|
+
assert msgs_changed_event.msg_id == msg.id
|
|
842
|
+
msg_snapshot = msg.get_snapshot()
|
|
843
|
+
assert msg_snapshot.download_state == DownloadState.DONE
|
|
844
|
+
|
|
845
|
+
bob_direct_imap = direct_imap(bob)
|
|
846
|
+
assert len(bob_direct_imap.get_all_messages()) == 2
|
|
847
|
+
# Avoid DeleteMessages sync message
|
|
848
|
+
bob.set_config("bcc_self", "0")
|
|
849
|
+
bob.delete_messages([msg])
|
|
850
|
+
bob.wait_for_event(EventType.MSG_DELETED)
|
|
851
|
+
# Messages may be deleted separately
|
|
852
|
+
while True:
|
|
853
|
+
bob.wait_for_event(EventType.IMAP_MESSAGE_DELETED)
|
|
854
|
+
while True:
|
|
855
|
+
event = bob.wait_for_event()
|
|
856
|
+
if event.kind == EventType.INFO and "Close/expunge succeeded." in event.msg:
|
|
857
|
+
break
|
|
858
|
+
if len(bob_direct_imap.get_all_messages()) == 0:
|
|
859
|
+
break
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def test_imap_autodelete_fully_downloaded_msg(acfactory, tmp_path, direct_imap):
|
|
863
|
+
alice, bob = acfactory.get_online_accounts(2)
|
|
864
|
+
|
|
865
|
+
chat_alice = alice.create_chat(bob)
|
|
866
|
+
path = tmp_path / "large"
|
|
867
|
+
# Big enough to be sent with a pre-message
|
|
868
|
+
path.write_bytes(os.urandom(300000))
|
|
869
|
+
chat_alice.send_file(str(path))
|
|
870
|
+
|
|
871
|
+
msg = bob.wait_for_incoming_msg()
|
|
872
|
+
msg_snapshot = msg.get_snapshot()
|
|
873
|
+
assert msg_snapshot.download_state == DownloadState.AVAILABLE
|
|
874
|
+
msgs_changed_event = bob.wait_for_msgs_changed_event()
|
|
875
|
+
assert msgs_changed_event.msg_id == msg.id
|
|
876
|
+
msg_snapshot = msg.get_snapshot()
|
|
877
|
+
assert msg_snapshot.download_state == DownloadState.DONE
|
|
878
|
+
|
|
879
|
+
bob_direct_imap = direct_imap(bob)
|
|
880
|
+
# Messages may be deleted separately
|
|
881
|
+
while True:
|
|
882
|
+
if len(bob_direct_imap.get_all_messages()) == 0:
|
|
883
|
+
break
|
|
884
|
+
bob.wait_for_event(EventType.IMAP_MESSAGE_DELETED)
|
|
885
|
+
while True:
|
|
886
|
+
event = bob.wait_for_event()
|
|
887
|
+
if event.kind == EventType.INFO and "Close/expunge succeeded." in event.msg:
|
|
888
|
+
break
|
|
889
|
+
|
|
890
|
+
|
|
778
891
|
def test_markseen_contact_request(acfactory):
|
|
779
892
|
"""
|
|
780
893
|
Test that seen status is synchronized for contact request messages
|
|
@@ -798,6 +911,47 @@ def test_markseen_contact_request(acfactory):
|
|
|
798
911
|
assert message2.get_snapshot().state == MessageState.IN_SEEN
|
|
799
912
|
|
|
800
913
|
|
|
914
|
+
@pytest.mark.parametrize("team_profile", [True, False])
|
|
915
|
+
def test_no_markseen_in_team_profile(team_profile, acfactory):
|
|
916
|
+
"""
|
|
917
|
+
Test that seen status is synchronized iff `team_profile` isn't set.
|
|
918
|
+
"""
|
|
919
|
+
alice, bob = acfactory.get_online_accounts(2)
|
|
920
|
+
if team_profile:
|
|
921
|
+
bob.set_config("team_profile", "1")
|
|
922
|
+
|
|
923
|
+
# Bob sets up a second device.
|
|
924
|
+
bob2 = bob.clone()
|
|
925
|
+
bob2.start_io()
|
|
926
|
+
|
|
927
|
+
alice_chat_bob = alice.create_chat(bob)
|
|
928
|
+
bob_chat_alice = bob.create_chat(alice)
|
|
929
|
+
bob2.create_chat(alice)
|
|
930
|
+
alice_chat_bob.send_text("Hello Bob!")
|
|
931
|
+
|
|
932
|
+
message = bob.wait_for_incoming_msg()
|
|
933
|
+
message2 = bob2.wait_for_incoming_msg()
|
|
934
|
+
assert message2.get_snapshot().state == MessageState.IN_FRESH
|
|
935
|
+
|
|
936
|
+
message.mark_seen()
|
|
937
|
+
|
|
938
|
+
# Send a message and wait until it arrives
|
|
939
|
+
# in order to wait until Bob2 gets the markseen message.
|
|
940
|
+
# This also tests that outgoing messages
|
|
941
|
+
# don't mark preceeding messages as seen in team profiles.
|
|
942
|
+
bob_chat_alice.send_text("Outgoing message")
|
|
943
|
+
while True:
|
|
944
|
+
outgoing = bob2.wait_for_msg(EventType.MSGS_CHANGED)
|
|
945
|
+
if outgoing.id != 0:
|
|
946
|
+
break
|
|
947
|
+
assert outgoing.get_snapshot().text == "Outgoing message"
|
|
948
|
+
|
|
949
|
+
if team_profile:
|
|
950
|
+
assert message2.get_snapshot().state == MessageState.IN_FRESH
|
|
951
|
+
else:
|
|
952
|
+
assert message2.get_snapshot().state == MessageState.IN_SEEN
|
|
953
|
+
|
|
954
|
+
|
|
801
955
|
def test_read_receipt(acfactory):
|
|
802
956
|
"""
|
|
803
957
|
Test sending a read receipt and ensure it is attributed to the correct contact.
|
|
@@ -817,6 +971,9 @@ def test_read_receipt(acfactory):
|
|
|
817
971
|
assert len(read_receipts) == 1
|
|
818
972
|
assert read_receipts[0].contact_id == alice_contact_bob.id
|
|
819
973
|
|
|
974
|
+
read_receipt_cnt = read_msg.get_read_receipt_count()
|
|
975
|
+
assert read_receipt_cnt == 1
|
|
976
|
+
|
|
820
977
|
|
|
821
978
|
def test_get_http_response(acfactory):
|
|
822
979
|
alice = acfactory.new_configured_account()
|
|
@@ -1020,6 +1177,30 @@ def test_leave_broadcast(acfactory, all_devices_online):
|
|
|
1020
1177
|
check_account(bob2, bob2.create_contact(alice), inviter_side=False)
|
|
1021
1178
|
|
|
1022
1179
|
|
|
1180
|
+
def test_leave_and_delete_group(acfactory, log):
|
|
1181
|
+
alice, bob = acfactory.get_online_accounts(2)
|
|
1182
|
+
|
|
1183
|
+
log.section("Alice creates a group")
|
|
1184
|
+
alice_chat = alice.create_group("Group")
|
|
1185
|
+
alice_chat.add_contact(bob)
|
|
1186
|
+
assert len(alice_chat.get_contacts()) == 2 # Alice and Bob
|
|
1187
|
+
alice_chat.send_text("hello")
|
|
1188
|
+
|
|
1189
|
+
log.section("Bob sees the group, and leaves and deletes it")
|
|
1190
|
+
msg = bob.wait_for_incoming_msg().get_snapshot()
|
|
1191
|
+
assert msg.text == "hello"
|
|
1192
|
+
msg.chat.accept()
|
|
1193
|
+
|
|
1194
|
+
msg.chat.leave()
|
|
1195
|
+
# Bob deletes the chat. This must not prevent the leave message from being sent.
|
|
1196
|
+
msg.chat.delete()
|
|
1197
|
+
|
|
1198
|
+
log.section("Alice receives the delete message")
|
|
1199
|
+
# After Bob left, only Alice will be left in the group:
|
|
1200
|
+
while len(alice_chat.get_contacts()) != 1:
|
|
1201
|
+
alice.wait_for_event(EventType.CHAT_MODIFIED)
|
|
1202
|
+
|
|
1203
|
+
|
|
1023
1204
|
def test_immediate_autodelete(acfactory, direct_imap, log):
|
|
1024
1205
|
ac1, ac2 = acfactory.get_online_accounts(2)
|
|
1025
1206
|
|
|
@@ -1152,3 +1333,23 @@ def test_synchronize_member_list_on_group_rejoin(acfactory, log):
|
|
|
1152
1333
|
|
|
1153
1334
|
assert chat.num_contacts() == 2
|
|
1154
1335
|
assert msg.get_snapshot().chat.num_contacts() == 2
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
def test_large_message(acfactory) -> None:
|
|
1339
|
+
"""
|
|
1340
|
+
Test sending large message without download limit set,
|
|
1341
|
+
so it is sent with pre-message but downloaded without user interaction.
|
|
1342
|
+
"""
|
|
1343
|
+
alice, bob = acfactory.get_online_accounts(2)
|
|
1344
|
+
|
|
1345
|
+
alice_chat_bob = alice.create_chat(bob)
|
|
1346
|
+
alice_chat_bob.send_message(
|
|
1347
|
+
"Hello World, this message is bigger than 5 bytes",
|
|
1348
|
+
file="../test-data/image/screenshot.jpg",
|
|
1349
|
+
)
|
|
1350
|
+
|
|
1351
|
+
msg = bob.wait_for_incoming_msg()
|
|
1352
|
+
msgs_changed_event = bob.wait_for_msgs_changed_event()
|
|
1353
|
+
assert msg.id == msgs_changed_event.msg_id
|
|
1354
|
+
snapshot = msg.get_snapshot()
|
|
1355
|
+
assert snapshot.text == "Hello World, this message is bigger than 5 bytes"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/__init__.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/account.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/chat.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/const.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/contact.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/deltachat.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/events.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.37.0 → deltachat_rpc_client-2.38.0}/src/deltachat_rpc_client/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|