simplex-chat 7.1.0b6.post1__tar.gz → 7.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/PKG-INFO +1 -10
  2. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/README.md +0 -9
  3. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/_native.py +23 -10
  4. simplex_chat-7.1.3/src/simplex_chat/_version.py +16 -0
  5. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/bot.py +4 -0
  6. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/core.py +78 -6
  7. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/types/_commands.py +190 -1
  8. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/types/_responses.py +62 -1
  9. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/types/_types.py +386 -3
  10. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_client_and_waiters.py +52 -2
  11. simplex_chat-7.1.3/tests/test_codegen.py +146 -0
  12. simplex_chat-7.1.3/tests/test_core_resize_image.py +121 -0
  13. simplex_chat-7.1.0b6.post1/src/simplex_chat/_version.py +0 -16
  14. simplex_chat-7.1.0b6.post1/tests/test_codegen.py +0 -47
  15. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/.gitignore +0 -0
  16. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/LICENSE +0 -0
  17. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/examples/squaring_bot.py +0 -0
  18. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/pyproject.toml +0 -0
  19. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/__init__.py +0 -0
  20. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/__main__.py +0 -0
  21. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/api.py +0 -0
  22. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/client.py +0 -0
  23. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/filters.py +0 -0
  24. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/py.typed +0 -0
  25. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/types/__init__.py +0 -0
  26. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/types/_events.py +0 -0
  27. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/src/simplex_chat/util.py +0 -0
  28. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_api.py +0 -0
  29. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_bot_registration.py +0 -0
  30. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_core_migrate_init.py +0 -0
  31. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_filters.py +0 -0
  32. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_native_cache.py +0 -0
  33. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_native_url.py +0 -0
  34. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_recv_executor.py +0 -0
  35. {simplex_chat-7.1.0b6.post1 → simplex_chat-7.1.3}/tests/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simplex-chat
3
- Version: 7.1.0b6.post1
3
+ Version: 7.1.3
4
4
  Summary: SimpleX Chat Python library for chat bots
5
5
  Project-URL: Homepage, https://github.com/simplex-chat/simplex-chat/tree/stable/packages/simplex-chat-python
6
6
  Project-URL: Issues, https://github.com/simplex-chat/simplex-chat/issues
@@ -84,15 +84,6 @@ ruff check && pyright && pytest tests/
84
84
 
85
85
  Wire types under `src/simplex_chat/types/_*.py` are generated. Regenerate with `cabal test simplex-chat-test --test-options='--match Python'`.
86
86
 
87
- ## Release
88
-
89
- Manual for now. Bump `_version.py:__version__`, build a wheel, upload to PyPI:
90
-
91
- ```bash
92
- uv build --wheel
93
- uv publish --token "$PYPI_TOKEN"
94
- ```
95
-
96
87
  ## License
97
88
 
98
89
  [AGPL-3.0](./LICENSE)
@@ -56,15 +56,6 @@ ruff check && pyright && pytest tests/
56
56
 
57
57
  Wire types under `src/simplex_chat/types/_*.py` are generated. Regenerate with `cabal test simplex-chat-test --test-options='--match Python'`.
58
58
 
59
- ## Release
60
-
61
- Manual for now. Bump `_version.py:__version__`, build a wheel, upload to PyPI:
62
-
63
- ```bash
64
- uv build --wheel
65
- uv publish --token "$PYPI_TOKEN"
66
- ```
67
-
68
59
  ## License
69
60
 
70
61
  [AGPL-3.0](./LICENSE)
@@ -277,17 +277,30 @@ def lib() -> ctypes.CDLL:
277
277
  return _lib
278
278
 
279
279
 
280
- QUEUE_SIZE_UNSUPPORTED = (
281
- "loaded libsimplex does not export chat_migrate_init_queue; queue size needs a newer libsimplex"
282
- )
283
-
284
-
285
- def migrate_init_queue() -> Any:
286
- """`chat_migrate_init_queue`, which older libsimplex releases do not export."""
280
+ def _optional_export(name: str, feature: str, argtypes: list[Any]) -> Any:
287
281
  try:
288
- fn = lib().chat_migrate_init_queue
282
+ fn = getattr(lib(), name)
289
283
  except AttributeError as e:
290
- raise RuntimeError(QUEUE_SIZE_UNSUPPORTED) from e
291
- fn.argtypes = [c_char_p, c_char_p, c_char_p, c_int, POINTER(c_void_p)]
284
+ raise RuntimeError(
285
+ f"loaded libsimplex does not export {name}; {feature} needs a newer libsimplex"
286
+ ) from e
287
+ fn.argtypes = argtypes
292
288
  fn.restype = c_void_p
293
289
  return fn
290
+
291
+
292
+ def migrate_init_queue() -> Any:
293
+ """`chat_migrate_init_queue`, which older libsimplex releases do not export."""
294
+ return _optional_export(
295
+ "chat_migrate_init_queue",
296
+ "queue size",
297
+ [c_char_p, c_char_p, c_char_p, c_int, POINTER(c_void_p)],
298
+ )
299
+
300
+
301
+ def resize_image() -> Any:
302
+ return _optional_export("chat_resize_image", "image resizing", [c_char_p, c_int, c_int, c_int])
303
+
304
+
305
+ def resize_image_uri() -> Any:
306
+ return _optional_export("chat_resize_image_uri", "image resizing", [c_char_p, c_int, c_int])
@@ -0,0 +1,16 @@
1
+ """Package version and the simplex-chat-libs release it loads.
2
+
3
+ Written by scripts/ci/packages_release.py prepare: it bumps the version
4
+ and pins new library releases with the SHA-256 of their assets.
5
+ """
6
+
7
+ __version__ = "7.1.3" # PEP 440 — read by hatchling for wheel metadata
8
+ LIBS_RELEASE = "core-7.1.3.0" # simplex-chat-libs release tag
9
+ LIBS_SHA256 = {
10
+ "simplex-chat-libs-linux-aarch64.zip": "46e296417de8cddc0d6f800d7c634dec410a1af20ea38ff31f224ef388aaeb00",
11
+ "simplex-chat-libs-linux-x86_64-postgres.zip": "ea89b4e95836c79c887cc13a2dc9fc66e319b21ee4148ca38817539657601050",
12
+ "simplex-chat-libs-linux-x86_64.zip": "12b6ea97c64a5ea53a66f47e827dc8c0d6bc767fd44b22aab47786ac90e91d01",
13
+ "simplex-chat-libs-macos-aarch64.zip": "b7612b05cd17267f4e24efacfd16fa4a9f9b0c39b34c2f52465e34395492a452",
14
+ "simplex-chat-libs-macos-x86_64.zip": "ce7a9a2be0cd0e6782902d8ce4420e8660e4e2360d7fc0248584a0163f1a236d",
15
+ "simplex-chat-libs-windows-x86_64.zip": "a9ed55c82753d57cfddaddee78cc989b0edf3fe5fe08d7214e6f84ab43609999",
16
+ }
@@ -96,6 +96,7 @@ class Bot(Client):
96
96
  update_profile: bool = True,
97
97
  auto_accept: bool = True,
98
98
  business_address: bool = False,
99
+ chat_layout: T.ChatLayout | None = None,
99
100
  allow_files: bool = False,
100
101
  log_contacts: bool = True,
101
102
  log_network: bool = False,
@@ -115,6 +116,7 @@ class Bot(Client):
115
116
  self._update_address = update_address
116
117
  self._auto_accept = auto_accept
117
118
  self._business_address = business_address
119
+ self._chat_layout = chat_layout
118
120
  self._allow_files = allow_files
119
121
 
120
122
  # ------------------------------------------------------------------ #
@@ -153,6 +155,8 @@ class Bot(Client):
153
155
  desired: T.AddressSettings = {"businessAddress": self._business_address}
154
156
  if self._auto_accept:
155
157
  desired["autoAccept"] = {"acceptIncognito": False}
158
+ if self._chat_layout is not None:
159
+ desired["chatLayout"] = self._chat_layout
156
160
  if self._welcome is not None:
157
161
  desired["autoReply"] = (
158
162
  {"type": "text", "text": self._welcome}
@@ -1,17 +1,19 @@
1
- """Internal typed async wrapper around libsimplex's 8 C ABI functions.
1
+ """Typed async wrapper around libsimplex's C ABI functions.
2
2
 
3
3
  Users interact with `Bot` / `ChatApi`. This module is exposed as
4
- `simplex_chat.core` for tests and the api.ChatApi class only.
4
+ `simplex_chat.core` for tests, the api.ChatApi class and image resizing
5
+ (`resize_image`, `chat_resize_image`, `chat_resize_image_uri`).
5
6
  """
6
7
 
7
8
  from __future__ import annotations
8
9
 
9
10
  import asyncio
11
+ import base64
10
12
  import ctypes
11
13
  import json
12
14
  from concurrent.futures import Executor
13
15
  from enum import StrEnum
14
- from typing import Any, TypedDict
16
+ from typing import Any, Literal, TypedDict, overload
15
17
 
16
18
  from . import _native
17
19
  from .types import CR, CEvt, T
@@ -70,6 +72,12 @@ class MigrationConfirmation(StrEnum):
70
72
  ERROR = "error"
71
73
 
72
74
 
75
+ class ImageSize(StrEnum):
76
+ IMAGE = "image"
77
+ PROFILE_PREVIEW = "profilePreview"
78
+ MESSAGE_PREVIEW = "messagePreview"
79
+
80
+
73
81
  class CryptoArgs(TypedDict): # wire-format JSON; camelCase fields
74
82
  fileKey: str
75
83
  fileNonce: str
@@ -88,6 +96,12 @@ def _read_and_free(ptr: int | None) -> str:
88
96
  _native.libc().free(ctypes.c_void_p(ptr))
89
97
 
90
98
 
99
+ def _check_c_int(name: str, value: int) -> None:
100
+ # ctypes silently wraps ints that do not fit C int.
101
+ if ctypes.c_int(value).value != value:
102
+ raise ValueError(f"{name} {value} does not fit C int")
103
+
104
+
91
105
  async def chat_send_cmd(ctrl: int, cmd: str) -> CR.ChatResponse:
92
106
  def _call() -> str:
93
107
  ptr = _native.lib().chat_send_cmd(ctrl, cmd.encode("utf-8"))
@@ -138,9 +152,8 @@ async def chat_migrate_init(
138
152
 
139
153
  `queue_size` is the size of internal queues; the core default is used when None.
140
154
  """
141
- # ctypes silently wraps ints that do not fit C int.
142
- if queue_size is not None and ctypes.c_int(queue_size).value != queue_size:
143
- raise ValueError(f"queue_size {queue_size} does not fit C int")
155
+ if queue_size is not None:
156
+ _check_c_int("queue_size", queue_size)
144
157
 
145
158
  init_queue = _native.migrate_init_queue() if queue_size is not None else None
146
159
 
@@ -246,3 +259,62 @@ def _crypto_args_result(raw: str) -> CryptoArgs:
246
259
  if parsed.get("type") == "error":
247
260
  raise RuntimeError(parsed.get("writeError", "unknown write error"))
248
261
  raise RuntimeError(f"unexpected result: {raw[:200]}")
262
+
263
+
264
+ async def chat_resize_image_uri(data: bytes, max_size: int) -> str:
265
+ _check_c_int("len(data)", len(data))
266
+ _check_c_int("max_size", max_size)
267
+ resize = _native.resize_image_uri()
268
+
269
+ def _call() -> str:
270
+ return _read_and_free(resize(data, len(data), max_size))
271
+
272
+ return _resized_image(await asyncio.to_thread(_call))
273
+
274
+
275
+ async def chat_resize_image(
276
+ data: bytes, max_size: int, image_format: Literal["jpeg", "png"]
277
+ ) -> bytes:
278
+ return await _resize_image_data(data, max_size, 1 if image_format == "png" else 0)
279
+
280
+
281
+ @overload
282
+ async def resize_image(data: bytes, size: Literal[ImageSize.IMAGE]) -> bytes: ...
283
+
284
+
285
+ @overload
286
+ async def resize_image(
287
+ data: bytes, size: Literal[ImageSize.PROFILE_PREVIEW, ImageSize.MESSAGE_PREVIEW]
288
+ ) -> str: ...
289
+
290
+
291
+ @overload
292
+ async def resize_image(data: bytes, size: ImageSize) -> bytes | str: ...
293
+
294
+
295
+ async def resize_image(data: bytes, size: ImageSize) -> bytes | str:
296
+ """Resize image to the app size limit: JPEG or PNG for an image, a data URI for a preview."""
297
+ match size:
298
+ case ImageSize.IMAGE:
299
+ return await _resize_image_data(data, 261120, -1)
300
+ case ImageSize.PROFILE_PREVIEW:
301
+ return await chat_resize_image_uri(data, 12500)
302
+ case ImageSize.MESSAGE_PREVIEW:
303
+ return await chat_resize_image_uri(data, 14000)
304
+
305
+
306
+ async def _resize_image_data(data: bytes, max_size: int, png: int) -> bytes:
307
+ _check_c_int("len(data)", len(data))
308
+ _check_c_int("max_size", max_size)
309
+ resize = _native.resize_image()
310
+
311
+ def _call() -> str:
312
+ return _read_and_free(resize(data, len(data), max_size, png))
313
+
314
+ return base64.b64decode(_resized_image(await asyncio.to_thread(_call)))
315
+
316
+
317
+ def _resized_image(res: str) -> str:
318
+ if not res:
319
+ raise RuntimeError("image cannot be decoded or does not fit max_size")
320
+ return res
@@ -342,6 +342,32 @@ def APIListMembers_cmd_string(self: APIListMembers) -> str:
342
342
  APIListMembers_Response = CR.GroupMembers | CR.ChatCmdError
343
343
 
344
344
 
345
+ # Create a direct message contact with a group member.
346
+ # Network usage: interactive.
347
+ class APICreateMemberContact(TypedDict):
348
+ groupId: int # int64
349
+ groupMemberId: int # int64
350
+
351
+
352
+ def APICreateMemberContact_cmd_string(self: APICreateMemberContact) -> str:
353
+ return '/_create member contact #' + str(self['groupId']) + ' ' + str(self['groupMemberId'])
354
+
355
+ APICreateMemberContact_Response = CR.NewMemberContact | CR.ChatCmdError
356
+
357
+
358
+ # Send a direct message invitation to a group member contact.
359
+ # Network usage: background.
360
+ class APISendMemberContactInvitation(TypedDict):
361
+ contactId: int # int64
362
+ msgContent_: NotRequired["T.MsgContent"]
363
+
364
+
365
+ def APISendMemberContactInvitation_cmd_string(self: APISendMemberContactInvitation) -> str:
366
+ return '/_invite member contact @' + str(self['contactId']) + ((' json ' + json.dumps(self.get('msgContent_'))) if self.get('msgContent_') is not None else '')
367
+
368
+ APISendMemberContactInvitation_Response = CR.NewMemberContactSentInv | CR.ChatCmdError
369
+
370
+
345
371
  # Create group.
346
372
  # Network usage: no.
347
373
  class APINewGroup(TypedDict):
@@ -512,7 +538,7 @@ class APIConnectPlan(TypedDict):
512
538
 
513
539
 
514
540
  def APIConnectPlan_cmd_string(self: APIConnectPlan) -> str:
515
- return '/_connect plan ' + str(self['userId']) + ' ' + self.get('connectTarget')
541
+ return '/_connect plan ' + str(self['userId']) + ' ' + self.get('connectTarget') + T.PlanResolveMode_cmd_string(self['resolveMode']) + ((' sig=' + json.dumps(self.get('linkOwnerSig'))) if self.get('linkOwnerSig') is not None else '')
516
542
 
517
543
  APIConnectPlan_Response = CR.ConnectionPlan | CR.ChatCmdError
518
544
 
@@ -553,6 +579,80 @@ Connect_Response = (
553
579
  )
554
580
 
555
581
 
582
+ # Prepare contact chat from link data before connecting.
583
+ # Network usage: no.
584
+ class APIPrepareContact(TypedDict):
585
+ userId: int # int64
586
+ connLink: "T.CreatedConnLink"
587
+ verifiedDomain: NotRequired[str]
588
+ contactSLinkData: "T.ContactShortLinkData"
589
+
590
+
591
+ def APIPrepareContact_cmd_string(self: APIPrepareContact) -> str:
592
+ return '/_prepare contact ' + str(self['userId']) + ' ' + T.CreatedConnLink_cmd_string(self['connLink']) + ((' domain=' + self.get('verifiedDomain')) if self.get('verifiedDomain') is not None else '') + ' ' + json.dumps(self['contactSLinkData'])
593
+
594
+ APIPrepareContact_Response = CR.NewPreparedChat | CR.ChatCmdError
595
+
596
+
597
+ # Prepare group chat from link data before connecting.
598
+ # Network usage: no.
599
+ class APIPrepareGroup(TypedDict):
600
+ userId: int # int64
601
+ connLinkContact: "T.CreatedConnLink"
602
+ direct: bool
603
+ verifiedDomain: NotRequired[str]
604
+ groupSLinkData: "T.GroupShortLinkData"
605
+
606
+
607
+ def APIPrepareGroup_cmd_string(self: APIPrepareGroup) -> str:
608
+ return '/_prepare group ' + str(self['userId']) + ' ' + T.CreatedConnLink_cmd_string(self['connLinkContact']) + (' direct=off' if not self['direct'] else '') + ((' domain=' + self.get('verifiedDomain')) if self.get('verifiedDomain') is not None else '') + ' ' + json.dumps(self['groupSLinkData'])
609
+
610
+ APIPrepareGroup_Response = CR.NewPreparedChat | CR.ChatCmdError
611
+
612
+
613
+ # Connect to prepared contact.
614
+ # Network usage: interactive.
615
+ class APIConnectPreparedContact(TypedDict):
616
+ contactId: int # int64
617
+ incognito: bool
618
+ msgContent_: NotRequired["T.MsgContent"]
619
+
620
+
621
+ def APIConnectPreparedContact_cmd_string(self: APIConnectPreparedContact) -> str:
622
+ return '/_connect contact @' + str(self['contactId']) + (' incognito=on' if self['incognito'] else '') + ((' json ' + json.dumps(self.get('msgContent_'))) if self.get('msgContent_') is not None else '')
623
+
624
+ APIConnectPreparedContact_Response = CR.StartedConnectionToContact | CR.ContactAlreadyExists | CR.ChatCmdError
625
+
626
+
627
+ # Connect to prepared group.
628
+ # Network usage: interactive.
629
+ class APIConnectPreparedGroup(TypedDict):
630
+ groupId: int # int64
631
+ incognito: bool
632
+ ownerContact: NotRequired["T.GroupOwnerContact"]
633
+ msgContent_: NotRequired["T.MsgContent"]
634
+
635
+
636
+ def APIConnectPreparedGroup_cmd_string(self: APIConnectPreparedGroup) -> str:
637
+ return '/_connect group #' + str(self['groupId']) + (' incognito=on' if self['incognito'] else '') + ((' ' + T.GroupOwnerContact_cmd_string(self.get('ownerContact'))) if self.get('ownerContact') is not None else '') + ((' json ' + json.dumps(self.get('msgContent_'))) if self.get('msgContent_') is not None else '')
638
+
639
+ APIConnectPreparedGroup_Response = CR.StartedConnectionToGroup | CR.ChatCmdError
640
+
641
+
642
+ # Connect to existing contact via address in its profile.
643
+ # Network usage: interactive.
644
+ class APIConnectContactViaAddress(TypedDict):
645
+ userId: int # int64
646
+ incognito: bool
647
+ contactId: int # int64
648
+
649
+
650
+ def APIConnectContactViaAddress_cmd_string(self: APIConnectContactViaAddress) -> str:
651
+ return '/_connect contact ' + str(self['userId']) + (' incognito=on' if self['incognito'] else '') + ' ' + str(self['contactId'])
652
+
653
+ APIConnectContactViaAddress_Response = CR.SentInvitationToContact | CR.ChatCmdError
654
+
655
+
556
656
  # Accept contact request.
557
657
  # Network usage: interactive.
558
658
  class APIAcceptContact(TypedDict):
@@ -622,6 +722,59 @@ def APIGetChats_cmd_string(self: APIGetChats) -> str:
622
722
  APIGetChats_Response = CR.ApiChats | CR.ChatCmdError
623
723
 
624
724
 
725
+ # Get chat messages.
726
+ # Network usage: no.
727
+ class APIGetChat(TypedDict):
728
+ chatRef: "T.ChatRef"
729
+ contentTag: NotRequired["T.MsgContentTag"]
730
+ chatPagination: "T.ChatPagination"
731
+ search: NotRequired[str]
732
+
733
+
734
+ def APIGetChat_cmd_string(self: APIGetChat) -> str:
735
+ return '/_get chat ' + T.ChatRef_cmd_string(self['chatRef']) + ((' content=' + str(self.get('contentTag'))) if self.get('contentTag') is not None else '') + ' ' + T.ChatPagination_cmd_string(self['chatPagination']) + ((' search=' + self.get('search')) if self.get('search') is not None else '')
736
+
737
+ APIGetChat_Response = CR.ApiChat | CR.ChatCmdError
738
+
739
+
740
+ # Mark messages as read.
741
+ # Network usage: no.
742
+ class APIChatItemsRead(TypedDict):
743
+ chatRef: "T.ChatRef"
744
+ chatItemIds: list[int] # int64, non-empty
745
+
746
+
747
+ def APIChatItemsRead_cmd_string(self: APIChatItemsRead) -> str:
748
+ return '/_read chat items ' + T.ChatRef_cmd_string(self['chatRef']) + ' ' + ','.join(map(str, self['chatItemIds']))
749
+
750
+ APIChatItemsRead_Response = CR.ItemsReadForChat | CR.ChatCmdError
751
+
752
+
753
+ # Get contact information.
754
+ # Network usage: no.
755
+ class APIContactInfo(TypedDict):
756
+ contactId: int # int64
757
+
758
+
759
+ def APIContactInfo_cmd_string(self: APIContactInfo) -> str:
760
+ return '/_info @' + str(self['contactId'])
761
+
762
+ APIContactInfo_Response = CR.ContactInfo | CR.ChatCmdError
763
+
764
+
765
+ # Get message information.
766
+ # Network usage: no.
767
+ class APIGetChatItemInfo(TypedDict):
768
+ chatRef: "T.ChatRef"
769
+ chatItemId: int # int64
770
+
771
+
772
+ def APIGetChatItemInfo_cmd_string(self: APIGetChatItemInfo) -> str:
773
+ return '/_get item info ' + T.ChatRef_cmd_string(self['chatRef']) + ' ' + str(self['chatItemId'])
774
+
775
+ APIGetChatItemInfo_Response = CR.ChatItemInfo | CR.ChatCmdError
776
+
777
+
625
778
  # Delete chat.
626
779
  # Network usage: background.
627
780
  class APIDeleteChat(TypedDict):
@@ -825,6 +978,42 @@ def APIStopChat_cmd_string(self: APIStopChat) -> str:
825
978
  APIStopChat_Response = CR.ChatStopped
826
979
 
827
980
 
981
+ # Get network configuration.
982
+ # Network usage: no.
983
+ class APIGetNetworkConfig(TypedDict):
984
+ pass
985
+
986
+
987
+ def APIGetNetworkConfig_cmd_string(self: APIGetNetworkConfig) -> str:
988
+ return '/network'
989
+
990
+ APIGetNetworkConfig_Response = CR.NetworkConfig | CR.ChatCmdError
991
+
992
+
993
+ # Set network configuration.
994
+ # Network usage: background.
995
+ class APISetNetworkConfig(TypedDict):
996
+ networkConfig: "T.NetworkConfig"
997
+
998
+
999
+ def APISetNetworkConfig_cmd_string(self: APISetNetworkConfig) -> str:
1000
+ return '/_network ' + json.dumps(self['networkConfig'])
1001
+
1002
+ APISetNetworkConfig_Response = CR.CmdOk | CR.ChatCmdError
1003
+
1004
+
1005
+ # Get user servers and chat relays, grouped by operator.
1006
+ # Network usage: no.
1007
+ class APIGetUserServers(TypedDict):
1008
+ userId: int # int64
1009
+
1010
+
1011
+ def APIGetUserServers_cmd_string(self: APIGetUserServers) -> str:
1012
+ return '/_servers ' + str(self['userId'])
1013
+
1014
+ APIGetUserServers_Response = CR.UserServers | CR.ChatCmdError
1015
+
1016
+
828
1017
  # Remote control commands
829
1018
  # Allows a bot to accept an incoming remote control session from a SimpleX Desktop client, giving the desktop live access to the bot's SimpleX instance.
830
1019
 
@@ -13,6 +13,18 @@ class ActiveUser(TypedDict):
13
13
  type: Literal["activeUser"]
14
14
  user: "T.User"
15
15
 
16
+ class ApiChat(TypedDict):
17
+ type: Literal["apiChat"]
18
+ user: "T.User"
19
+ chat: "T.AChat"
20
+ navInfo: NotRequired["T.NavigationInfo"]
21
+
22
+ class ChatItemInfo(TypedDict):
23
+ type: Literal["chatItemInfo"]
24
+ user: "T.User"
25
+ chatItem: "T.AChatItem"
26
+ chatItemInfo: "T.ChatItemInfo"
27
+
16
28
  class ChatItemNotChanged(TypedDict):
17
29
  type: Literal["chatItemNotChanged"]
18
30
  user: "T.User"
@@ -81,6 +93,13 @@ class ContactDeleted(TypedDict):
81
93
  user: "T.User"
82
94
  contact: "T.Contact"
83
95
 
96
+ class ContactInfo(TypedDict):
97
+ type: Literal["contactInfo"]
98
+ user: "T.User"
99
+ contact: "T.Contact"
100
+ connectionStats_: NotRequired["T.ConnectionStats"]
101
+ customUserProfile: NotRequired["T.Profile"]
102
+
84
103
  class ContactPrefsUpdated(TypedDict):
85
104
  type: Literal["contactPrefsUpdated"]
86
105
  user: "T.User"
@@ -192,6 +211,11 @@ class Invitation(TypedDict):
192
211
  connLinkInvitation: "T.CreatedConnLink"
193
212
  connection: "T.PendingContactConnection"
194
213
 
214
+ class ItemsReadForChat(TypedDict):
215
+ type: Literal["itemsReadForChat"]
216
+ user: "T.User"
217
+ chatInfo: "T.ChatInfo"
218
+
195
219
  class LeftMemberUser(TypedDict):
196
220
  type: Literal["leftMemberUser"]
197
221
  user: "T.User"
@@ -219,11 +243,34 @@ class MembersRoleUser(TypedDict):
219
243
  toRole: "T.GroupMemberRole"
220
244
  msgSigned: bool
221
245
 
246
+ class NetworkConfig(TypedDict):
247
+ type: Literal["networkConfig"]
248
+ networkConfig: "T.NetworkConfig"
249
+
222
250
  class NewChatItems(TypedDict):
223
251
  type: Literal["newChatItems"]
224
252
  user: "T.User"
225
253
  chatItems: list["T.AChatItem"]
226
254
 
255
+ class NewMemberContact(TypedDict):
256
+ type: Literal["newMemberContact"]
257
+ user: "T.User"
258
+ contact: "T.Contact"
259
+ groupInfo: "T.GroupInfo"
260
+ member: "T.GroupMember"
261
+
262
+ class NewMemberContactSentInv(TypedDict):
263
+ type: Literal["newMemberContactSentInv"]
264
+ user: "T.User"
265
+ contact: "T.Contact"
266
+ groupInfo: "T.GroupInfo"
267
+ member: "T.GroupMember"
268
+
269
+ class NewPreparedChat(TypedDict):
270
+ type: Literal["newPreparedChat"]
271
+ user: "T.User"
272
+ chat: "T.AChat"
273
+
227
274
  class RcvFileAccepted(TypedDict):
228
275
  type: Literal["rcvFileAccepted"]
229
276
  user: "T.User"
@@ -353,6 +400,11 @@ class UserProfileNoChange(TypedDict):
353
400
  type: Literal["userProfileNoChange"]
354
401
  user: "T.User"
355
402
 
403
+ class UserServers(TypedDict):
404
+ type: Literal["userServers"]
405
+ user: "T.User"
406
+ userServers: list["T.UserOperatorServers"]
407
+
356
408
  class UsersList(TypedDict):
357
409
  type: Literal["usersList"]
358
410
  users: list["T.UserInfo"]
@@ -365,6 +417,8 @@ class ApiChats(TypedDict):
365
417
  ChatResponse = (
366
418
  AcceptingContactRequest
367
419
  | ActiveUser
420
+ | ApiChat
421
+ | ChatItemInfo
368
422
  | ChatItemNotChanged
369
423
  | ChatItemReaction
370
424
  | ChatItemUpdated
@@ -379,6 +433,7 @@ ChatResponse = (
379
433
  | ContactAlreadyExists
380
434
  | ContactConnectionDeleted
381
435
  | ContactDeleted
436
+ | ContactInfo
382
437
  | ContactPrefsUpdated
383
438
  | ContactRequestRejected
384
439
  | ContactsList
@@ -398,11 +453,16 @@ ChatResponse = (
398
453
  | GroupsList
399
454
  | GroupDomainVerified
400
455
  | Invitation
456
+ | ItemsReadForChat
401
457
  | LeftMemberUser
402
458
  | MemberAccepted
403
459
  | MembersBlockedForAllUser
404
460
  | MembersRoleUser
461
+ | NetworkConfig
405
462
  | NewChatItems
463
+ | NewMemberContact
464
+ | NewMemberContactSentInv
465
+ | NewPreparedChat
406
466
  | RcvFileAccepted
407
467
  | RcvFileAcceptedSndCancelled
408
468
  | RcvFileCancelled
@@ -425,8 +485,9 @@ ChatResponse = (
425
485
  | UserDeletedMembers
426
486
  | UserProfileUpdated
427
487
  | UserProfileNoChange
488
+ | UserServers
428
489
  | UsersList
429
490
  | ApiChats
430
491
  )
431
492
 
432
- ChatResponse_Tag = Literal["acceptingContactRequest", "activeUser", "chatItemNotChanged", "chatItemReaction", "chatItemUpdated", "chatItemsDeleted", "chatMsgContent", "chatRunning", "chatStarted", "chatStopped", "cmdOk", "chatCmdError", "connectionPlan", "contactAlreadyExists", "contactConnectionDeleted", "contactDeleted", "contactPrefsUpdated", "contactRequestRejected", "contactsList", "groupDeletedUser", "groupLink", "groupLinkCreated", "groupLinkDeleted", "groupCreated", "publicGroupCreated", "publicGroupCreationFailed", "groupRelays", "groupRelaysAdded", "groupRelaysAddFailed", "relayGroupAllowed", "groupMembers", "groupUpdated", "groupsList", "groupDomainVerified", "invitation", "leftMemberUser", "memberAccepted", "membersBlockedForAllUser", "membersRoleUser", "newChatItems", "rcvFileAccepted", "rcvFileAcceptedSndCancelled", "rcvFileCancelled", "remoteCtrlConnected", "remoteCtrlConnecting", "sentConfirmation", "sentGroupInvitation", "sentGroupInvitationViaGroup", "sentInvitation", "sentInvitationToContact", "serviceReplyAccepted", "sndFileCancelled", "startedConnectionToContact", "startedConnectionToGroup", "userAcceptedGroupSent", "userContactLink", "userContactLinkCreated", "userContactLinkDeleted", "userContactLinkUpdated", "userDeletedMembers", "userProfileUpdated", "userProfileNoChange", "usersList", "apiChats"]
493
+ ChatResponse_Tag = Literal["acceptingContactRequest", "activeUser", "apiChat", "chatItemInfo", "chatItemNotChanged", "chatItemReaction", "chatItemUpdated", "chatItemsDeleted", "chatMsgContent", "chatRunning", "chatStarted", "chatStopped", "cmdOk", "chatCmdError", "connectionPlan", "contactAlreadyExists", "contactConnectionDeleted", "contactDeleted", "contactInfo", "contactPrefsUpdated", "contactRequestRejected", "contactsList", "groupDeletedUser", "groupLink", "groupLinkCreated", "groupLinkDeleted", "groupCreated", "publicGroupCreated", "publicGroupCreationFailed", "groupRelays", "groupRelaysAdded", "groupRelaysAddFailed", "relayGroupAllowed", "groupMembers", "groupUpdated", "groupsList", "groupDomainVerified", "invitation", "itemsReadForChat", "leftMemberUser", "memberAccepted", "membersBlockedForAllUser", "membersRoleUser", "networkConfig", "newChatItems", "newMemberContact", "newMemberContactSentInv", "newPreparedChat", "rcvFileAccepted", "rcvFileAcceptedSndCancelled", "rcvFileCancelled", "remoteCtrlConnected", "remoteCtrlConnecting", "sentConfirmation", "sentGroupInvitation", "sentGroupInvitationViaGroup", "sentInvitation", "sentInvitationToContact", "serviceReplyAccepted", "sndFileCancelled", "startedConnectionToContact", "startedConnectionToGroup", "userAcceptedGroupSent", "userContactLink", "userContactLinkCreated", "userContactLinkDeleted", "userContactLinkUpdated", "userDeletedMembers", "userProfileUpdated", "userProfileNoChange", "userServers", "usersList", "apiChats"]