deltachat-rpc-client 2.27.0__tar.gz → 2.29.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.27.0/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-2.29.0}/PKG-INFO +3 -5
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/pyproject.toml +4 -6
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/account.py +4 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/message.py +4 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/pytestplugin.py +8 -2
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/rpc.py +23 -48
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0/src/deltachat_rpc_client.egg-info}/PKG-INFO +3 -5
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/SOURCES.txt +2 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_folders.py +1 -1
- deltachat_rpc_client-2.29.0/tests/test_multitransport.py +158 -0
- deltachat_rpc_client-2.29.0/tests/test_rpc_virtual.py +20 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_securejoin.py +8 -8
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_something.py +27 -17
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/LICENSE +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/README.md +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/setup.cfg +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/__init__.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/_utils.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/chat.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/client.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/const.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/contact.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/deltachat.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/events.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/py.typed +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_account_events.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_calls.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_chatlist_events.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_iroh_webxdc.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_key_transfer.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_multidevice.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_vcard.py +0 -0
- {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_webxdc.py +0 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deltachat-rpc-client
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.29.0
|
|
4
4
|
Summary: Python client for Delta Chat core JSON-RPC interface
|
|
5
|
+
License-Expression: MPL-2.0
|
|
5
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
7
|
Classifier: Intended Audience :: Developers
|
|
7
|
-
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
8
8
|
Classifier: Operating System :: POSIX :: Linux
|
|
9
9
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -17,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
16
|
Classifier: Topic :: Communications :: Chat
|
|
19
17
|
Classifier: Topic :: Communications :: Email
|
|
20
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.10
|
|
21
19
|
Description-Content-Type: text/markdown
|
|
22
20
|
License-File: LICENSE
|
|
23
21
|
Dynamic: license-file
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deltachat-rpc-client"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.29.0"
|
|
8
|
+
license = "MPL-2.0"
|
|
8
9
|
description = "Python client for Delta Chat core JSON-RPC interface"
|
|
9
10
|
classifiers = [
|
|
10
11
|
"Development Status :: 5 - Production/Stable",
|
|
11
12
|
"Intended Audience :: Developers",
|
|
12
|
-
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
|
|
13
13
|
"Operating System :: POSIX :: Linux",
|
|
14
14
|
"Operating System :: MacOS :: MacOS X",
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
|
-
"Programming Language :: Python :: 3.8",
|
|
17
|
-
"Programming Language :: Python :: 3.9",
|
|
18
16
|
"Programming Language :: Python :: 3.10",
|
|
19
17
|
"Programming Language :: Python :: 3.11",
|
|
20
18
|
"Programming Language :: Python :: 3.12",
|
|
@@ -24,7 +22,7 @@ classifiers = [
|
|
|
24
22
|
"Topic :: Communications :: Email"
|
|
25
23
|
]
|
|
26
24
|
readme = "README.md"
|
|
27
|
-
requires-python = ">=3.
|
|
25
|
+
requires-python = ">=3.10"
|
|
28
26
|
|
|
29
27
|
[tool.setuptools.package-data]
|
|
30
28
|
deltachat_rpc_client = [
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/account.py
RENAMED
|
@@ -130,6 +130,10 @@ class Account:
|
|
|
130
130
|
"""Add a new transport using a QR code."""
|
|
131
131
|
yield self._rpc.add_transport_from_qr.future(self.id, qr)
|
|
132
132
|
|
|
133
|
+
def delete_transport(self, addr: str):
|
|
134
|
+
"""Delete a transport."""
|
|
135
|
+
self._rpc.delete_transport(self.id, addr)
|
|
136
|
+
|
|
133
137
|
@futuremethod
|
|
134
138
|
def list_transports(self):
|
|
135
139
|
"""Return the list of all email accounts that are used as a transport in the current profile."""
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/message.py
RENAMED
|
@@ -60,6 +60,10 @@ class Message:
|
|
|
60
60
|
"""Mark the message as seen."""
|
|
61
61
|
self._rpc.markseen_msgs(self.account.id, [self.id])
|
|
62
62
|
|
|
63
|
+
def exists(self) -> bool:
|
|
64
|
+
"""Return True if the message exists."""
|
|
65
|
+
return bool(self._rpc.get_existing_msg_ids(self.account.id, [self.id]))
|
|
66
|
+
|
|
63
67
|
def continue_autocrypt_key_transfer(self, setup_code: str) -> None:
|
|
64
68
|
"""Continue the Autocrypt Setup Message key transfer.
|
|
65
69
|
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/pytestplugin.py
RENAMED
|
@@ -40,12 +40,17 @@ class ACFactory:
|
|
|
40
40
|
username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
|
|
41
41
|
return f"{username}@{domain}", f"{username}${username}"
|
|
42
42
|
|
|
43
|
+
def get_account_qr(self):
|
|
44
|
+
"""Return "dcaccount:" QR code for testing chatmail relay."""
|
|
45
|
+
domain = os.getenv("CHATMAIL_DOMAIN")
|
|
46
|
+
return f"dcaccount:{domain}"
|
|
47
|
+
|
|
43
48
|
@futuremethod
|
|
44
49
|
def new_configured_account(self):
|
|
45
50
|
"""Create a new configured account."""
|
|
46
51
|
account = self.get_unconfigured_account()
|
|
47
|
-
|
|
48
|
-
yield account.add_transport_from_qr.future(
|
|
52
|
+
qr = self.get_account_qr()
|
|
53
|
+
yield account.add_transport_from_qr.future(qr)
|
|
49
54
|
|
|
50
55
|
assert account.is_configured()
|
|
51
56
|
return account
|
|
@@ -77,6 +82,7 @@ class ACFactory:
|
|
|
77
82
|
ac_clone = self.get_unconfigured_account()
|
|
78
83
|
for transport in transports:
|
|
79
84
|
ac_clone.add_or_update_transport(transport)
|
|
85
|
+
ac_clone.bring_online()
|
|
80
86
|
return ac_clone
|
|
81
87
|
|
|
82
88
|
def get_accepted_chat(self, ac1: Account, ac2: Account) -> Chat:
|
|
@@ -9,7 +9,7 @@ import os
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import sys
|
|
11
11
|
from queue import Empty, Queue
|
|
12
|
-
from threading import
|
|
12
|
+
from threading import Thread
|
|
13
13
|
from typing import Any, Iterator, Optional
|
|
14
14
|
|
|
15
15
|
|
|
@@ -17,25 +17,6 @@ class JsonRpcError(Exception):
|
|
|
17
17
|
"""JSON-RPC error."""
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class RpcFuture:
|
|
21
|
-
"""RPC future waiting for RPC call result."""
|
|
22
|
-
|
|
23
|
-
def __init__(self, rpc: "Rpc", request_id: int, event: Event):
|
|
24
|
-
self.rpc = rpc
|
|
25
|
-
self.request_id = request_id
|
|
26
|
-
self.event = event
|
|
27
|
-
|
|
28
|
-
def __call__(self):
|
|
29
|
-
"""Wait for the future to return the result."""
|
|
30
|
-
self.event.wait()
|
|
31
|
-
response = self.rpc.request_results.pop(self.request_id)
|
|
32
|
-
if "error" in response:
|
|
33
|
-
raise JsonRpcError(response["error"])
|
|
34
|
-
if "result" in response:
|
|
35
|
-
return response["result"]
|
|
36
|
-
return None
|
|
37
|
-
|
|
38
|
-
|
|
39
20
|
class RpcMethod:
|
|
40
21
|
"""RPC method."""
|
|
41
22
|
|
|
@@ -57,17 +38,23 @@ class RpcMethod:
|
|
|
57
38
|
"params": args,
|
|
58
39
|
"id": request_id,
|
|
59
40
|
}
|
|
60
|
-
|
|
61
|
-
self.rpc.request_events[request_id] = event
|
|
41
|
+
self.rpc.request_results[request_id] = queue = Queue()
|
|
62
42
|
self.rpc.request_queue.put(request)
|
|
63
43
|
|
|
64
|
-
|
|
44
|
+
def rpc_future():
|
|
45
|
+
"""Wait for the request to receive a result."""
|
|
46
|
+
response = queue.get()
|
|
47
|
+
if "error" in response:
|
|
48
|
+
raise JsonRpcError(response["error"])
|
|
49
|
+
return response.get("result", None)
|
|
50
|
+
|
|
51
|
+
return rpc_future
|
|
65
52
|
|
|
66
53
|
|
|
67
54
|
class Rpc:
|
|
68
55
|
"""RPC client."""
|
|
69
56
|
|
|
70
|
-
def __init__(self, accounts_dir: Optional[str] = None, **kwargs):
|
|
57
|
+
def __init__(self, accounts_dir: Optional[str] = None, rpc_server_path="deltachat-rpc-server", **kwargs):
|
|
71
58
|
"""Initialize RPC client.
|
|
72
59
|
|
|
73
60
|
The given arguments will be passed to subprocess.Popen().
|
|
@@ -79,13 +66,12 @@ class Rpc:
|
|
|
79
66
|
}
|
|
80
67
|
|
|
81
68
|
self._kwargs = kwargs
|
|
69
|
+
self.rpc_server_path = rpc_server_path
|
|
82
70
|
self.process: subprocess.Popen
|
|
83
71
|
self.id_iterator: Iterator[int]
|
|
84
72
|
self.event_queues: dict[int, Queue]
|
|
85
|
-
# Map from request ID to
|
|
86
|
-
self.
|
|
87
|
-
# Map from request ID to the result.
|
|
88
|
-
self.request_results: dict[int, Any]
|
|
73
|
+
# Map from request ID to a Queue which provides a single result
|
|
74
|
+
self.request_results: dict[int, Queue]
|
|
89
75
|
self.request_queue: Queue[Any]
|
|
90
76
|
self.closing: bool
|
|
91
77
|
self.reader_thread: Thread
|
|
@@ -94,27 +80,18 @@ class Rpc:
|
|
|
94
80
|
|
|
95
81
|
def start(self) -> None:
|
|
96
82
|
"""Start RPC server subprocess."""
|
|
83
|
+
popen_kwargs = {"stdin": subprocess.PIPE, "stdout": subprocess.PIPE}
|
|
97
84
|
if sys.version_info >= (3, 11):
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
stdin=subprocess.PIPE,
|
|
101
|
-
stdout=subprocess.PIPE,
|
|
102
|
-
# Prevent subprocess from capturing SIGINT.
|
|
103
|
-
process_group=0,
|
|
104
|
-
**self._kwargs,
|
|
105
|
-
)
|
|
85
|
+
# Prevent subprocess from capturing SIGINT.
|
|
86
|
+
popen_kwargs["process_group"] = 0
|
|
106
87
|
else:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
preexec_fn=os.setpgrp, # noqa: PLW1509
|
|
113
|
-
**self._kwargs,
|
|
114
|
-
)
|
|
88
|
+
# `process_group` is not supported before Python 3.11.
|
|
89
|
+
popen_kwargs["preexec_fn"] = os.setpgrp # noqa: PLW1509
|
|
90
|
+
|
|
91
|
+
popen_kwargs.update(self._kwargs)
|
|
92
|
+
self.process = subprocess.Popen(self.rpc_server_path, **popen_kwargs)
|
|
115
93
|
self.id_iterator = itertools.count(start=1)
|
|
116
94
|
self.event_queues = {}
|
|
117
|
-
self.request_events = {}
|
|
118
95
|
self.request_results = {}
|
|
119
96
|
self.request_queue = Queue()
|
|
120
97
|
self.closing = False
|
|
@@ -149,9 +126,7 @@ class Rpc:
|
|
|
149
126
|
response = json.loads(line)
|
|
150
127
|
if "id" in response:
|
|
151
128
|
response_id = response["id"]
|
|
152
|
-
|
|
153
|
-
self.request_results[response_id] = response
|
|
154
|
-
event.set()
|
|
129
|
+
self.request_results.pop(response_id).put(response)
|
|
155
130
|
else:
|
|
156
131
|
logging.warning("Got a response without ID: %s", response)
|
|
157
132
|
except Exception:
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deltachat-rpc-client
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.29.0
|
|
4
4
|
Summary: Python client for Delta Chat core JSON-RPC interface
|
|
5
|
+
License-Expression: MPL-2.0
|
|
5
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
7
|
Classifier: Intended Audience :: Developers
|
|
7
|
-
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
8
8
|
Classifier: Operating System :: POSIX :: Linux
|
|
9
9
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -17,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
16
|
Classifier: Topic :: Communications :: Chat
|
|
19
17
|
Classifier: Topic :: Communications :: Email
|
|
20
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.10
|
|
21
19
|
Description-Content-Type: text/markdown
|
|
22
20
|
License-File: LICENSE
|
|
23
21
|
Dynamic: license-file
|
|
@@ -143,7 +143,7 @@ def test_delete_deltachat_folder(acfactory, direct_imap):
|
|
|
143
143
|
# Wait until new folder is created and UIDVALIDITY is updated.
|
|
144
144
|
while True:
|
|
145
145
|
event = ac1.wait_for_event()
|
|
146
|
-
if event.kind == EventType.INFO and "
|
|
146
|
+
if event.kind == EventType.INFO and "transport 1: UID validity for folder DeltaChat changed from " in event.msg:
|
|
147
147
|
break
|
|
148
148
|
|
|
149
149
|
ac2 = acfactory.get_online_account()
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from deltachat_rpc_client.rpc import JsonRpcError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_add_second_address(acfactory) -> None:
|
|
7
|
+
account = acfactory.new_configured_account()
|
|
8
|
+
assert len(account.list_transports()) == 1
|
|
9
|
+
|
|
10
|
+
# When the first transport is created,
|
|
11
|
+
# mvbox_move and only_fetch_mvbox should be disabled.
|
|
12
|
+
assert account.get_config("mvbox_move") == "0"
|
|
13
|
+
assert account.get_config("only_fetch_mvbox") == "0"
|
|
14
|
+
assert account.get_config("show_emails") == "2"
|
|
15
|
+
|
|
16
|
+
qr = acfactory.get_account_qr()
|
|
17
|
+
account.add_transport_from_qr(qr)
|
|
18
|
+
assert len(account.list_transports()) == 2
|
|
19
|
+
|
|
20
|
+
account.add_transport_from_qr(qr)
|
|
21
|
+
assert len(account.list_transports()) == 3
|
|
22
|
+
|
|
23
|
+
first_addr = account.list_transports()[0]["addr"]
|
|
24
|
+
second_addr = account.list_transports()[1]["addr"]
|
|
25
|
+
|
|
26
|
+
# Cannot delete the first address.
|
|
27
|
+
with pytest.raises(JsonRpcError):
|
|
28
|
+
account.delete_transport(first_addr)
|
|
29
|
+
|
|
30
|
+
account.delete_transport(second_addr)
|
|
31
|
+
assert len(account.list_transports()) == 2
|
|
32
|
+
|
|
33
|
+
# Enabling mvbox_move or only_fetch_mvbox
|
|
34
|
+
# is not allowed when multi-transport is enabled.
|
|
35
|
+
for option in ["mvbox_move", "only_fetch_mvbox"]:
|
|
36
|
+
with pytest.raises(JsonRpcError):
|
|
37
|
+
account.set_config(option, "1")
|
|
38
|
+
|
|
39
|
+
with pytest.raises(JsonRpcError):
|
|
40
|
+
account.set_config("show_emails", "0")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize("key", ["mvbox_move", "only_fetch_mvbox"])
|
|
44
|
+
def test_no_second_transport_with_mvbox(acfactory, key) -> None:
|
|
45
|
+
"""Test that second transport cannot be configured if mvbox is used."""
|
|
46
|
+
account = acfactory.new_configured_account()
|
|
47
|
+
assert len(account.list_transports()) == 1
|
|
48
|
+
|
|
49
|
+
assert account.get_config("mvbox_move") == "0"
|
|
50
|
+
assert account.get_config("only_fetch_mvbox") == "0"
|
|
51
|
+
|
|
52
|
+
qr = acfactory.get_account_qr()
|
|
53
|
+
account.set_config(key, "1")
|
|
54
|
+
|
|
55
|
+
with pytest.raises(JsonRpcError):
|
|
56
|
+
account.add_transport_from_qr(qr)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_no_second_transport_without_classic_emails(acfactory) -> None:
|
|
60
|
+
"""Test that second transport cannot be configured if classic emails are not fetched."""
|
|
61
|
+
account = acfactory.new_configured_account()
|
|
62
|
+
assert len(account.list_transports()) == 1
|
|
63
|
+
|
|
64
|
+
assert account.get_config("show_emails") == "2"
|
|
65
|
+
|
|
66
|
+
qr = acfactory.get_account_qr()
|
|
67
|
+
account.set_config("show_emails", "0")
|
|
68
|
+
|
|
69
|
+
with pytest.raises(JsonRpcError):
|
|
70
|
+
account.add_transport_from_qr(qr)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_change_address(acfactory) -> None:
|
|
74
|
+
"""Test Alice configuring a second transport and setting it as a primary one."""
|
|
75
|
+
alice, bob = acfactory.get_online_accounts(2)
|
|
76
|
+
|
|
77
|
+
bob_addr = bob.get_config("configured_addr")
|
|
78
|
+
bob.create_chat(alice)
|
|
79
|
+
|
|
80
|
+
alice_chat_bob = alice.create_chat(bob)
|
|
81
|
+
alice_chat_bob.send_text("Hello!")
|
|
82
|
+
|
|
83
|
+
msg1 = bob.wait_for_incoming_msg().get_snapshot()
|
|
84
|
+
sender_addr1 = msg1.sender.get_snapshot().address
|
|
85
|
+
|
|
86
|
+
alice.stop_io()
|
|
87
|
+
old_alice_addr = alice.get_config("configured_addr")
|
|
88
|
+
alice_vcard = alice.self_contact.make_vcard()
|
|
89
|
+
assert old_alice_addr in alice_vcard
|
|
90
|
+
qr = acfactory.get_account_qr()
|
|
91
|
+
alice.add_transport_from_qr(qr)
|
|
92
|
+
new_alice_addr = alice.list_transports()[1]["addr"]
|
|
93
|
+
with pytest.raises(JsonRpcError):
|
|
94
|
+
# Cannot use the address that is not
|
|
95
|
+
# configured for any transport.
|
|
96
|
+
alice.set_config("configured_addr", bob_addr)
|
|
97
|
+
|
|
98
|
+
# Load old address so it is cached.
|
|
99
|
+
assert alice.get_config("configured_addr") == old_alice_addr
|
|
100
|
+
alice.set_config("configured_addr", new_alice_addr)
|
|
101
|
+
# Make sure that setting `configured_addr` invalidated the cache.
|
|
102
|
+
assert alice.get_config("configured_addr") == new_alice_addr
|
|
103
|
+
|
|
104
|
+
alice_vcard = alice.self_contact.make_vcard()
|
|
105
|
+
assert old_alice_addr not in alice_vcard
|
|
106
|
+
assert new_alice_addr in alice_vcard
|
|
107
|
+
with pytest.raises(JsonRpcError):
|
|
108
|
+
alice.delete_transport(new_alice_addr)
|
|
109
|
+
alice.start_io()
|
|
110
|
+
|
|
111
|
+
alice_chat_bob.send_text("Hello again!")
|
|
112
|
+
|
|
113
|
+
msg2 = bob.wait_for_incoming_msg().get_snapshot()
|
|
114
|
+
sender_addr2 = msg2.sender.get_snapshot().address
|
|
115
|
+
|
|
116
|
+
assert msg1.sender == msg2.sender
|
|
117
|
+
assert sender_addr1 != sender_addr2
|
|
118
|
+
assert sender_addr1 == old_alice_addr
|
|
119
|
+
assert sender_addr2 == new_alice_addr
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@pytest.mark.parametrize("is_chatmail", ["0", "1"])
|
|
123
|
+
def test_mvbox_move_first_transport(acfactory, is_chatmail) -> None:
|
|
124
|
+
"""Test that mvbox_move is disabled by default even for non-chatmail accounts.
|
|
125
|
+
Disabling mvbox_move is required to be able to setup a second transport.
|
|
126
|
+
"""
|
|
127
|
+
account = acfactory.get_unconfigured_account()
|
|
128
|
+
|
|
129
|
+
account.set_config("fix_is_chatmail", "1")
|
|
130
|
+
account.set_config("is_chatmail", is_chatmail)
|
|
131
|
+
|
|
132
|
+
# The default value when the setting is unset is "1".
|
|
133
|
+
# This is not changed for compatibility with old databases
|
|
134
|
+
# imported from backups.
|
|
135
|
+
assert account.get_config("mvbox_move") == "1"
|
|
136
|
+
|
|
137
|
+
qr = acfactory.get_account_qr()
|
|
138
|
+
account.add_transport_from_qr(qr)
|
|
139
|
+
|
|
140
|
+
# Once the first transport is set up,
|
|
141
|
+
# mvbox_move is disabled.
|
|
142
|
+
assert account.get_config("mvbox_move") == "0"
|
|
143
|
+
assert account.get_config("is_chatmail") == is_chatmail
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_reconfigure_transport(acfactory) -> None:
|
|
147
|
+
"""Test that reconfiguring the transport works
|
|
148
|
+
even if settings not supported for multi-transport
|
|
149
|
+
like mvbox_move are enabled."""
|
|
150
|
+
account = acfactory.get_online_account()
|
|
151
|
+
account.set_config("mvbox_move", "1")
|
|
152
|
+
|
|
153
|
+
[transport] = account.list_transports()
|
|
154
|
+
account.add_or_update_transport(transport)
|
|
155
|
+
|
|
156
|
+
# Reconfiguring the transport should not reset
|
|
157
|
+
# the settings as if when configuring the first transport.
|
|
158
|
+
assert account.get_config("mvbox_move") == "1"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
from platform import system # noqa
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from deltachat_rpc_client import DeltaChat, Rpc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.skipif("system() == 'Windows'")
|
|
11
|
+
def test_install_venv_and_use_other_core(tmp_path):
|
|
12
|
+
venv = tmp_path.joinpath("venv1")
|
|
13
|
+
subprocess.check_call([sys.executable, "-m", "venv", venv])
|
|
14
|
+
python = venv / "bin" / "python"
|
|
15
|
+
subprocess.check_call([python, "-m", "pip", "install", "deltachat-rpc-server==2.20.0"])
|
|
16
|
+
rpc = Rpc(accounts_dir=tmp_path.joinpath("accounts"), rpc_server_path=venv.joinpath("bin", "deltachat-rpc-server"))
|
|
17
|
+
|
|
18
|
+
with rpc:
|
|
19
|
+
dc = DeltaChat(rpc)
|
|
20
|
+
assert dc.rpc.get_system_info()["deltachat_core_version"] == "v2.20.0"
|
|
@@ -158,29 +158,29 @@ def test_qr_securejoin_broadcast(acfactory, all_devices_online):
|
|
|
158
158
|
chat = get_broadcast(ac)
|
|
159
159
|
chat_msgs = chat.get_messages()
|
|
160
160
|
|
|
161
|
+
encrypted_msg = chat_msgs.pop(0).get_snapshot()
|
|
162
|
+
assert encrypted_msg.text == "Messages are end-to-end encrypted."
|
|
163
|
+
assert encrypted_msg.is_info
|
|
164
|
+
|
|
161
165
|
if please_wait_info_msg:
|
|
162
166
|
first_msg = chat_msgs.pop(0).get_snapshot()
|
|
163
|
-
assert
|
|
167
|
+
assert "invited you to join this channel" in first_msg.text
|
|
164
168
|
assert first_msg.is_info
|
|
165
169
|
|
|
166
|
-
|
|
167
|
-
assert encrypted_msg.text == "Messages are end-to-end encrypted."
|
|
168
|
-
assert encrypted_msg.is_info
|
|
169
|
-
|
|
170
|
-
member_added_msg = chat_msgs[1].get_snapshot()
|
|
170
|
+
member_added_msg = chat_msgs.pop(0).get_snapshot()
|
|
171
171
|
if inviter_side:
|
|
172
172
|
assert member_added_msg.text == f"Member {contact_snapshot.display_name} added."
|
|
173
173
|
else:
|
|
174
174
|
assert member_added_msg.text == "You joined the channel."
|
|
175
175
|
assert member_added_msg.is_info
|
|
176
176
|
|
|
177
|
-
hello_msg = chat_msgs
|
|
177
|
+
hello_msg = chat_msgs.pop(0).get_snapshot()
|
|
178
178
|
assert hello_msg.text == "Hello everyone!"
|
|
179
179
|
assert not hello_msg.is_info
|
|
180
180
|
assert hello_msg.show_padlock
|
|
181
181
|
assert hello_msg.error is None
|
|
182
182
|
|
|
183
|
-
assert len(chat_msgs) ==
|
|
183
|
+
assert len(chat_msgs) == 0
|
|
184
184
|
|
|
185
185
|
chat_snapshot = chat.get_full_snapshot()
|
|
186
186
|
assert chat_snapshot.is_encrypted
|
|
@@ -467,7 +467,7 @@ def test_bot(acfactory) -> None:
|
|
|
467
467
|
|
|
468
468
|
|
|
469
469
|
def test_wait_next_messages(acfactory) -> None:
|
|
470
|
-
alice = acfactory.
|
|
470
|
+
alice = acfactory.get_online_account()
|
|
471
471
|
|
|
472
472
|
# Create a bot account so it does not receive device messages in the beginning.
|
|
473
473
|
addr, password = acfactory.get_credentials()
|
|
@@ -475,6 +475,7 @@ def test_wait_next_messages(acfactory) -> None:
|
|
|
475
475
|
bot.set_config("bot", "1")
|
|
476
476
|
bot.add_or_update_transport({"addr": addr, "password": password})
|
|
477
477
|
assert bot.is_configured()
|
|
478
|
+
bot.bring_online()
|
|
478
479
|
|
|
479
480
|
# There are no old messages and the call returns immediately.
|
|
480
481
|
assert not bot.wait_next_messages()
|
|
@@ -660,8 +661,6 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
|
|
|
660
661
|
contact = alice.create_contact(account)
|
|
661
662
|
alice_group.add_contact(contact)
|
|
662
663
|
|
|
663
|
-
if n_accounts == 2:
|
|
664
|
-
bob_chat_alice = bob.create_chat(alice)
|
|
665
664
|
bob.set_config("download_limit", str(download_limit))
|
|
666
665
|
|
|
667
666
|
alice_group.send_text("hi")
|
|
@@ -677,15 +676,7 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
|
|
|
677
676
|
alice_group.send_file(str(path))
|
|
678
677
|
snapshot = bob.wait_for_incoming_msg().get_snapshot()
|
|
679
678
|
assert snapshot.download_state == DownloadState.AVAILABLE
|
|
680
|
-
|
|
681
|
-
assert snapshot.chat == bob_group
|
|
682
|
-
else:
|
|
683
|
-
# Group contains only Alice and Bob,
|
|
684
|
-
# so partially downloaded messages are
|
|
685
|
-
# hard to distinguish from private replies to group messages.
|
|
686
|
-
#
|
|
687
|
-
# Message may be a private reply, so we assign it to 1:1 chat with Alice.
|
|
688
|
-
assert snapshot.chat == bob_chat_alice
|
|
679
|
+
assert snapshot.chat == bob_group
|
|
689
680
|
|
|
690
681
|
|
|
691
682
|
def test_markseen_contact_request(acfactory):
|
|
@@ -867,15 +858,15 @@ def test_leave_broadcast(acfactory, all_devices_online):
|
|
|
867
858
|
contact_snapshot = contact.get_snapshot()
|
|
868
859
|
chat_msgs = chat.get_messages()
|
|
869
860
|
|
|
870
|
-
if please_wait_info_msg:
|
|
871
|
-
first_msg = chat_msgs.pop(0).get_snapshot()
|
|
872
|
-
assert first_msg.text == "Establishing guaranteed end-to-end encryption, please wait…"
|
|
873
|
-
assert first_msg.is_info
|
|
874
|
-
|
|
875
861
|
encrypted_msg = chat_msgs.pop(0).get_snapshot()
|
|
876
862
|
assert encrypted_msg.text == "Messages are end-to-end encrypted."
|
|
877
863
|
assert encrypted_msg.is_info
|
|
878
864
|
|
|
865
|
+
if please_wait_info_msg:
|
|
866
|
+
first_msg = chat_msgs.pop(0).get_snapshot()
|
|
867
|
+
assert "invited you to join this channel" in first_msg.text
|
|
868
|
+
assert first_msg.is_info
|
|
869
|
+
|
|
879
870
|
member_added_msg = chat_msgs.pop(0).get_snapshot()
|
|
880
871
|
if inviter_side:
|
|
881
872
|
assert member_added_msg.text == f"Member {contact_snapshot.display_name} added."
|
|
@@ -1002,3 +993,22 @@ def test_background_fetch(acfactory, dc):
|
|
|
1002
993
|
snapshot = messages[-1].get_snapshot()
|
|
1003
994
|
if snapshot.text == "Hello again!":
|
|
1004
995
|
break
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
def test_message_exists(acfactory):
|
|
999
|
+
ac1, ac2 = acfactory.get_online_accounts(2)
|
|
1000
|
+
chat = ac1.create_chat(ac2)
|
|
1001
|
+
message1 = chat.send_text("Hello!")
|
|
1002
|
+
message2 = chat.send_text("Hello again!")
|
|
1003
|
+
assert message1.exists()
|
|
1004
|
+
assert message2.exists()
|
|
1005
|
+
|
|
1006
|
+
ac1.delete_messages([message1])
|
|
1007
|
+
assert not message1.exists()
|
|
1008
|
+
assert message2.exists()
|
|
1009
|
+
|
|
1010
|
+
# There is no error when checking if
|
|
1011
|
+
# the message exists for deleted account.
|
|
1012
|
+
ac1.remove()
|
|
1013
|
+
assert not message1.exists()
|
|
1014
|
+
assert not message2.exists()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/__init__.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/_utils.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/chat.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/client.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/const.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/contact.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/deltachat.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/events.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.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
|