deltachat-rpc-client 1.142.8__tar.gz → 2.24.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.
Potentially problematic release.
This version of deltachat-rpc-client might be problematic. Click here for more details.
- {deltachat_rpc_client-1.142.8/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-2.24.0}/PKG-INFO +9 -4
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/README.md +2 -1
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/pyproject.toml +16 -4
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/__init__.py +1 -1
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/_utils.py +6 -5
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/account.py +158 -33
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/chat.py +25 -4
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/client.py +6 -3
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/const.py +64 -26
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/contact.py +8 -5
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/deltachat.py +6 -5
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/events.py +31 -26
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/message.py +48 -1
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/pytestplugin.py +88 -27
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/rpc.py +24 -15
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0/src/deltachat_rpc_client.egg-info}/PKG-INFO +9 -4
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client.egg-info/SOURCES.txt +4 -2
- deltachat_rpc_client-2.24.0/tests/test_account_events.py +30 -0
- deltachat_rpc_client-2.24.0/tests/test_calls.py +109 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/tests/test_chatlist_events.py +6 -11
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/tests/test_iroh_webxdc.py +35 -16
- deltachat_rpc_client-2.24.0/tests/test_key_transfer.py +49 -0
- deltachat_rpc_client-2.24.0/tests/test_multidevice.py +113 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/tests/test_securejoin.py +251 -188
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/tests/test_something.py +453 -79
- deltachat_rpc_client-2.24.0/tests/test_vcard.py +27 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/tests/test_webxdc.py +8 -13
- deltachat_rpc_client-1.142.8/src/deltachat_rpc_client/direct_imap.py +0 -226
- deltachat_rpc_client-1.142.8/src/deltachat_rpc_client.egg-info/requires.txt +0 -1
- deltachat_rpc_client-1.142.8/tests/test_vcard.py +0 -15
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/LICENSE +0 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/setup.cfg +0 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/py.typed +0 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
- {deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: deltachat-rpc-client
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.24.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
|
|
@@ -12,11 +12,15 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
18
|
Classifier: Topic :: Communications :: Chat
|
|
16
19
|
Classifier: Topic :: Communications :: Email
|
|
20
|
+
Requires-Python: >=3.8
|
|
17
21
|
Description-Content-Type: text/markdown
|
|
18
22
|
License-File: LICENSE
|
|
19
|
-
|
|
23
|
+
Dynamic: license-file
|
|
20
24
|
|
|
21
25
|
# Delta Chat RPC python client
|
|
22
26
|
|
|
@@ -45,7 +49,8 @@ $ pip install .
|
|
|
45
49
|
## Testing
|
|
46
50
|
|
|
47
51
|
1. Build `deltachat-rpc-server` with `cargo build -p deltachat-rpc-server`.
|
|
48
|
-
2.
|
|
52
|
+
2. Install tox `pip install -U tox`
|
|
53
|
+
3. Run `CHATMAIL_DOMAIN=nine.testrun.org PATH="../target/debug:$PATH" tox`.
|
|
49
54
|
|
|
50
55
|
Additional arguments to `tox` are passed to pytest, e.g. `tox -- -s` does not capture test output.
|
|
51
56
|
|
|
@@ -25,7 +25,8 @@ $ pip install .
|
|
|
25
25
|
## Testing
|
|
26
26
|
|
|
27
27
|
1. Build `deltachat-rpc-server` with `cargo build -p deltachat-rpc-server`.
|
|
28
|
-
2.
|
|
28
|
+
2. Install tox `pip install -U tox`
|
|
29
|
+
3. Run `CHATMAIL_DOMAIN=nine.testrun.org PATH="../target/debug:$PATH" tox`.
|
|
29
30
|
|
|
30
31
|
Additional arguments to `tox` are passed to pytest, e.g. `tox -- -s` does not capture test output.
|
|
31
32
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deltachat-rpc-client"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.24.0"
|
|
8
8
|
description = "Python client for Delta Chat core JSON-RPC interface"
|
|
9
9
|
classifiers = [
|
|
10
10
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -17,13 +17,14 @@ classifiers = [
|
|
|
17
17
|
"Programming Language :: Python :: 3.9",
|
|
18
18
|
"Programming Language :: Python :: 3.10",
|
|
19
19
|
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
20
23
|
"Topic :: Communications :: Chat",
|
|
21
24
|
"Topic :: Communications :: Email"
|
|
22
25
|
]
|
|
23
26
|
readme = "README.md"
|
|
24
|
-
|
|
25
|
-
"imap-tools",
|
|
26
|
-
]
|
|
27
|
+
requires-python = ">=3.8"
|
|
27
28
|
|
|
28
29
|
[tool.setuptools.package-data]
|
|
29
30
|
deltachat_rpc_client = [
|
|
@@ -66,7 +67,18 @@ lint.select = [
|
|
|
66
67
|
|
|
67
68
|
"RUF006" # asyncio-dangling-task
|
|
68
69
|
]
|
|
70
|
+
lint.ignore = [
|
|
71
|
+
"PLC0415" # `import` should be at the top-level of a file
|
|
72
|
+
]
|
|
69
73
|
line-length = 120
|
|
70
74
|
|
|
71
75
|
[tool.isort]
|
|
72
76
|
profile = "black"
|
|
77
|
+
|
|
78
|
+
[dependency-groups]
|
|
79
|
+
dev = [
|
|
80
|
+
"imap-tools",
|
|
81
|
+
"pytest",
|
|
82
|
+
"pytest-timeout",
|
|
83
|
+
"pytest-xdist",
|
|
84
|
+
]
|
{deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/_utils.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
import os
|
|
2
3
|
import re
|
|
3
4
|
import sys
|
|
4
5
|
from threading import Thread
|
|
@@ -89,8 +90,8 @@ def _run_cli(
|
|
|
89
90
|
help="accounts folder (default: current working directory)",
|
|
90
91
|
nargs="?",
|
|
91
92
|
)
|
|
92
|
-
parser.add_argument("--email", action="store", help="email address")
|
|
93
|
-
parser.add_argument("--password", action="store", help="password")
|
|
93
|
+
parser.add_argument("--email", action="store", help="email address", default=os.getenv("DELTACHAT_EMAIL"))
|
|
94
|
+
parser.add_argument("--password", action="store", help="password", default=os.getenv("DELTACHAT_PASSWORD"))
|
|
94
95
|
args = parser.parse_args(argv[1:])
|
|
95
96
|
|
|
96
97
|
with Rpc(accounts_dir=args.accounts_dir, **kwargs) as rpc:
|
|
@@ -114,7 +115,7 @@ def _run_cli(
|
|
|
114
115
|
|
|
115
116
|
|
|
116
117
|
def extract_addr(text: str) -> str:
|
|
117
|
-
"""
|
|
118
|
+
"""Extract email address from the given text."""
|
|
118
119
|
match = re.match(r".*\((.+@.+)\)", text)
|
|
119
120
|
if match:
|
|
120
121
|
text = match.group(1)
|
|
@@ -123,7 +124,7 @@ def extract_addr(text: str) -> str:
|
|
|
123
124
|
|
|
124
125
|
|
|
125
126
|
def parse_system_image_changed(text: str) -> Optional[Tuple[str, bool]]:
|
|
126
|
-
"""
|
|
127
|
+
"""Return image changed/deleted info from parsing the given system message text."""
|
|
127
128
|
text = text.lower()
|
|
128
129
|
match = re.match(r"group image (changed|deleted) by (.+).", text)
|
|
129
130
|
if match:
|
|
@@ -142,7 +143,7 @@ def parse_system_title_changed(text: str) -> Optional[Tuple[str, str]]:
|
|
|
142
143
|
|
|
143
144
|
|
|
144
145
|
def parse_system_add_remove(text: str) -> Optional[Tuple[str, str, str]]:
|
|
145
|
-
"""
|
|
146
|
+
"""Return add/remove info from parsing the given system message text.
|
|
146
147
|
|
|
147
148
|
returns a (action, affected, actor) tuple.
|
|
148
149
|
"""
|
{deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/account.py
RENAMED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
"""Account module."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
4
|
|
|
5
|
+
import json
|
|
3
6
|
from dataclasses import dataclass
|
|
4
7
|
from typing import TYPE_CHECKING, Optional, Union
|
|
5
8
|
from warnings import warn
|
|
@@ -26,18 +29,36 @@ class Account:
|
|
|
26
29
|
def _rpc(self) -> "Rpc":
|
|
27
30
|
return self.manager.rpc
|
|
28
31
|
|
|
29
|
-
def wait_for_event(self) -> AttrDict:
|
|
32
|
+
def wait_for_event(self, event_type=None) -> AttrDict:
|
|
30
33
|
"""Wait until the next event and return it."""
|
|
31
|
-
|
|
34
|
+
while True:
|
|
35
|
+
next_event = AttrDict(self._rpc.wait_for_event(self.id))
|
|
36
|
+
if event_type is None or next_event.kind == event_type:
|
|
37
|
+
return next_event
|
|
32
38
|
|
|
33
39
|
def clear_all_events(self):
|
|
34
|
-
"""
|
|
40
|
+
"""Remove all queued-up events for a given account.
|
|
41
|
+
|
|
42
|
+
Useful for tests.
|
|
43
|
+
"""
|
|
35
44
|
self._rpc.clear_all_events(self.id)
|
|
36
45
|
|
|
37
46
|
def remove(self) -> None:
|
|
38
47
|
"""Remove the account."""
|
|
39
48
|
self._rpc.remove_account(self.id)
|
|
40
49
|
|
|
50
|
+
def clone(self) -> "Account":
|
|
51
|
+
"""Clone given account.
|
|
52
|
+
|
|
53
|
+
This uses backup-transfer via iroh, i.e. the 'Add second device' feature.
|
|
54
|
+
"""
|
|
55
|
+
future = self._rpc.provide_backup.future(self.id)
|
|
56
|
+
qr = self._rpc.get_backup_qr(self.id)
|
|
57
|
+
new_account = self.manager.add_account()
|
|
58
|
+
new_account._rpc.get_backup(new_account.id, qr)
|
|
59
|
+
future()
|
|
60
|
+
return new_account
|
|
61
|
+
|
|
41
62
|
def start_io(self) -> None:
|
|
42
63
|
"""Start the account I/O."""
|
|
43
64
|
self._rpc.start_io(self.id)
|
|
@@ -67,7 +88,7 @@ class Account:
|
|
|
67
88
|
return self._rpc.get_config(self.id, key)
|
|
68
89
|
|
|
69
90
|
def update_config(self, **kwargs) -> None:
|
|
70
|
-
"""
|
|
91
|
+
"""Update config values."""
|
|
71
92
|
for key, value in kwargs.items():
|
|
72
93
|
self.set_config(key, value)
|
|
73
94
|
|
|
@@ -83,9 +104,15 @@ class Account:
|
|
|
83
104
|
return self.get_config("selfavatar")
|
|
84
105
|
|
|
85
106
|
def check_qr(self, qr):
|
|
107
|
+
"""Parse QR code contents.
|
|
108
|
+
|
|
109
|
+
This function takes the raw text scanned
|
|
110
|
+
and checks what can be done with it.
|
|
111
|
+
"""
|
|
86
112
|
return self._rpc.check_qr(self.id, qr)
|
|
87
113
|
|
|
88
114
|
def set_config_from_qr(self, qr: str):
|
|
115
|
+
"""Set configuration values from a QR code."""
|
|
89
116
|
self._rpc.set_config_from_qr(self.id, qr)
|
|
90
117
|
|
|
91
118
|
@futuremethod
|
|
@@ -93,15 +120,28 @@ class Account:
|
|
|
93
120
|
"""Configure an account."""
|
|
94
121
|
yield self._rpc.configure.future(self.id)
|
|
95
122
|
|
|
123
|
+
@futuremethod
|
|
124
|
+
def add_or_update_transport(self, params):
|
|
125
|
+
"""Add a new transport."""
|
|
126
|
+
yield self._rpc.add_or_update_transport.future(self.id, params)
|
|
127
|
+
|
|
128
|
+
@futuremethod
|
|
129
|
+
def add_transport_from_qr(self, qr: str):
|
|
130
|
+
"""Add a new transport using a QR code."""
|
|
131
|
+
yield self._rpc.add_transport_from_qr.future(self.id, qr)
|
|
132
|
+
|
|
133
|
+
@futuremethod
|
|
134
|
+
def list_transports(self):
|
|
135
|
+
"""Return the list of all email accounts that are used as a transport in the current profile."""
|
|
136
|
+
transports = yield self._rpc.list_transports.future(self.id)
|
|
137
|
+
return transports
|
|
138
|
+
|
|
96
139
|
def bring_online(self):
|
|
97
140
|
"""Start I/O and wait until IMAP becomes IDLE."""
|
|
98
141
|
self.start_io()
|
|
99
|
-
|
|
100
|
-
event = self.wait_for_event()
|
|
101
|
-
if event.kind == EventType.IMAP_INBOX_IDLE:
|
|
102
|
-
break
|
|
142
|
+
self.wait_for_event(EventType.IMAP_INBOX_IDLE)
|
|
103
143
|
|
|
104
|
-
def create_contact(self, obj: Union[int, str, Contact], name: Optional[str] = None) -> Contact:
|
|
144
|
+
def create_contact(self, obj: Union[int, str, Contact, "Account"], name: Optional[str] = None) -> Contact:
|
|
105
145
|
"""Create a new Contact or return an existing one.
|
|
106
146
|
|
|
107
147
|
Calling this method will always result in the same
|
|
@@ -109,26 +149,63 @@ class Account:
|
|
|
109
149
|
with that e-mail address, it is unblocked and its display
|
|
110
150
|
name is updated if specified.
|
|
111
151
|
|
|
112
|
-
:param obj: email-address
|
|
152
|
+
:param obj: email-address, contact id or account.
|
|
113
153
|
:param name: (optional) display name for this contact.
|
|
114
154
|
"""
|
|
155
|
+
if isinstance(obj, Account):
|
|
156
|
+
vcard = obj.self_contact.make_vcard()
|
|
157
|
+
[contact] = self.import_vcard(vcard)
|
|
158
|
+
if name:
|
|
159
|
+
contact.set_name(name)
|
|
160
|
+
return contact
|
|
115
161
|
if isinstance(obj, int):
|
|
116
162
|
obj = Contact(self, obj)
|
|
117
163
|
if isinstance(obj, Contact):
|
|
118
164
|
obj = obj.get_snapshot().address
|
|
119
165
|
return Contact(self, self._rpc.create_contact(self.id, obj, name))
|
|
120
166
|
|
|
167
|
+
def make_vcard(self, contacts: list[Contact]) -> str:
|
|
168
|
+
"""Create vCard with the given contacts."""
|
|
169
|
+
assert all(contact.account == self for contact in contacts)
|
|
170
|
+
contact_ids = [contact.id for contact in contacts]
|
|
171
|
+
return self._rpc.make_vcard(self.id, contact_ids)
|
|
172
|
+
|
|
173
|
+
def import_vcard(self, vcard: str) -> list[Contact]:
|
|
174
|
+
"""Import vCard.
|
|
175
|
+
|
|
176
|
+
Return created or modified contacts in the order they appear in vCard.
|
|
177
|
+
"""
|
|
178
|
+
contact_ids = self._rpc.import_vcard_contents(self.id, vcard)
|
|
179
|
+
return [Contact(self, contact_id) for contact_id in contact_ids]
|
|
180
|
+
|
|
121
181
|
def create_chat(self, account: "Account") -> Chat:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
182
|
+
"""Create a 1:1 chat with another account."""
|
|
183
|
+
return self.create_contact(account).create_chat()
|
|
184
|
+
|
|
185
|
+
def get_device_chat(self) -> Chat:
|
|
186
|
+
"""Return device chat."""
|
|
187
|
+
return self.device_contact.create_chat()
|
|
125
188
|
|
|
126
189
|
def get_contact_by_id(self, contact_id: int) -> Contact:
|
|
127
190
|
"""Return Contact instance for the given contact ID."""
|
|
128
191
|
return Contact(self, contact_id)
|
|
129
192
|
|
|
130
193
|
def get_contact_by_addr(self, address: str) -> Optional[Contact]:
|
|
131
|
-
"""
|
|
194
|
+
"""Looks up a known and unblocked contact with a given e-mail address.
|
|
195
|
+
To get a list of all known and unblocked contacts, use contacts_get_contacts().
|
|
196
|
+
|
|
197
|
+
**POTENTIAL SECURITY ISSUE**: If there are multiple contacts with this address
|
|
198
|
+
(e.g. an address-contact and a key-contact),
|
|
199
|
+
this looks up the most recently seen contact,
|
|
200
|
+
i.e. which contact is returned depends on which contact last sent a message.
|
|
201
|
+
If the user just clicked on a mailto: link, then this is the best thing you can do.
|
|
202
|
+
But **DO NOT** internally represent contacts by their email address
|
|
203
|
+
and do not use this function to look them up;
|
|
204
|
+
otherwise this function will sometimes look up the wrong contact.
|
|
205
|
+
Instead, you should internally represent contacts by their ids.
|
|
206
|
+
|
|
207
|
+
To validate an e-mail address independently of the contact database
|
|
208
|
+
use check_email_validity()."""
|
|
132
209
|
contact_id = self._rpc.lookup_contact_id_by_addr(self.id, address)
|
|
133
210
|
return contact_id and Contact(self, contact_id)
|
|
134
211
|
|
|
@@ -154,8 +231,8 @@ class Account:
|
|
|
154
231
|
def get_contacts(
|
|
155
232
|
self,
|
|
156
233
|
query: Optional[str] = None,
|
|
234
|
+
*,
|
|
157
235
|
with_self: bool = False,
|
|
158
|
-
verified_only: bool = False,
|
|
159
236
|
snapshot: bool = False,
|
|
160
237
|
) -> Union[list[Contact], list[AttrDict]]:
|
|
161
238
|
"""Get a filtered list of contacts.
|
|
@@ -163,12 +240,9 @@ class Account:
|
|
|
163
240
|
:param query: if a string is specified, only return contacts
|
|
164
241
|
whose name or e-mail matches query.
|
|
165
242
|
:param with_self: if True the self-contact is also included if it matches the query.
|
|
166
|
-
:param only_verified: if True only return verified contacts.
|
|
167
243
|
:param snapshot: If True return a list of contact snapshots instead of Contact instances.
|
|
168
244
|
"""
|
|
169
245
|
flags = 0
|
|
170
|
-
if verified_only:
|
|
171
|
-
flags |= ContactFlag.VERIFIED_ONLY
|
|
172
246
|
if with_self:
|
|
173
247
|
flags |= ContactFlag.ADD_SELF
|
|
174
248
|
|
|
@@ -180,9 +254,14 @@ class Account:
|
|
|
180
254
|
|
|
181
255
|
@property
|
|
182
256
|
def self_contact(self) -> Contact:
|
|
183
|
-
"""
|
|
257
|
+
"""Account's identity as a Contact."""
|
|
184
258
|
return Contact(self, SpecialContactId.SELF)
|
|
185
259
|
|
|
260
|
+
@property
|
|
261
|
+
def device_contact(self) -> Chat:
|
|
262
|
+
"""Account's device contact."""
|
|
263
|
+
return Contact(self, SpecialContactId.DEVICE)
|
|
264
|
+
|
|
186
265
|
def get_chatlist(
|
|
187
266
|
self,
|
|
188
267
|
query: Optional[str] = None,
|
|
@@ -226,20 +305,51 @@ class Account:
|
|
|
226
305
|
chats.append(AttrDict(item))
|
|
227
306
|
return chats
|
|
228
307
|
|
|
229
|
-
def create_group(self, name: str
|
|
308
|
+
def create_group(self, name: str) -> Chat:
|
|
230
309
|
"""Create a new group chat.
|
|
231
310
|
|
|
232
|
-
After creation,
|
|
311
|
+
After creation,
|
|
312
|
+
the group has only self-contact as member one member (see `SpecialContactId.SELF`)
|
|
313
|
+
and is in _unpromoted_ state.
|
|
314
|
+
This means, you can add or remove members, change the name,
|
|
315
|
+
the group image and so on without messages being sent to all group members.
|
|
316
|
+
|
|
317
|
+
This changes as soon as the first message is sent to the group members
|
|
318
|
+
and the group becomes _promoted_.
|
|
319
|
+
After that, all changes are synced with all group members
|
|
320
|
+
by sending status message.
|
|
321
|
+
|
|
322
|
+
To check, if a chat is still unpromoted, you can look at the `is_unpromoted` property of a chat
|
|
323
|
+
(see `get_full_snapshot()` / `get_basic_snapshot()`).
|
|
324
|
+
This may be useful if you want to show some help for just created groups.
|
|
233
325
|
"""
|
|
234
|
-
return Chat(self, self._rpc.create_group_chat(self.id, name,
|
|
326
|
+
return Chat(self, self._rpc.create_group_chat(self.id, name, False))
|
|
327
|
+
|
|
328
|
+
def create_broadcast(self, name: str) -> Chat:
|
|
329
|
+
"""Create a new, outgoing **broadcast channel**
|
|
330
|
+
(called "Channel" in the UI).
|
|
331
|
+
|
|
332
|
+
Broadcast channels are similar to groups on the sending device,
|
|
333
|
+
however, recipients get the messages in a read-only chat
|
|
334
|
+
and will not see who the other members are.
|
|
335
|
+
|
|
336
|
+
Called `broadcast` here rather than `channel`,
|
|
337
|
+
because the word "channel" already appears a lot in the code,
|
|
338
|
+
which would make it hard to grep for it.
|
|
339
|
+
|
|
340
|
+
After creation, the chat contains no recipients and is in _unpromoted_ state;
|
|
341
|
+
see `create_group()` for more information on the unpromoted state.
|
|
342
|
+
|
|
343
|
+
Returns the created chat.
|
|
344
|
+
"""
|
|
345
|
+
return Chat(self, self._rpc.create_broadcast(self.id, name))
|
|
235
346
|
|
|
236
347
|
def get_chat_by_id(self, chat_id: int) -> Chat:
|
|
237
348
|
"""Return the Chat instance with the given ID."""
|
|
238
349
|
return Chat(self, chat_id)
|
|
239
350
|
|
|
240
351
|
def secure_join(self, qrdata: str) -> Chat:
|
|
241
|
-
"""Continue a Setup-Contact or Verified-Group-Invite protocol started on
|
|
242
|
-
another device.
|
|
352
|
+
"""Continue a Setup-Contact or Verified-Group-Invite protocol started on another device.
|
|
243
353
|
|
|
244
354
|
The function returns immediately and the handshake runs in background, sending
|
|
245
355
|
and receiving several messages.
|
|
@@ -296,34 +406,40 @@ class Account:
|
|
|
296
406
|
|
|
297
407
|
def wait_for_incoming_msg_event(self):
|
|
298
408
|
"""Wait for incoming message event and return it."""
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
409
|
+
return self.wait_for_event(EventType.INCOMING_MSG)
|
|
410
|
+
|
|
411
|
+
def wait_for_msgs_changed_event(self):
|
|
412
|
+
"""Wait for messages changed event and return it."""
|
|
413
|
+
return self.wait_for_event(EventType.MSGS_CHANGED)
|
|
414
|
+
|
|
415
|
+
def wait_for_msgs_noticed_event(self):
|
|
416
|
+
"""Wait for messages noticed event and return it."""
|
|
417
|
+
return self.wait_for_event(EventType.MSGS_NOTICED)
|
|
303
418
|
|
|
304
419
|
def wait_for_incoming_msg(self):
|
|
305
420
|
"""Wait for incoming message and return it.
|
|
306
421
|
|
|
307
|
-
Consumes all events before the next incoming message event.
|
|
422
|
+
Consumes all events before the next incoming message event.
|
|
423
|
+
"""
|
|
308
424
|
return self.get_message_by_id(self.wait_for_incoming_msg_event().msg_id)
|
|
309
425
|
|
|
310
426
|
def wait_for_securejoin_inviter_success(self):
|
|
427
|
+
"""Wait until SecureJoin process finishes successfully on the inviter side."""
|
|
311
428
|
while True:
|
|
312
429
|
event = self.wait_for_event()
|
|
313
430
|
if event["kind"] == "SecurejoinInviterProgress" and event["progress"] == 1000:
|
|
314
431
|
break
|
|
315
432
|
|
|
316
433
|
def wait_for_securejoin_joiner_success(self):
|
|
434
|
+
"""Wait until SecureJoin process finishes successfully on the joiner side."""
|
|
317
435
|
while True:
|
|
318
436
|
event = self.wait_for_event()
|
|
319
437
|
if event["kind"] == "SecurejoinJoinerProgress" and event["progress"] == 1000:
|
|
320
438
|
break
|
|
321
439
|
|
|
322
440
|
def wait_for_reactions_changed(self):
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if event.kind == EventType.REACTIONS_CHANGED:
|
|
326
|
-
return event
|
|
441
|
+
"""Wait for reaction change event."""
|
|
442
|
+
return self.wait_for_event(EventType.REACTIONS_CHANGED)
|
|
327
443
|
|
|
328
444
|
def get_fresh_messages_in_arrival_order(self) -> list[Message]:
|
|
329
445
|
"""Return fresh messages list sorted in the order of their arrival, with ascending IDs."""
|
|
@@ -352,3 +468,12 @@ class Account:
|
|
|
352
468
|
"""Import keys."""
|
|
353
469
|
passphrase = "" # Importing passphrase-protected keys is currently not supported.
|
|
354
470
|
self._rpc.import_self_keys(self.id, str(path), passphrase)
|
|
471
|
+
|
|
472
|
+
def initiate_autocrypt_key_transfer(self) -> None:
|
|
473
|
+
"""Send Autocrypt Setup Message."""
|
|
474
|
+
return self._rpc.initiate_autocrypt_key_transfer(self.id)
|
|
475
|
+
|
|
476
|
+
def ice_servers(self) -> list:
|
|
477
|
+
"""Return ICE servers for WebRTC configuration."""
|
|
478
|
+
ice_servers_json = self._rpc.ice_servers(self.id)
|
|
479
|
+
return json.loads(ice_servers_json)
|
{deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/chat.py
RENAMED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Chat module."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
4
|
|
|
3
5
|
import calendar
|
|
@@ -89,7 +91,8 @@ class Chat:
|
|
|
89
91
|
def set_ephemeral_timer(self, timer: int) -> None:
|
|
90
92
|
"""Set ephemeral timer of this chat in seconds.
|
|
91
93
|
|
|
92
|
-
0 means the timer is disabled, use 1 for immediate deletion.
|
|
94
|
+
0 means the timer is disabled, use 1 for immediate deletion.
|
|
95
|
+
"""
|
|
93
96
|
self._rpc.set_chat_ephemeral_timer(self.account.id, self.id, timer)
|
|
94
97
|
|
|
95
98
|
def get_encryption_info(self) -> str:
|
|
@@ -124,6 +127,7 @@ class Chat:
|
|
|
124
127
|
html: Optional[str] = None,
|
|
125
128
|
viewtype: Optional[ViewType] = None,
|
|
126
129
|
file: Optional[str] = None,
|
|
130
|
+
filename: Optional[str] = None,
|
|
127
131
|
location: Optional[tuple[float, float]] = None,
|
|
128
132
|
override_sender_name: Optional[str] = None,
|
|
129
133
|
quoted_msg: Optional[Union[int, Message]] = None,
|
|
@@ -137,6 +141,7 @@ class Chat:
|
|
|
137
141
|
"html": html,
|
|
138
142
|
"viewtype": viewtype,
|
|
139
143
|
"file": file,
|
|
144
|
+
"filename": filename,
|
|
140
145
|
"location": location,
|
|
141
146
|
"overrideSenderName": override_sender_name,
|
|
142
147
|
"quotedMessageId": quoted_msg,
|
|
@@ -163,6 +168,11 @@ class Chat:
|
|
|
163
168
|
msg_id = self._rpc.send_sticker(self.account.id, self.id, path)
|
|
164
169
|
return Message(self.account, msg_id)
|
|
165
170
|
|
|
171
|
+
def resend_messages(self, messages: list[Message]) -> None:
|
|
172
|
+
"""Resend a list of messages to this chat."""
|
|
173
|
+
msg_ids = [msg.id for msg in messages]
|
|
174
|
+
self._rpc.resend_messages(self.account.id, msg_ids)
|
|
175
|
+
|
|
166
176
|
def forward_messages(self, messages: list[Message]) -> None:
|
|
167
177
|
"""Forward a list of messages to this chat."""
|
|
168
178
|
msg_ids = [msg.id for msg in messages]
|
|
@@ -172,13 +182,14 @@ class Chat:
|
|
|
172
182
|
self,
|
|
173
183
|
text: Optional[str] = None,
|
|
174
184
|
file: Optional[str] = None,
|
|
185
|
+
filename: Optional[str] = None,
|
|
175
186
|
quoted_msg: Optional[int] = None,
|
|
176
187
|
viewtype: Optional[str] = None,
|
|
177
188
|
) -> None:
|
|
178
189
|
"""Set draft message."""
|
|
179
190
|
if isinstance(quoted_msg, Message):
|
|
180
191
|
quoted_msg = quoted_msg.id
|
|
181
|
-
self._rpc.misc_set_draft(self.account.id, self.id, text, file, quoted_msg, viewtype)
|
|
192
|
+
self._rpc.misc_set_draft(self.account.id, self.id, text, file, filename, quoted_msg, viewtype)
|
|
182
193
|
|
|
183
194
|
def remove_draft(self) -> None:
|
|
184
195
|
"""Remove draft message."""
|
|
@@ -196,12 +207,12 @@ class Chat:
|
|
|
196
207
|
return snapshot
|
|
197
208
|
|
|
198
209
|
def get_messages(self, info_only: bool = False, add_daymarker: bool = False) -> list[Message]:
|
|
199
|
-
"""
|
|
210
|
+
"""Get the list of messages in this chat."""
|
|
200
211
|
msgs = self._rpc.get_message_ids(self.account.id, self.id, info_only, add_daymarker)
|
|
201
212
|
return [Message(self.account, msg_id) for msg_id in msgs]
|
|
202
213
|
|
|
203
214
|
def get_fresh_message_count(self) -> int:
|
|
204
|
-
"""Get number of fresh messages in this chat"""
|
|
215
|
+
"""Get number of fresh messages in this chat."""
|
|
205
216
|
return self._rpc.get_fresh_msg_cnt(self.account.id, self.id)
|
|
206
217
|
|
|
207
218
|
def mark_noticed(self) -> None:
|
|
@@ -238,6 +249,11 @@ class Chat:
|
|
|
238
249
|
contacts = self._rpc.get_chat_contacts(self.account.id, self.id)
|
|
239
250
|
return [Contact(self.account, contact_id) for contact_id in contacts]
|
|
240
251
|
|
|
252
|
+
def get_past_contacts(self) -> list[Contact]:
|
|
253
|
+
"""Get past contacts for this chat."""
|
|
254
|
+
past_contacts = self._rpc.get_past_chat_contacts(self.account.id, self.id)
|
|
255
|
+
return [Contact(self.account, contact_id) for contact_id in past_contacts]
|
|
256
|
+
|
|
241
257
|
def set_image(self, path: str) -> None:
|
|
242
258
|
"""Set profile image of this chat.
|
|
243
259
|
|
|
@@ -278,3 +294,8 @@ class Chat:
|
|
|
278
294
|
f.write(vcard.encode())
|
|
279
295
|
f.flush()
|
|
280
296
|
self._rpc.send_msg(self.account.id, self.id, {"viewtype": ViewType.VCARD, "file": f.name})
|
|
297
|
+
|
|
298
|
+
def place_outgoing_call(self, place_call_info: str) -> Message:
|
|
299
|
+
"""Starts an outgoing call."""
|
|
300
|
+
msg_id = self._rpc.place_outgoing_call(self.account.id, self.id, place_call_info)
|
|
301
|
+
return Message(self.account, msg_id)
|
{deltachat_rpc_client-1.142.8 → deltachat_rpc_client-2.24.0}/src/deltachat_rpc_client/client.py
RENAMED
|
@@ -48,6 +48,7 @@ class Client:
|
|
|
48
48
|
self.add_hooks(hooks or [])
|
|
49
49
|
|
|
50
50
|
def add_hooks(self, hooks: Iterable[tuple[Callable, Union[type, EventFilter]]]) -> None:
|
|
51
|
+
"""Register multiple hooks."""
|
|
51
52
|
for hook, event in hooks:
|
|
52
53
|
self.add_hook(hook, event)
|
|
53
54
|
|
|
@@ -77,14 +78,15 @@ class Client:
|
|
|
77
78
|
self._hooks.get(type(event), set()).remove((hook, event))
|
|
78
79
|
|
|
79
80
|
def is_configured(self) -> bool:
|
|
81
|
+
"""Return True if the client is configured."""
|
|
80
82
|
return self.account.is_configured()
|
|
81
83
|
|
|
82
84
|
def configure(self, email: str, password: str, **kwargs) -> None:
|
|
83
|
-
|
|
84
|
-
self.account.set_config("mail_pw", password)
|
|
85
|
+
"""Configure the client."""
|
|
85
86
|
for key, value in kwargs.items():
|
|
86
87
|
self.account.set_config(key, value)
|
|
87
|
-
|
|
88
|
+
params = {"addr": email, "password": password}
|
|
89
|
+
self.account.add_or_update_transport(params)
|
|
88
90
|
self.logger.debug("Account configured")
|
|
89
91
|
|
|
90
92
|
def run_forever(self) -> None:
|
|
@@ -198,5 +200,6 @@ class Bot(Client):
|
|
|
198
200
|
"""Simple bot implementation that listens to events of a single account."""
|
|
199
201
|
|
|
200
202
|
def configure(self, email: str, password: str, **kwargs) -> None:
|
|
203
|
+
"""Configure the bot."""
|
|
201
204
|
kwargs.setdefault("bot", "1")
|
|
202
205
|
super().configure(email, password, **kwargs)
|