deltachat-rpc-client 1.159.3__py3-none-any.whl → 1.159.4__py3-none-any.whl

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.
@@ -1,4 +1,4 @@
1
- """Delta Chat JSON-RPC high-level API"""
1
+ """Delta Chat JSON-RPC high-level API."""
2
2
 
3
3
  from ._utils import AttrDict, run_bot_cli, run_client_cli
4
4
  from .account import Account
@@ -115,7 +115,7 @@ def _run_cli(
115
115
 
116
116
 
117
117
  def extract_addr(text: str) -> str:
118
- """extract email address from the given text."""
118
+ """Extract email address from the given text."""
119
119
  match = re.match(r".*\((.+@.+)\)", text)
120
120
  if match:
121
121
  text = match.group(1)
@@ -124,7 +124,7 @@ def extract_addr(text: str) -> str:
124
124
 
125
125
 
126
126
  def parse_system_image_changed(text: str) -> Optional[Tuple[str, bool]]:
127
- """return image changed/deleted info from parsing the given system message text."""
127
+ """Return image changed/deleted info from parsing the given system message text."""
128
128
  text = text.lower()
129
129
  match = re.match(r"group image (changed|deleted) by (.+).", text)
130
130
  if match:
@@ -143,7 +143,7 @@ def parse_system_title_changed(text: str) -> Optional[Tuple[str, str]]:
143
143
 
144
144
 
145
145
  def parse_system_add_remove(text: str) -> Optional[Tuple[str, str, str]]:
146
- """return add/remove info from parsing the given system message text.
146
+ """Return add/remove info from parsing the given system message text.
147
147
 
148
148
  returns a (action, affected, actor) tuple.
149
149
  """
@@ -1,3 +1,5 @@
1
+ """Account module."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  from dataclasses import dataclass
@@ -34,7 +36,10 @@ class Account:
34
36
  return next_event
35
37
 
36
38
  def clear_all_events(self):
37
- """Removes all queued-up events for a given account. Useful for tests."""
39
+ """Remove all queued-up events for a given account.
40
+
41
+ Useful for tests.
42
+ """
38
43
  self._rpc.clear_all_events(self.id)
39
44
 
40
45
  def remove(self) -> None:
@@ -43,7 +48,9 @@ class Account:
43
48
 
44
49
  def clone(self) -> "Account":
45
50
  """Clone given account.
46
- This uses backup-transfer via iroh, i.e. the 'Add second device' feature."""
51
+
52
+ This uses backup-transfer via iroh, i.e. the 'Add second device' feature.
53
+ """
47
54
  future = self._rpc.provide_backup.future(self.id)
48
55
  qr = self._rpc.get_backup_qr(self.id)
49
56
  new_account = self.manager.add_account()
@@ -80,7 +87,7 @@ class Account:
80
87
  return self._rpc.get_config(self.id, key)
81
88
 
82
89
  def update_config(self, **kwargs) -> None:
83
- """update config values."""
90
+ """Update config values."""
84
91
  for key, value in kwargs.items():
85
92
  self.set_config(key, value)
86
93
 
@@ -99,10 +106,12 @@ class Account:
99
106
  """Parse QR code contents.
100
107
 
101
108
  This function takes the raw text scanned
102
- and checks what can be done with it."""
109
+ and checks what can be done with it.
110
+ """
103
111
  return self._rpc.check_qr(self.id, qr)
104
112
 
105
113
  def set_config_from_qr(self, qr: str):
114
+ """Set configuration values from a QR code."""
106
115
  self._rpc.set_config_from_qr(self.id, qr)
107
116
 
108
117
  @futuremethod
@@ -117,7 +126,7 @@ class Account:
117
126
 
118
127
  @futuremethod
119
128
  def list_transports(self):
120
- """Returns the list of all email accounts that are used as a transport in the current profile."""
129
+ """Return the list of all email accounts that are used as a transport in the current profile."""
121
130
  transports = yield self._rpc.list_transports.future(self.id)
122
131
  return transports
123
132
 
@@ -158,7 +167,8 @@ class Account:
158
167
  def import_vcard(self, vcard: str) -> list[Contact]:
159
168
  """Import vCard.
160
169
 
161
- Return created or modified contacts in the order they appear in vCard."""
170
+ Return created or modified contacts in the order they appear in vCard.
171
+ """
162
172
  contact_ids = self._rpc.import_vcard_contents(self.id, vcard)
163
173
  return [Contact(self, contact_id) for contact_id in contact_ids]
164
174
 
@@ -227,12 +237,12 @@ class Account:
227
237
 
228
238
  @property
229
239
  def self_contact(self) -> Contact:
230
- """This account's identity as a Contact."""
240
+ """Account's identity as a Contact."""
231
241
  return Contact(self, SpecialContactId.SELF)
232
242
 
233
243
  @property
234
244
  def device_contact(self) -> Chat:
235
- """This account's device contact."""
245
+ """Account's device contact."""
236
246
  return Contact(self, SpecialContactId.DEVICE)
237
247
 
238
248
  def get_chatlist(
@@ -290,8 +300,7 @@ class Account:
290
300
  return Chat(self, chat_id)
291
301
 
292
302
  def secure_join(self, qrdata: str) -> Chat:
293
- """Continue a Setup-Contact or Verified-Group-Invite protocol started on
294
- another device.
303
+ """Continue a Setup-Contact or Verified-Group-Invite protocol started on another device.
295
304
 
296
305
  The function returns immediately and the handshake runs in background, sending
297
306
  and receiving several messages.
@@ -361,22 +370,26 @@ class Account:
361
370
  def wait_for_incoming_msg(self):
362
371
  """Wait for incoming message and return it.
363
372
 
364
- Consumes all events before the next incoming message event."""
373
+ Consumes all events before the next incoming message event.
374
+ """
365
375
  return self.get_message_by_id(self.wait_for_incoming_msg_event().msg_id)
366
376
 
367
377
  def wait_for_securejoin_inviter_success(self):
378
+ """Wait until SecureJoin process finishes successfully on the inviter side."""
368
379
  while True:
369
380
  event = self.wait_for_event()
370
381
  if event["kind"] == "SecurejoinInviterProgress" and event["progress"] == 1000:
371
382
  break
372
383
 
373
384
  def wait_for_securejoin_joiner_success(self):
385
+ """Wait until SecureJoin process finishes successfully on the joiner side."""
374
386
  while True:
375
387
  event = self.wait_for_event()
376
388
  if event["kind"] == "SecurejoinJoinerProgress" and event["progress"] == 1000:
377
389
  break
378
390
 
379
391
  def wait_for_reactions_changed(self):
392
+ """Wait for reaction change event."""
380
393
  return self.wait_for_event(EventType.REACTIONS_CHANGED)
381
394
 
382
395
  def get_fresh_messages_in_arrival_order(self) -> list[Message]:
@@ -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:
@@ -199,12 +202,12 @@ class Chat:
199
202
  return snapshot
200
203
 
201
204
  def get_messages(self, info_only: bool = False, add_daymarker: bool = False) -> list[Message]:
202
- """get the list of messages in this chat."""
205
+ """Get the list of messages in this chat."""
203
206
  msgs = self._rpc.get_message_ids(self.account.id, self.id, info_only, add_daymarker)
204
207
  return [Message(self.account, msg_id) for msg_id in msgs]
205
208
 
206
209
  def get_fresh_message_count(self) -> int:
207
- """Get number of fresh messages in this chat"""
210
+ """Get number of fresh messages in this chat."""
208
211
  return self._rpc.get_fresh_msg_cnt(self.account.id, self.id)
209
212
 
210
213
  def mark_noticed(self) -> None:
@@ -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,9 +78,11 @@ 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:
85
+ """Configure the client."""
83
86
  self.account.set_config("addr", email)
84
87
  self.account.set_config("mail_pw", password)
85
88
  for key, value in kwargs.items():
@@ -198,5 +201,6 @@ class Bot(Client):
198
201
  """Simple bot implementation that listens to events of a single account."""
199
202
 
200
203
  def configure(self, email: str, password: str, **kwargs) -> None:
204
+ """Configure the bot."""
201
205
  kwargs.setdefault("bot", "1")
202
206
  super().configure(email, password, **kwargs)
@@ -1,14 +1,20 @@
1
+ """Constants module."""
2
+
1
3
  from enum import Enum, IntEnum
2
4
 
3
5
  COMMAND_PREFIX = "/"
4
6
 
5
7
 
6
8
  class ContactFlag(IntEnum):
9
+ """Bit flags for get_contacts() method."""
10
+
7
11
  VERIFIED_ONLY = 0x01
8
12
  ADD_SELF = 0x02
9
13
 
10
14
 
11
15
  class ChatlistFlag(IntEnum):
16
+ """Bit flags for get_chatlist() method."""
17
+
12
18
  ARCHIVED_ONLY = 0x01
13
19
  NO_SPECIALS = 0x02
14
20
  ADD_ALLDONE_HINT = 0x04
@@ -16,6 +22,8 @@ class ChatlistFlag(IntEnum):
16
22
 
17
23
 
18
24
  class SpecialContactId(IntEnum):
25
+ """Special contact IDs."""
26
+
19
27
  SELF = 1
20
28
  INFO = 2 # centered messages as "member added", used in all chats
21
29
  DEVICE = 5 # messages "update info" in the device-chat
@@ -23,7 +31,7 @@ class SpecialContactId(IntEnum):
23
31
 
24
32
 
25
33
  class EventType(str, Enum):
26
- """Core event types"""
34
+ """Core event types."""
27
35
 
28
36
  INFO = "Info"
29
37
  SMTP_CONNECTED = "SmtpConnected"
@@ -71,7 +79,7 @@ class EventType(str, Enum):
71
79
 
72
80
 
73
81
  class ChatId(IntEnum):
74
- """Special chat ids"""
82
+ """Special chat IDs."""
75
83
 
76
84
  TRASH = 3
77
85
  ARCHIVED_LINK = 6
@@ -80,7 +88,7 @@ class ChatId(IntEnum):
80
88
 
81
89
 
82
90
  class ChatType(IntEnum):
83
- """Chat types"""
91
+ """Chat type."""
84
92
 
85
93
  UNDEFINED = 0
86
94
  SINGLE = 100
@@ -90,7 +98,7 @@ class ChatType(IntEnum):
90
98
 
91
99
 
92
100
  class ChatVisibility(str, Enum):
93
- """Chat visibility types"""
101
+ """Chat visibility types."""
94
102
 
95
103
  NORMAL = "Normal"
96
104
  ARCHIVED = "Archived"
@@ -98,7 +106,7 @@ class ChatVisibility(str, Enum):
98
106
 
99
107
 
100
108
  class DownloadState(str, Enum):
101
- """Message download state"""
109
+ """Message download state."""
102
110
 
103
111
  DONE = "Done"
104
112
  AVAILABLE = "Available"
@@ -159,14 +167,14 @@ class MessageState(IntEnum):
159
167
 
160
168
 
161
169
  class MessageId(IntEnum):
162
- """Special message ids"""
170
+ """Special message IDs."""
163
171
 
164
172
  DAYMARKER = 9
165
173
  LAST_SPECIAL = 9
166
174
 
167
175
 
168
176
  class CertificateChecks(IntEnum):
169
- """Certificate checks mode"""
177
+ """Certificate checks mode."""
170
178
 
171
179
  AUTOMATIC = 0
172
180
  STRICT = 1
@@ -174,7 +182,7 @@ class CertificateChecks(IntEnum):
174
182
 
175
183
 
176
184
  class Connectivity(IntEnum):
177
- """Connectivity states"""
185
+ """Connectivity states."""
178
186
 
179
187
  NOT_CONNECTED = 1000
180
188
  CONNECTING = 2000
@@ -183,7 +191,7 @@ class Connectivity(IntEnum):
183
191
 
184
192
 
185
193
  class KeyGenType(IntEnum):
186
- """Type of the key to generate"""
194
+ """Type of the key to generate."""
187
195
 
188
196
  DEFAULT = 0
189
197
  RSA2048 = 1
@@ -193,21 +201,21 @@ class KeyGenType(IntEnum):
193
201
 
194
202
  # "Lp" means "login parameters"
195
203
  class LpAuthFlag(IntEnum):
196
- """Authorization flags"""
204
+ """Authorization flags."""
197
205
 
198
206
  OAUTH2 = 0x2
199
207
  NORMAL = 0x4
200
208
 
201
209
 
202
210
  class MediaQuality(IntEnum):
203
- """Media quality setting"""
211
+ """Media quality setting."""
204
212
 
205
213
  BALANCED = 0
206
214
  WORSE = 1
207
215
 
208
216
 
209
217
  class ProviderStatus(IntEnum):
210
- """Provider status according to manual testing"""
218
+ """Provider status according to manual testing."""
211
219
 
212
220
  OK = 1
213
221
  PREPARATION = 2
@@ -215,7 +223,7 @@ class ProviderStatus(IntEnum):
215
223
 
216
224
 
217
225
  class PushNotifyState(IntEnum):
218
- """Push notifications state"""
226
+ """Push notifications state."""
219
227
 
220
228
  NOT_CONNECTED = 0
221
229
  HEARTBEAT = 1
@@ -223,7 +231,7 @@ class PushNotifyState(IntEnum):
223
231
 
224
232
 
225
233
  class ShowEmails(IntEnum):
226
- """Show emails mode"""
234
+ """Show emails mode."""
227
235
 
228
236
  OFF = 0
229
237
  ACCEPTED_CONTACTS = 1
@@ -231,7 +239,7 @@ class ShowEmails(IntEnum):
231
239
 
232
240
 
233
241
  class SocketSecurity(IntEnum):
234
- """Socket security"""
242
+ """Socket security."""
235
243
 
236
244
  AUTOMATIC = 0
237
245
  SSL = 1
@@ -240,7 +248,7 @@ class SocketSecurity(IntEnum):
240
248
 
241
249
 
242
250
  class VideochatType(IntEnum):
243
- """Video chat URL type"""
251
+ """Video chat URL type."""
244
252
 
245
253
  UNKNOWN = 0
246
254
  BASICWEBRTC = 1
@@ -1,3 +1,5 @@
1
+ """Contact module."""
2
+
1
3
  from dataclasses import dataclass
2
4
  from typing import TYPE_CHECKING
3
5
 
@@ -11,8 +13,7 @@ if TYPE_CHECKING:
11
13
 
12
14
  @dataclass
13
15
  class Contact:
14
- """
15
- Contact API.
16
+ """Contact API.
16
17
 
17
18
  Essentially a wrapper for RPC, account ID and a contact ID.
18
19
  """
@@ -45,8 +46,9 @@ class Contact:
45
46
  self._rpc.change_contact_name(self.account.id, self.id, name)
46
47
 
47
48
  def get_encryption_info(self) -> str:
48
- """Get a multi-line encryption info, containing your fingerprint and
49
- the fingerprint of the contact.
49
+ """Get a multi-line encryption info.
50
+
51
+ Encryption info contains your fingerprint and the fingerprint of the contact.
50
52
  """
51
53
  return self._rpc.get_contact_encryption_info(self.account.id, self.id)
52
54
 
@@ -66,4 +68,5 @@ class Contact:
66
68
  )
67
69
 
68
70
  def make_vcard(self) -> str:
71
+ """Make a vCard for the contact."""
69
72
  return self.account.make_vcard([self])
@@ -1,3 +1,5 @@
1
+ """Account manager module."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  from typing import TYPE_CHECKING
@@ -10,12 +12,13 @@ if TYPE_CHECKING:
10
12
 
11
13
 
12
14
  class DeltaChat:
13
- """
14
- Delta Chat accounts manager.
15
+ """Delta Chat accounts manager.
16
+
15
17
  This is the root of the object oriented API.
16
18
  """
17
19
 
18
20
  def __init__(self, rpc: "Rpc") -> None:
21
+ """Initialize account manager."""
19
22
  self.rpc = rpc
20
23
 
21
24
  def add_account(self) -> Account:
@@ -37,9 +40,7 @@ class DeltaChat:
37
40
  self.rpc.stop_io_for_all_accounts()
38
41
 
39
42
  def maybe_network(self) -> None:
40
- """Indicate that the network likely has come back or just that the network
41
- conditions might have changed.
42
- """
43
+ """Indicate that the network conditions might have changed."""
43
44
  self.rpc.maybe_network()
44
45
 
45
46
  def get_system_info(self) -> AttrDict:
@@ -36,7 +36,7 @@ class EventFilter(ABC):
36
36
 
37
37
  @abstractmethod
38
38
  def __hash__(self) -> int:
39
- """Object's unique hash"""
39
+ """Object's unique hash."""
40
40
 
41
41
  @abstractmethod
42
42
  def __eq__(self, other) -> bool:
@@ -52,9 +52,7 @@ class EventFilter(ABC):
52
52
 
53
53
  @abstractmethod
54
54
  def filter(self, event):
55
- """Return True-like value if the event passed the filter and should be
56
- used, or False-like value otherwise.
57
- """
55
+ """Return True-like value if the event passed the filter."""
58
56
 
59
57
 
60
58
  class RawEvent(EventFilter):
@@ -82,31 +80,17 @@ class RawEvent(EventFilter):
82
80
  return False
83
81
 
84
82
  def filter(self, event: "AttrDict") -> bool:
83
+ """Filter an event.
84
+
85
+ Return true if the event should be processed.
86
+ """
85
87
  if self.types and event.kind not in self.types:
86
88
  return False
87
89
  return self._call_func(event)
88
90
 
89
91
 
90
92
  class NewMessage(EventFilter):
91
- """Matches whenever a new message arrives.
92
-
93
- Warning: registering a handler for this event will cause the messages
94
- to be marked as read. Its usage is mainly intended for bots.
95
-
96
- :param pattern: if set, this Pattern will be used to filter the message by its text
97
- content.
98
- :param command: If set, only match messages with the given command (ex. /help).
99
- Setting this property implies `is_info==False`.
100
- :param is_bot: If set to True only match messages sent by bots, if set to None
101
- match messages from bots and users. If omitted or set to False
102
- only messages from users will be matched.
103
- :param is_info: If set to True only match info/system messages, if set to False
104
- only match messages that are not info/system messages. If omitted
105
- info/system messages as well as normal messages will be matched.
106
- :param func: A Callable function that should accept the event as input
107
- parameter, and return a bool value indicating whether the event
108
- should be dispatched or not.
109
- """
93
+ """Matches whenever a new message arrives."""
110
94
 
111
95
  def __init__(
112
96
  self,
@@ -121,6 +105,25 @@ class NewMessage(EventFilter):
121
105
  is_info: Optional[bool] = None,
122
106
  func: Optional[Callable[["AttrDict"], bool]] = None,
123
107
  ) -> None:
108
+ """Initialize a new message filter.
109
+
110
+ Warning: registering a handler for this event will cause the messages
111
+ to be marked as read. Its usage is mainly intended for bots.
112
+
113
+ :param pattern: if set, this Pattern will be used to filter the message by its text
114
+ content.
115
+ :param command: If set, only match messages with the given command (ex. /help).
116
+ Setting this property implies `is_info==False`.
117
+ :param is_bot: If set to True only match messages sent by bots, if set to None
118
+ match messages from bots and users. If omitted or set to False
119
+ only messages from users will be matched.
120
+ :param is_info: If set to True only match info/system messages, if set to False
121
+ only match messages that are not info/system messages. If omitted
122
+ info/system messages as well as normal messages will be matched.
123
+ :param func: A Callable function that should accept the event as input
124
+ parameter, and return a bool value indicating whether the event
125
+ should be dispatched or not.
126
+ """
124
127
  super().__init__(func=func)
125
128
  self.is_bot = is_bot
126
129
  self.is_info = is_info
@@ -159,6 +162,7 @@ class NewMessage(EventFilter):
159
162
  return False
160
163
 
161
164
  def filter(self, event: "AttrDict") -> bool:
165
+ """Return true if if the event is a new message event."""
162
166
  if self.is_bot is not None and self.is_bot != event.message_snapshot.is_bot:
163
167
  return False
164
168
  if self.is_info is not None and self.is_info != event.message_snapshot.is_info:
@@ -199,6 +203,7 @@ class MemberListChanged(EventFilter):
199
203
  return False
200
204
 
201
205
  def filter(self, event: "AttrDict") -> bool:
206
+ """Return true if if the event is a member addition event."""
202
207
  if self.added is not None and self.added != event.member_added:
203
208
  return False
204
209
  return self._call_func(event)
@@ -231,6 +236,7 @@ class GroupImageChanged(EventFilter):
231
236
  return False
232
237
 
233
238
  def filter(self, event: "AttrDict") -> bool:
239
+ """Return True if event is matched."""
234
240
  if self.deleted is not None and self.deleted != event.image_deleted:
235
241
  return False
236
242
  return self._call_func(event)
@@ -256,13 +262,12 @@ class GroupNameChanged(EventFilter):
256
262
  return False
257
263
 
258
264
  def filter(self, event: "AttrDict") -> bool:
265
+ """Return True if event is matched."""
259
266
  return self._call_func(event)
260
267
 
261
268
 
262
269
  class HookCollection:
263
- """
264
- Helper class to collect event hooks that can later be added to a Delta Chat client.
265
- """
270
+ """Helper class to collect event hooks that can later be added to a Delta Chat client."""
266
271
 
267
272
  def __init__(self) -> None:
268
273
  self._hooks: set[tuple[Callable, Union[type, EventFilter]]] = set()
@@ -1,3 +1,5 @@
1
+ """Message module."""
2
+
1
3
  import json
2
4
  from dataclasses import dataclass
3
5
  from typing import TYPE_CHECKING, Optional, Union
@@ -45,6 +47,7 @@ class Message:
45
47
  return None
46
48
 
47
49
  def get_sender_contact(self) -> Contact:
50
+ """Return sender contact."""
48
51
  from_id = self.get_snapshot().from_id
49
52
  return self.account.get_contact_by_id(from_id)
50
53
 
@@ -53,6 +56,11 @@ class Message:
53
56
  self._rpc.markseen_msgs(self.account.id, [self.id])
54
57
 
55
58
  def continue_autocrypt_key_transfer(self, setup_code: str) -> None:
59
+ """Continue the Autocrypt Setup Message key transfer.
60
+
61
+ This function can be called on received Autocrypt Setup Message
62
+ to import the key encrypted with the provided setup code.
63
+ """
56
64
  self._rpc.continue_autocrypt_key_transfer(self.account.id, self.id, setup_code)
57
65
 
58
66
  def send_webxdc_status_update(self, update: Union[dict, str], description: str) -> None:
@@ -62,6 +70,7 @@ class Message:
62
70
  self._rpc.send_webxdc_status_update(self.account.id, self.id, update, description)
63
71
 
64
72
  def get_webxdc_status_updates(self, last_known_serial: int = 0) -> list:
73
+ """Return a list of Webxdc status updates for Webxdc instance message."""
65
74
  return json.loads(self._rpc.get_webxdc_status_updates(self.account.id, self.id, last_known_serial))
66
75
 
67
76
  def get_info(self) -> str:
@@ -69,6 +78,7 @@ class Message:
69
78
  return self._rpc.get_message_info(self.account.id, self.id)
70
79
 
71
80
  def get_webxdc_info(self) -> dict:
81
+ """Get info from a Webxdc message in JSON format."""
72
82
  return self._rpc.get_webxdc_info(self.account.id, self.id)
73
83
 
74
84
  def wait_until_delivered(self) -> None:
@@ -80,8 +90,10 @@ class Message:
80
90
 
81
91
  @futuremethod
82
92
  def send_webxdc_realtime_advertisement(self):
93
+ """Send an advertisement to join the realtime channel."""
83
94
  yield self._rpc.send_webxdc_realtime_advertisement.future(self.account.id, self.id)
84
95
 
85
96
  @futuremethod
86
97
  def send_webxdc_realtime_data(self, data) -> None:
98
+ """Send data to the realtime channel."""
87
99
  yield self._rpc.send_webxdc_realtime_data.future(self.account.id, self.id, list(data))
@@ -1,3 +1,5 @@
1
+ """Pytest plugin module."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import os
@@ -13,24 +15,30 @@ from .rpc import Rpc
13
15
 
14
16
 
15
17
  class ACFactory:
18
+ """Test account factory."""
19
+
16
20
  def __init__(self, deltachat: DeltaChat) -> None:
17
21
  self.deltachat = deltachat
18
22
 
19
23
  def get_unconfigured_account(self) -> Account:
24
+ """Create a new unconfigured account."""
20
25
  account = self.deltachat.add_account()
21
26
  account.set_config("verified_one_on_one_chats", "1")
22
27
  return account
23
28
 
24
29
  def get_unconfigured_bot(self) -> Bot:
30
+ """Create a new unconfigured bot."""
25
31
  return Bot(self.get_unconfigured_account())
26
32
 
27
33
  def get_credentials(self) -> (str, str):
34
+ """Generate new credentials for chatmail account."""
28
35
  domain = os.getenv("CHATMAIL_DOMAIN")
29
36
  username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
30
37
  return f"{username}@{domain}", f"{username}${username}"
31
38
 
32
39
  @futuremethod
33
40
  def new_configured_account(self):
41
+ """Create a new configured account."""
34
42
  addr, password = self.get_credentials()
35
43
  account = self.get_unconfigured_account()
36
44
  params = {"addr": addr, "password": password}
@@ -40,6 +48,7 @@ class ACFactory:
40
48
  return account
41
49
 
42
50
  def new_configured_bot(self) -> Bot:
51
+ """Create a new configured bot."""
43
52
  addr, password = self.get_credentials()
44
53
  bot = self.get_unconfigured_bot()
45
54
  bot.configure(addr, password)
@@ -47,11 +56,13 @@ class ACFactory:
47
56
 
48
57
  @futuremethod
49
58
  def get_online_account(self):
59
+ """Create a new account and start I/O."""
50
60
  account = yield self.new_configured_account.future()
51
61
  account.bring_online()
52
62
  return account
53
63
 
54
64
  def get_online_accounts(self, num: int) -> list[Account]:
65
+ """Create multiple online accounts."""
55
66
  futures = [self.get_online_account.future() for _ in range(num)]
56
67
  return [f() for f in futures]
57
68
 
@@ -66,6 +77,10 @@ class ACFactory:
66
77
  return ac_clone
67
78
 
68
79
  def get_accepted_chat(self, ac1: Account, ac2: Account) -> Chat:
80
+ """Create a new 1:1 chat between ac1 and ac2 accepted on both sides.
81
+
82
+ Returned chat is a chat with ac2 from ac1 point of view.
83
+ """
69
84
  ac2.create_chat(ac1)
70
85
  return ac1.create_chat(ac2)
71
86
 
@@ -77,6 +92,7 @@ class ACFactory:
77
92
  file: Optional[str] = None,
78
93
  group: Optional[str] = None,
79
94
  ) -> Message:
95
+ """Send a message."""
80
96
  if not from_account:
81
97
  from_account = (self.get_online_accounts(1))[0]
82
98
  to_contact = from_account.create_contact(to_account)
@@ -95,6 +111,7 @@ class ACFactory:
95
111
  file: Optional[str] = None,
96
112
  group: Optional[str] = None,
97
113
  ) -> AttrDict:
114
+ """Send a message and wait until recipient processes it."""
98
115
  self.send_message(
99
116
  to_account=to_client.account,
100
117
  from_account=from_account,
@@ -108,6 +125,7 @@ class ACFactory:
108
125
 
109
126
  @pytest.fixture
110
127
  def rpc(tmp_path) -> AsyncGenerator:
128
+ """RPC client fixture."""
111
129
  rpc_server = Rpc(accounts_dir=str(tmp_path / "accounts"))
112
130
  with rpc_server:
113
131
  yield rpc_server
@@ -115,6 +133,7 @@ def rpc(tmp_path) -> AsyncGenerator:
115
133
 
116
134
  @pytest.fixture
117
135
  def acfactory(rpc) -> AsyncGenerator:
136
+ """Return account factory fixture."""
118
137
  return ACFactory(DeltaChat(rpc))
119
138
 
120
139
 
@@ -132,7 +151,7 @@ def data():
132
151
  raise Exception("Data path cannot be found")
133
152
 
134
153
  def get_path(self, bn):
135
- """return path of file or None if it doesn't exist."""
154
+ """Return path of file or None if it doesn't exist."""
136
155
  fn = os.path.join(self.path, *bn.split("/"))
137
156
  assert os.path.exists(fn)
138
157
  return fn
@@ -1,3 +1,5 @@
1
+ """JSON-RPC client module."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import itertools
@@ -12,16 +14,19 @@ from typing import Any, Iterator, Optional
12
14
 
13
15
 
14
16
  class JsonRpcError(Exception):
15
- pass
17
+ """JSON-RPC error."""
16
18
 
17
19
 
18
20
  class RpcFuture:
21
+ """RPC future waiting for RPC call result."""
22
+
19
23
  def __init__(self, rpc: "Rpc", request_id: int, event: Event):
20
24
  self.rpc = rpc
21
25
  self.request_id = request_id
22
26
  self.event = event
23
27
 
24
28
  def __call__(self):
29
+ """Wait for the future to return the result."""
25
30
  self.event.wait()
26
31
  response = self.rpc.request_results.pop(self.request_id)
27
32
  if "error" in response:
@@ -32,17 +37,19 @@ class RpcFuture:
32
37
 
33
38
 
34
39
  class RpcMethod:
40
+ """RPC method."""
41
+
35
42
  def __init__(self, rpc: "Rpc", name: str):
36
43
  self.rpc = rpc
37
44
  self.name = name
38
45
 
39
46
  def __call__(self, *args) -> Any:
40
- """Synchronously calls JSON-RPC method."""
47
+ """Call JSON-RPC method synchronously."""
41
48
  future = self.future(*args)
42
49
  return future()
43
50
 
44
51
  def future(self, *args) -> Any:
45
- """Asynchronously calls JSON-RPC method."""
52
+ """Call JSON-RPC method asynchronously."""
46
53
  request_id = next(self.rpc.id_iterator)
47
54
  request = {
48
55
  "jsonrpc": "2.0",
@@ -58,8 +65,13 @@ class RpcMethod:
58
65
 
59
66
 
60
67
  class Rpc:
68
+ """RPC client."""
69
+
61
70
  def __init__(self, accounts_dir: Optional[str] = None, **kwargs):
62
- """The given arguments will be passed to subprocess.Popen()"""
71
+ """Initialize RPC client.
72
+
73
+ The given arguments will be passed to subprocess.Popen().
74
+ """
63
75
  if accounts_dir:
64
76
  kwargs["env"] = {
65
77
  **kwargs.get("env", os.environ),
@@ -81,6 +93,7 @@ class Rpc:
81
93
  self.events_thread: Thread
82
94
 
83
95
  def start(self) -> None:
96
+ """Start RPC server subprocess."""
84
97
  if sys.version_info >= (3, 11):
85
98
  self.process = subprocess.Popen(
86
99
  "deltachat-rpc-server",
@@ -130,6 +143,7 @@ class Rpc:
130
143
  self.close()
131
144
 
132
145
  def reader_loop(self) -> None:
146
+ """Process JSON-RPC responses from the RPC server process output."""
133
147
  try:
134
148
  while line := self.process.stdout.readline():
135
149
  response = json.loads(line)
@@ -157,12 +171,13 @@ class Rpc:
157
171
  logging.exception("Exception in the writer loop")
158
172
 
159
173
  def get_queue(self, account_id: int) -> Queue:
174
+ """Get event queue corresponding to the given account ID."""
160
175
  if account_id not in self.event_queues:
161
176
  self.event_queues[account_id] = Queue()
162
177
  return self.event_queues[account_id]
163
178
 
164
179
  def events_loop(self) -> None:
165
- """Requests new events and distributes them between queues."""
180
+ """Request new events and distributes them between queues."""
166
181
  try:
167
182
  while True:
168
183
  if self.closing:
@@ -178,12 +193,12 @@ class Rpc:
178
193
  logging.exception("Exception in the event loop")
179
194
 
180
195
  def wait_for_event(self, account_id: int) -> Optional[dict]:
181
- """Waits for the next event from the given account and returns it."""
196
+ """Wait for the next event from the given account and returns it."""
182
197
  queue = self.get_queue(account_id)
183
198
  return queue.get()
184
199
 
185
200
  def clear_all_events(self, account_id: int):
186
- """Removes all queued-up events for a given account. Useful for tests."""
201
+ """Remove all queued-up events for a given account. Useful for tests."""
187
202
  queue = self.get_queue(account_id)
188
203
  try:
189
204
  while True:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deltachat-rpc-client
3
- Version: 1.159.3
3
+ Version: 1.159.4
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
@@ -0,0 +1,19 @@
1
+ deltachat_rpc_client/__init__.py,sha256=zroJARiq9HBi7Ln03KlICOUJHSjm3CGIFkHfXILEdOQ,567
2
+ deltachat_rpc_client/_utils.py,sha256=v0iWVSa6S-s078GgnnhflCRhA9pfWI-CfK8mjZ4zjXc,6393
3
+ deltachat_rpc_client/account.py,sha256=1Vh2Dez3CCpXaZaa9Fyjpx8fkKheg5n-4_1YOZ5qqTM,16554
4
+ deltachat_rpc_client/chat.py,sha256=LTZSYHC24bo5fDAX1XCDP79XaHIyuPZS_-zzD0vu1Rw,11238
5
+ deltachat_rpc_client/client.py,sha256=UeCJov-ZvB3WMzl_6aJEESGRQCdvS3UAKFL4Adar6cg,7206
6
+ deltachat_rpc_client/const.py,sha256=3Ds8xvY58CIntj7SfJ6BLdlu20csBTlUV8x8OkqGXCM,5778
7
+ deltachat_rpc_client/contact.py,sha256=ORhz1TWcCxvYQnpVACDptWEbrpjKoIJEsTWAcwo8Fqw,2043
8
+ deltachat_rpc_client/deltachat.py,sha256=4KpiHbpfXM_LmbG-OvtDACK9W09UH0tsJyFunfH9TR4,1544
9
+ deltachat_rpc_client/events.py,sha256=Y45LoGlQy0i1U-LhoqF9njqJWA8v4b-XHfXZ4VOr1TQ,10205
10
+ deltachat_rpc_client/message.py,sha256=2MnC4OgDockrfqPherOI_ad8AFbGhHGLVoGKanb5GRU,3687
11
+ deltachat_rpc_client/py.typed,sha256=nGQ9Itq-bkXBn5Ri1JIR0oYnDNv7LDRfkowxBSSqVTM,60
12
+ deltachat_rpc_client/pytestplugin.py,sha256=gUOP63mbEBrWJAta1JOoXaGYm3oCElYRgBkEEtKtnn8,5603
13
+ deltachat_rpc_client/rpc.py,sha256=AUY8UJP7NlRSE5ewJs9jGxJdK8etKqsCEFWiIqnX5T8,6981
14
+ deltachat_rpc_client-1.159.4.dist-info/licenses/LICENSE,sha256=Pz2eACSxkhsGfW9_iN60pgy-enjnbGTj8df8O3ebnQQ,16726
15
+ deltachat_rpc_client-1.159.4.dist-info/METADATA,sha256=ysjsCKJiInq_qRbS733uH43_WAt-vqZV1DZxJUl1ukA,2175
16
+ deltachat_rpc_client-1.159.4.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
17
+ deltachat_rpc_client-1.159.4.dist-info/entry_points.txt,sha256=VHpX6EnKBaNj89qJWctApThnMa8suyaamfZEnQacXgc,81
18
+ deltachat_rpc_client-1.159.4.dist-info/top_level.txt,sha256=ePNMkY10htGrLiLydH1ITvYFM3LcTEa51HyPqJ40hDk,21
19
+ deltachat_rpc_client-1.159.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,19 +0,0 @@
1
- deltachat_rpc_client/__init__.py,sha256=L19BWnRu9TMF--jQ7e5Asgme951WGsldrTknW86KyTg,566
2
- deltachat_rpc_client/_utils.py,sha256=FnmjEIU3pvr3y9jqFAvil8GUHzXNm2dDVNxsIJTZEMI,6393
3
- deltachat_rpc_client/account.py,sha256=qPIPYPHN8nNR8ZUi0joMHxdYY6IvWxxA1uT_T7pSG9Y,16222
4
- deltachat_rpc_client/chat.py,sha256=1pbuEBWBjZDD9qQ3U1hY5jozzx0MSs9Ybpw33reA2MU,11208
5
- deltachat_rpc_client/client.py,sha256=yyXt2USkBeTsqNxtXZNNUqdsPVkqEUHYf7C-5JFmX7s,7043
6
- deltachat_rpc_client/const.py,sha256=39o1czdW4COf5qH60B7G_vPF3eyeC42tpgIKcejtK8Q,5610
7
- deltachat_rpc_client/contact.py,sha256=kbqVI93V7-VSCjH8Hfm5M9a62HMnOjBKuw_g6iJN7w8,1966
8
- deltachat_rpc_client/deltachat.py,sha256=c22_2GX71we6JwQ4yjP-S89mrUYPPbEb3rrPwHHeXt4,1538
9
- deltachat_rpc_client/events.py,sha256=qYgydsbuWYxQ1PO5ms-D8I5aHpTIiQ6Fg9p23QTvqf0,9877
10
- deltachat_rpc_client/message.py,sha256=6Jf2WlOp8HOYpjPLXXa_5_rGTg6wVN27PXnF3XIjpGQ,3156
11
- deltachat_rpc_client/py.typed,sha256=nGQ9Itq-bkXBn5Ri1JIR0oYnDNv7LDRfkowxBSSqVTM,60
12
- deltachat_rpc_client/pytestplugin.py,sha256=Hqzx1nBryhzUV_yMR9BDnrqLrskvSd8MJ5Ap8BRUAFE,4874
13
- deltachat_rpc_client/rpc.py,sha256=7RIdyEDwOXU0ACU0LanVqTT1RuIDci5iDr7dNEDE_tE,6554
14
- deltachat_rpc_client-1.159.3.dist-info/licenses/LICENSE,sha256=Pz2eACSxkhsGfW9_iN60pgy-enjnbGTj8df8O3ebnQQ,16726
15
- deltachat_rpc_client-1.159.3.dist-info/METADATA,sha256=O8crLgUozS793i6ApnpuXdYCsoE0eShZ-sdDXMgN4x8,2175
16
- deltachat_rpc_client-1.159.3.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
17
- deltachat_rpc_client-1.159.3.dist-info/entry_points.txt,sha256=VHpX6EnKBaNj89qJWctApThnMa8suyaamfZEnQacXgc,81
18
- deltachat_rpc_client-1.159.3.dist-info/top_level.txt,sha256=ePNMkY10htGrLiLydH1ITvYFM3LcTEa51HyPqJ40hDk,21
19
- deltachat_rpc_client-1.159.3.dist-info/RECORD,,