diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ca0ca2..ed91b37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/beeper-desktop-api-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | @@ -46,7 +46,7 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/beeper-desktop-api-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | @@ -67,7 +67,7 @@ jobs: github.repository == 'stainless-sdks/beeper-desktop-api-python' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -87,7 +87,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/beeper-desktop-api-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 54361b5..4954ee6 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 2d24407..0fe0ce0 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'beeper/desktop-api-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8e76abb..4808d97 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.0.0" + ".": "5.1.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2dd3fee..3e06ccc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 30 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper/beeper-desktop-api-c08c14bb754b4cb0e02b21fabb680469368286be339dec0aaa8c69d04a1f021a.yml -openapi_spec_hash: a10246aaf7cdc33b682fc245bd5f893b -config_hash: 72f9d43b9b51a5da912e9f3730e53ae2 +configured_endpoints: 56 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper/beeper-desktop-api-baac187842e51587134950c59c4d746bfcb59239f01919ed83b92c24c47d98f4.yml +openapi_spec_hash: 9de80d05f7562b7ecd07c466f0fdf58b +config_hash: a8a4a8b869ccd5976fd4107e67d2ecae diff --git a/CHANGELOG.md b/CHANGELOG.md index 03fd832..81afe40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 5.1.0 (2026-05-17) + +Full Changelog: [v5.0.0...v5.1.0](https://github.com/beeper/desktop-api-python/compare/v5.0.0...v5.1.0) + +### Features + +* **api:** add app resource with login and verification endpoints ([9868f77](https://github.com/beeper/desktop-api-python/commit/9868f77423a998bfc443bc60fc00d6c7d0e58f53)) +* **api:** api update ([9c92ab4](https://github.com/beeper/desktop-api-python/commit/9c92ab490194b4640b4c152e197df698b87d465b)) +* **internal/types:** support eagerly validating pydantic iterators ([1850c8a](https://github.com/beeper/desktop-api-python/commit/1850c8a7b7d3e45407524a6ebffe902d4a3f8a71)) + + +### Bug Fixes + +* **client:** add missing f-string prefix in file type error message ([78424e2](https://github.com/beeper/desktop-api-python/commit/78424e24ad4e1f336f843222ff19bf70a50f073f)) + ## 5.0.0 (2026-05-06) Full Changelog: [v4.3.0...v5.0.0](https://github.com/beeper/desktop-api-python/compare/v4.3.0...v5.0.0) diff --git a/api.md b/api.md index 4818056..561079d 100644 --- a/api.md +++ b/api.md @@ -1,7 +1,15 @@ # Shared Types ```python -from beeper_desktop_api.types import Attachment, Error, Message, Reaction, User +from beeper_desktop_api.types import ( + APIError, + AppStateSnapshot, + Attachment, + Error, + Message, + Reaction, + User, +) ``` # BeeperDesktop @@ -22,11 +30,17 @@ Methods: Types: ```python -from beeper_desktop_api.types import Account, AccountListResponse +from beeper_desktop_api.types import ( + Account, + AccountBridge, + AccountRetrieveResponse, + AccountListResponse, +) ``` Methods: +- client.accounts.retrieve(account_id) -> AccountRetrieveResponse - client.accounts.list() -> AccountListResponse ## Contacts @@ -42,6 +56,65 @@ Methods: - client.accounts.contacts.list(account_id, \*\*params) -> SyncCursorSearch[User] - client.accounts.contacts.search(account_id, \*\*params) -> ContactSearchResponse +# Bridges + +Types: + +```python +from beeper_desktop_api.types import ( + Bridge, + CookieField, + DisappearingTimerCapability, + GroupFieldCapability, + GroupTypeCapabilities, + LoginFlow, + LoginInputField, + LoginSession, + ProvisioningCapabilities, + ResolveIdentifierCapabilities, + BridgeRetrieveResponse, + BridgeListResponse, +) +``` + +Methods: + +- client.bridges.retrieve(bridge_id) -> BridgeRetrieveResponse +- client.bridges.list() -> BridgeListResponse +- client.bridges.retrieve_capabilities(bridge_id) -> ProvisioningCapabilities + +## LoginFlows + +Types: + +```python +from beeper_desktop_api.types.bridges import LoginFlowListResponse +``` + +Methods: + +- client.bridges.login_flows.list(bridge_id) -> LoginFlowListResponse + +## LoginSessions + +Types: + +```python +from beeper_desktop_api.types.bridges import LoginSessionCancelResponse +``` + +Methods: + +- client.bridges.login_sessions.create(bridge_id, \*\*params) -> LoginSession +- client.bridges.login_sessions.retrieve(login_session_id, \*, bridge_id) -> LoginSession +- client.bridges.login_sessions.cancel(login_session_id, \*, bridge_id) -> LoginSessionCancelResponse + +### Steps + +Methods: + +- client.bridges.login_sessions.steps.submit(step_id, \*, bridge_id, login_session_id, \*\*params) -> LoginSession + # Chats Types: @@ -132,3 +205,112 @@ from beeper_desktop_api.types import InfoRetrieveResponse Methods: - client.info.retrieve() -> InfoRetrieveResponse + +# App + +Types: + +```python +from beeper_desktop_api.types import Verification, AppSessionResponse +``` + +Methods: + +- client.app.session() -> AppSessionResponse + +## Login + +Types: + +```python +from beeper_desktop_api.types.app import ( + LoginRegisterResponse, + LoginResponseResponse, + LoginStartResponse, +) +``` + +Methods: + +- client.app.login.email(\*\*params) -> None +- client.app.login.register(\*\*params) -> LoginRegisterResponse +- client.app.login.response(\*\*params) -> LoginResponseResponse +- client.app.login.start() -> LoginStartResponse + +### Verification + +#### RecoveryKey + +Types: + +```python +from beeper_desktop_api.types.app.login.verification import RecoveryKeyVerifyResponse +``` + +Methods: + +- client.app.login.verification.recovery_key.verify(\*\*params) -> RecoveryKeyVerifyResponse + +##### Reset + +Types: + +```python +from beeper_desktop_api.types.app.login.verification.recovery_key import ( + ResetCreateResponse, + ResetConfirmResponse, +) +``` + +Methods: + +- client.app.login.verification.recovery_key.reset.create(\*\*params) -> ResetCreateResponse +- client.app.login.verification.recovery_key.reset.confirm(\*\*params) -> ResetConfirmResponse + +## Verifications + +Types: + +```python +from beeper_desktop_api.types.app import ( + VerificationCreateResponse, + VerificationRetrieveResponse, + VerificationListResponse, + VerificationAcceptResponse, + VerificationCancelResponse, +) +``` + +Methods: + +- client.app.verifications.create(\*\*params) -> VerificationCreateResponse +- client.app.verifications.retrieve(verification_id) -> VerificationRetrieveResponse +- client.app.verifications.list() -> VerificationListResponse +- client.app.verifications.accept(verification_id) -> VerificationAcceptResponse +- client.app.verifications.cancel(verification_id, \*\*params) -> VerificationCancelResponse + +### Qr + +Types: + +```python +from beeper_desktop_api.types.app.verifications import QrConfirmScannedResponse, QrScanResponse +``` + +Methods: + +- client.app.verifications.qr.confirm_scanned(verification_id) -> QrConfirmScannedResponse +- client.app.verifications.qr.scan(\*\*params) -> QrScanResponse + +### SAS + +Types: + +```python +from beeper_desktop_api.types.app.verifications import SASConfirmResponse, SASStartResponse +``` + +Methods: + +- client.app.verifications.sas.confirm(verification_id) -> SASConfirmResponse +- client.app.verifications.sas.start(verification_id) -> SASStartResponse diff --git a/pyproject.toml b/pyproject.toml index 83d327e..5ce2088 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "beeper_desktop_api" -version = "5.0.0" +version = "5.1.0" description = "The official Python library for the beeperdesktop API" dynamic = ["readme"] license = "MIT" diff --git a/src/beeper_desktop_api/_client.py b/src/beeper_desktop_api/_client.py index 6c43fcb..49220c9 100644 --- a/src/beeper_desktop_api/_client.py +++ b/src/beeper_desktop_api/_client.py @@ -52,11 +52,13 @@ from .types.search_response import SearchResponse if TYPE_CHECKING: - from .resources import info, chats, assets, accounts, messages + from .resources import app, info, chats, assets, bridges, accounts, messages from .resources.info import InfoResource, AsyncInfoResource from .resources.assets import AssetsResource, AsyncAssetsResource + from .resources.app.app import AppResource, AsyncAppResource from .resources.messages import MessagesResource, AsyncMessagesResource from .resources.chats.chats import ChatsResource, AsyncChatsResource + from .resources.bridges.bridges import BridgesResource, AsyncBridgesResource from .resources.accounts.accounts import AccountsResource, AsyncAccountsResource __all__ = [ @@ -142,6 +144,13 @@ def accounts(self) -> AccountsResource: return AccountsResource(self) + @cached_property + def bridges(self) -> BridgesResource: + """Manage bridge-backed account types, connections, and login sessions""" + from .resources.bridges import BridgesResource + + return BridgesResource(self) + @cached_property def chats(self) -> ChatsResource: """Manage chats""" @@ -173,6 +182,13 @@ def info(self) -> InfoResource: return InfoResource(self) + @cached_property + def app(self) -> AppResource: + """Manage Beeper app login and encrypted messaging setup""" + from .resources.app import AppResource + + return AppResource(self) + @cached_property def with_raw_response(self) -> BeeperDesktopWithRawResponse: return BeeperDesktopWithRawResponse(self) @@ -272,14 +288,14 @@ def focus( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FocusResponse: """ - Focus Beeper Desktop and optionally navigate to a specific chat, message, or - pre-fill plain text and an image path. + Focus Beeper Desktop and optionally open a specific chat, jump to a message, or + pre-fill text and an image. Args: chat_id: Optional Beeper chat ID (or local chat ID) to focus after opening the app. If omitted, only opens/focuses the app. - draft_attachment_path: Optional image path to populate in the message input field. + draft_attachment_path: Optional local image path to populate in the message input field. draft_text: Optional plain text to populate in the message input field. @@ -322,12 +338,12 @@ def search( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchResponse: """ - Returns matching chats, participant name matches in groups, and the first page - of messages in one call. Paginate messages via search-messages. Paginate chats - via search-chats. + Return matching chats, participant matches in group chats, and the first page of + message results in one call. Use the dedicated chat and message search endpoints + for pagination. Args: - query: User-typed search text. Literal word matching (non-semantic). + query: User-typed search text. Uses literal word matching. extra_headers: Send extra headers @@ -454,6 +470,13 @@ def accounts(self) -> AsyncAccountsResource: return AsyncAccountsResource(self) + @cached_property + def bridges(self) -> AsyncBridgesResource: + """Manage bridge-backed account types, connections, and login sessions""" + from .resources.bridges import AsyncBridgesResource + + return AsyncBridgesResource(self) + @cached_property def chats(self) -> AsyncChatsResource: """Manage chats""" @@ -485,6 +508,13 @@ def info(self) -> AsyncInfoResource: return AsyncInfoResource(self) + @cached_property + def app(self) -> AsyncAppResource: + """Manage Beeper app login and encrypted messaging setup""" + from .resources.app import AsyncAppResource + + return AsyncAppResource(self) + @cached_property def with_raw_response(self) -> AsyncBeeperDesktopWithRawResponse: return AsyncBeeperDesktopWithRawResponse(self) @@ -584,14 +614,14 @@ async def focus( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FocusResponse: """ - Focus Beeper Desktop and optionally navigate to a specific chat, message, or - pre-fill plain text and an image path. + Focus Beeper Desktop and optionally open a specific chat, jump to a message, or + pre-fill text and an image. Args: chat_id: Optional Beeper chat ID (or local chat ID) to focus after opening the app. If omitted, only opens/focuses the app. - draft_attachment_path: Optional image path to populate in the message input field. + draft_attachment_path: Optional local image path to populate in the message input field. draft_text: Optional plain text to populate in the message input field. @@ -634,12 +664,12 @@ async def search( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchResponse: """ - Returns matching chats, participant name matches in groups, and the first page - of messages in one call. Paginate messages via search-messages. Paginate chats - via search-chats. + Return matching chats, participant matches in group chats, and the first page of + message results in one call. Use the dedicated chat and message search endpoints + for pagination. Args: - query: User-typed search text. Literal word matching (non-semantic). + query: User-typed search text. Uses literal word matching. extra_headers: Send extra headers @@ -715,6 +745,13 @@ def accounts(self) -> accounts.AccountsResourceWithRawResponse: return AccountsResourceWithRawResponse(self._client.accounts) + @cached_property + def bridges(self) -> bridges.BridgesResourceWithRawResponse: + """Manage bridge-backed account types, connections, and login sessions""" + from .resources.bridges import BridgesResourceWithRawResponse + + return BridgesResourceWithRawResponse(self._client.bridges) + @cached_property def chats(self) -> chats.ChatsResourceWithRawResponse: """Manage chats""" @@ -746,6 +783,13 @@ def info(self) -> info.InfoResourceWithRawResponse: return InfoResourceWithRawResponse(self._client.info) + @cached_property + def app(self) -> app.AppResourceWithRawResponse: + """Manage Beeper app login and encrypted messaging setup""" + from .resources.app import AppResourceWithRawResponse + + return AppResourceWithRawResponse(self._client.app) + class AsyncBeeperDesktopWithRawResponse: _client: AsyncBeeperDesktop @@ -767,6 +811,13 @@ def accounts(self) -> accounts.AsyncAccountsResourceWithRawResponse: return AsyncAccountsResourceWithRawResponse(self._client.accounts) + @cached_property + def bridges(self) -> bridges.AsyncBridgesResourceWithRawResponse: + """Manage bridge-backed account types, connections, and login sessions""" + from .resources.bridges import AsyncBridgesResourceWithRawResponse + + return AsyncBridgesResourceWithRawResponse(self._client.bridges) + @cached_property def chats(self) -> chats.AsyncChatsResourceWithRawResponse: """Manage chats""" @@ -798,6 +849,13 @@ def info(self) -> info.AsyncInfoResourceWithRawResponse: return AsyncInfoResourceWithRawResponse(self._client.info) + @cached_property + def app(self) -> app.AsyncAppResourceWithRawResponse: + """Manage Beeper app login and encrypted messaging setup""" + from .resources.app import AsyncAppResourceWithRawResponse + + return AsyncAppResourceWithRawResponse(self._client.app) + class BeeperDesktopWithStreamedResponse: _client: BeeperDesktop @@ -819,6 +877,13 @@ def accounts(self) -> accounts.AccountsResourceWithStreamingResponse: return AccountsResourceWithStreamingResponse(self._client.accounts) + @cached_property + def bridges(self) -> bridges.BridgesResourceWithStreamingResponse: + """Manage bridge-backed account types, connections, and login sessions""" + from .resources.bridges import BridgesResourceWithStreamingResponse + + return BridgesResourceWithStreamingResponse(self._client.bridges) + @cached_property def chats(self) -> chats.ChatsResourceWithStreamingResponse: """Manage chats""" @@ -850,6 +915,13 @@ def info(self) -> info.InfoResourceWithStreamingResponse: return InfoResourceWithStreamingResponse(self._client.info) + @cached_property + def app(self) -> app.AppResourceWithStreamingResponse: + """Manage Beeper app login and encrypted messaging setup""" + from .resources.app import AppResourceWithStreamingResponse + + return AppResourceWithStreamingResponse(self._client.app) + class AsyncBeeperDesktopWithStreamedResponse: _client: AsyncBeeperDesktop @@ -871,6 +943,13 @@ def accounts(self) -> accounts.AsyncAccountsResourceWithStreamingResponse: return AsyncAccountsResourceWithStreamingResponse(self._client.accounts) + @cached_property + def bridges(self) -> bridges.AsyncBridgesResourceWithStreamingResponse: + """Manage bridge-backed account types, connections, and login sessions""" + from .resources.bridges import AsyncBridgesResourceWithStreamingResponse + + return AsyncBridgesResourceWithStreamingResponse(self._client.bridges) + @cached_property def chats(self) -> chats.AsyncChatsResourceWithStreamingResponse: """Manage chats""" @@ -902,6 +981,13 @@ def info(self) -> info.AsyncInfoResourceWithStreamingResponse: return AsyncInfoResourceWithStreamingResponse(self._client.info) + @cached_property + def app(self) -> app.AsyncAppResourceWithStreamingResponse: + """Manage Beeper app login and encrypted messaging setup""" + from .resources.app import AsyncAppResourceWithStreamingResponse + + return AsyncAppResourceWithStreamingResponse(self._client.app) + Client = BeeperDesktop diff --git a/src/beeper_desktop_api/_files.py b/src/beeper_desktop_api/_files.py index 8a371d3..be8e0e1 100644 --- a/src/beeper_desktop_api/_files.py +++ b/src/beeper_desktop_api/_files.py @@ -99,7 +99,7 @@ async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles elif is_sequence_t(files): files = [(key, await _async_transform_file(file)) for key, file in files] else: - raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") return files diff --git a/src/beeper_desktop_api/_models.py b/src/beeper_desktop_api/_models.py index e22dd2a..69f41a6 100644 --- a/src/beeper_desktop_api/_models.py +++ b/src/beeper_desktop_api/_models.py @@ -25,7 +25,9 @@ ClassVar, Protocol, Required, + Annotated, ParamSpec, + TypeAlias, TypedDict, TypeGuard, final, @@ -79,7 +81,15 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: + from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler + from pydantic_core import CoreSchema, core_schema from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema +else: + try: + from pydantic_core import CoreSchema, core_schema + except ImportError: + CoreSchema = None + core_schema = None __all__ = ["BaseModel", "GenericModel"] @@ -396,6 +406,76 @@ def model_dump_json( ) +class _EagerIterable(list[_T], Generic[_T]): + """ + Accepts any Iterable[T] input (including generators), consumes it + eagerly, and validates all items upfront. + + Validation preserves the original container type where possible + (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) + always emits a list — round-tripping through model_dump() will not + restore the original container type. + """ + + @classmethod + def __get_pydantic_core_schema__( + cls, + source_type: Any, + handler: GetCoreSchemaHandler, + ) -> CoreSchema: + (item_type,) = get_args(source_type) or (Any,) + item_schema: CoreSchema = handler.generate_schema(item_type) + list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_of_items_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + cls._serialize, + info_arg=False, + ), + ) + + @staticmethod + def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: + original_type: type[Any] = type(v) + + # Normalize to list so list_schema can validate each item + if isinstance(v, list): + items: list[_T] = v + else: + try: + items = list(v) + except TypeError as e: + raise TypeError("Value is not iterable") from e + + # Validate items against the inner schema + validated: list[_T] = handler(items) + + # Reconstruct original container type + if original_type is list: + return validated + # str(list) produces the list's repr, not a string built from items, + # so skip reconstruction for str and its subclasses. + if issubclass(original_type, str): + return validated + try: + return original_type(validated) + except (TypeError, ValueError): + # If the type cannot be reconstructed, just return the validated list + return validated + + @staticmethod + def _serialize(v: Iterable[_T]) -> list[_T]: + """Always serialize as a list so Pydantic's JSON encoder is happy.""" + if isinstance(v, list): + return v + return list(v) + + +EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] + + def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) diff --git a/src/beeper_desktop_api/_version.py b/src/beeper_desktop_api/_version.py index 60fb169..701658d 100644 --- a/src/beeper_desktop_api/_version.py +++ b/src/beeper_desktop_api/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "beeper_desktop_api" -__version__ = "5.0.0" # x-release-please-version +__version__ = "5.1.0" # x-release-please-version diff --git a/src/beeper_desktop_api/resources/__init__.py b/src/beeper_desktop_api/resources/__init__.py index a066e9b..528054c 100644 --- a/src/beeper_desktop_api/resources/__init__.py +++ b/src/beeper_desktop_api/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .app import ( + AppResource, + AsyncAppResource, + AppResourceWithRawResponse, + AsyncAppResourceWithRawResponse, + AppResourceWithStreamingResponse, + AsyncAppResourceWithStreamingResponse, +) from .info import ( InfoResource, AsyncInfoResource, @@ -24,6 +32,14 @@ AssetsResourceWithStreamingResponse, AsyncAssetsResourceWithStreamingResponse, ) +from .bridges import ( + BridgesResource, + AsyncBridgesResource, + BridgesResourceWithRawResponse, + AsyncBridgesResourceWithRawResponse, + BridgesResourceWithStreamingResponse, + AsyncBridgesResourceWithStreamingResponse, +) from .accounts import ( AccountsResource, AsyncAccountsResource, @@ -48,6 +64,12 @@ "AsyncAccountsResourceWithRawResponse", "AccountsResourceWithStreamingResponse", "AsyncAccountsResourceWithStreamingResponse", + "BridgesResource", + "AsyncBridgesResource", + "BridgesResourceWithRawResponse", + "AsyncBridgesResourceWithRawResponse", + "BridgesResourceWithStreamingResponse", + "AsyncBridgesResourceWithStreamingResponse", "ChatsResource", "AsyncChatsResource", "ChatsResourceWithRawResponse", @@ -72,4 +94,10 @@ "AsyncInfoResourceWithRawResponse", "InfoResourceWithStreamingResponse", "AsyncInfoResourceWithStreamingResponse", + "AppResource", + "AsyncAppResource", + "AppResourceWithRawResponse", + "AsyncAppResourceWithRawResponse", + "AppResourceWithStreamingResponse", + "AsyncAppResourceWithStreamingResponse", ] diff --git a/src/beeper_desktop_api/resources/accounts/accounts.py b/src/beeper_desktop_api/resources/accounts/accounts.py index 15cfd33..8c247e4 100644 --- a/src/beeper_desktop_api/resources/accounts/accounts.py +++ b/src/beeper_desktop_api/resources/accounts/accounts.py @@ -5,6 +5,7 @@ import httpx from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template from .contacts import ( ContactsResource, AsyncContactsResource, @@ -23,6 +24,7 @@ ) from ..._base_client import make_request_options from ...types.account_list_response import AccountListResponse +from ...types.account_retrieve_response import AccountRetrieveResponse __all__ = ["AccountsResource", "AsyncAccountsResource"] @@ -54,6 +56,41 @@ def with_streaming_response(self) -> AccountsResourceWithStreamingResponse: """ return AccountsResourceWithStreamingResponse(self) + def retrieve( + self, + account_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AccountRetrieveResponse: + """ + Get one chat account connected to this Beeper Client API server. + + Args: + account_id: Account ID this resource belongs to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return self._get( + path_template("/v1/accounts/{account_id}", account_id=account_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccountRetrieveResponse, + ) + def list( self, *, @@ -65,8 +102,8 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AccountListResponse: """ - List Chat Accounts connected to this Beeper Desktop instance, including bridge - metadata and network identity. + List chat accounts connected to this Beeper Client API server, including bridge, + network, user identity, and connection status. """ return self._get( "/v1/accounts", @@ -104,6 +141,41 @@ def with_streaming_response(self) -> AsyncAccountsResourceWithStreamingResponse: """ return AsyncAccountsResourceWithStreamingResponse(self) + async def retrieve( + self, + account_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AccountRetrieveResponse: + """ + Get one chat account connected to this Beeper Client API server. + + Args: + account_id: Account ID this resource belongs to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return await self._get( + path_template("/v1/accounts/{account_id}", account_id=account_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccountRetrieveResponse, + ) + async def list( self, *, @@ -115,8 +187,8 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AccountListResponse: """ - List Chat Accounts connected to this Beeper Desktop instance, including bridge - metadata and network identity. + List chat accounts connected to this Beeper Client API server, including bridge, + network, user identity, and connection status. """ return await self._get( "/v1/accounts", @@ -131,6 +203,9 @@ class AccountsResourceWithRawResponse: def __init__(self, accounts: AccountsResource) -> None: self._accounts = accounts + self.retrieve = to_raw_response_wrapper( + accounts.retrieve, + ) self.list = to_raw_response_wrapper( accounts.list, ) @@ -145,6 +220,9 @@ class AsyncAccountsResourceWithRawResponse: def __init__(self, accounts: AsyncAccountsResource) -> None: self._accounts = accounts + self.retrieve = async_to_raw_response_wrapper( + accounts.retrieve, + ) self.list = async_to_raw_response_wrapper( accounts.list, ) @@ -159,6 +237,9 @@ class AccountsResourceWithStreamingResponse: def __init__(self, accounts: AccountsResource) -> None: self._accounts = accounts + self.retrieve = to_streamed_response_wrapper( + accounts.retrieve, + ) self.list = to_streamed_response_wrapper( accounts.list, ) @@ -173,6 +254,9 @@ class AsyncAccountsResourceWithStreamingResponse: def __init__(self, accounts: AsyncAccountsResource) -> None: self._accounts = accounts + self.retrieve = async_to_streamed_response_wrapper( + accounts.retrieve, + ) self.list = async_to_streamed_response_wrapper( accounts.list, ) diff --git a/src/beeper_desktop_api/resources/accounts/contacts.py b/src/beeper_desktop_api/resources/accounts/contacts.py index ba704bb..7c6469d 100644 --- a/src/beeper_desktop_api/resources/accounts/contacts.py +++ b/src/beeper_desktop_api/resources/accounts/contacts.py @@ -75,7 +75,7 @@ def list( limit: Maximum contacts to return per page. - query: Optional search query for blended contact lookup. + query: Optional search query for contact lookup. extra_headers: Send extra headers @@ -127,7 +127,7 @@ def search( Args: account_id: Account ID this resource belongs to. - query: Text to search users by. Network-specific behavior. + query: Text to search contacts by. Matching behavior depends on the network. extra_headers: Send extra headers @@ -202,7 +202,7 @@ def list( limit: Maximum contacts to return per page. - query: Optional search query for blended contact lookup. + query: Optional search query for contact lookup. extra_headers: Send extra headers @@ -254,7 +254,7 @@ async def search( Args: account_id: Account ID this resource belongs to. - query: Text to search users by. Network-specific behavior. + query: Text to search contacts by. Matching behavior depends on the network. extra_headers: Send extra headers diff --git a/src/beeper_desktop_api/resources/app/__init__.py b/src/beeper_desktop_api/resources/app/__init__.py new file mode 100644 index 0000000..68b246e --- /dev/null +++ b/src/beeper_desktop_api/resources/app/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .app import ( + AppResource, + AsyncAppResource, + AppResourceWithRawResponse, + AsyncAppResourceWithRawResponse, + AppResourceWithStreamingResponse, + AsyncAppResourceWithStreamingResponse, +) +from .login import ( + LoginResource, + AsyncLoginResource, + LoginResourceWithRawResponse, + AsyncLoginResourceWithRawResponse, + LoginResourceWithStreamingResponse, + AsyncLoginResourceWithStreamingResponse, +) +from .verifications import ( + VerificationsResource, + AsyncVerificationsResource, + VerificationsResourceWithRawResponse, + AsyncVerificationsResourceWithRawResponse, + VerificationsResourceWithStreamingResponse, + AsyncVerificationsResourceWithStreamingResponse, +) + +__all__ = [ + "LoginResource", + "AsyncLoginResource", + "LoginResourceWithRawResponse", + "AsyncLoginResourceWithRawResponse", + "LoginResourceWithStreamingResponse", + "AsyncLoginResourceWithStreamingResponse", + "VerificationsResource", + "AsyncVerificationsResource", + "VerificationsResourceWithRawResponse", + "AsyncVerificationsResourceWithRawResponse", + "VerificationsResourceWithStreamingResponse", + "AsyncVerificationsResourceWithStreamingResponse", + "AppResource", + "AsyncAppResource", + "AppResourceWithRawResponse", + "AsyncAppResourceWithRawResponse", + "AppResourceWithStreamingResponse", + "AsyncAppResourceWithStreamingResponse", +] diff --git a/src/beeper_desktop_api/resources/app/app.py b/src/beeper_desktop_api/resources/app/app.py new file mode 100644 index 0000000..e53e47f --- /dev/null +++ b/src/beeper_desktop_api/resources/app/app.py @@ -0,0 +1,223 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .login.login import ( + LoginResource, + AsyncLoginResource, + LoginResourceWithRawResponse, + AsyncLoginResourceWithRawResponse, + LoginResourceWithStreamingResponse, + AsyncLoginResourceWithStreamingResponse, +) +from ..._base_client import make_request_options +from .verifications.verifications import ( + VerificationsResource, + AsyncVerificationsResource, + VerificationsResourceWithRawResponse, + AsyncVerificationsResourceWithRawResponse, + VerificationsResourceWithStreamingResponse, + AsyncVerificationsResourceWithStreamingResponse, +) +from ...types.app_session_response import AppSessionResponse + +__all__ = ["AppResource", "AsyncAppResource"] + + +class AppResource(SyncAPIResource): + """Manage Beeper app login and encrypted messaging setup""" + + @cached_property + def login(self) -> LoginResource: + """Complete first-party Beeper app login""" + return LoginResource(self._client) + + @cached_property + def verifications(self) -> VerificationsResource: + """Manage device verification transactions""" + return VerificationsResource(self._client) + + @cached_property + def with_raw_response(self) -> AppResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AppResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AppResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AppResourceWithStreamingResponse(self) + + def session( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AppSessionResponse: + """ + Return the current Beeper Desktop or Beeper Server sign-in and encrypted + messaging setup state. This endpoint is public before sign-in so apps can + discover that sign-in is needed; after sign-in, pass a read token. + """ + return self._get( + "/v1/app/setup", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AppSessionResponse, + ) + + +class AsyncAppResource(AsyncAPIResource): + """Manage Beeper app login and encrypted messaging setup""" + + @cached_property + def login(self) -> AsyncLoginResource: + """Complete first-party Beeper app login""" + return AsyncLoginResource(self._client) + + @cached_property + def verifications(self) -> AsyncVerificationsResource: + """Manage device verification transactions""" + return AsyncVerificationsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAppResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncAppResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAppResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncAppResourceWithStreamingResponse(self) + + async def session( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AppSessionResponse: + """ + Return the current Beeper Desktop or Beeper Server sign-in and encrypted + messaging setup state. This endpoint is public before sign-in so apps can + discover that sign-in is needed; after sign-in, pass a read token. + """ + return await self._get( + "/v1/app/setup", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AppSessionResponse, + ) + + +class AppResourceWithRawResponse: + def __init__(self, app: AppResource) -> None: + self._app = app + + self.session = to_raw_response_wrapper( + app.session, + ) + + @cached_property + def login(self) -> LoginResourceWithRawResponse: + """Complete first-party Beeper app login""" + return LoginResourceWithRawResponse(self._app.login) + + @cached_property + def verifications(self) -> VerificationsResourceWithRawResponse: + """Manage device verification transactions""" + return VerificationsResourceWithRawResponse(self._app.verifications) + + +class AsyncAppResourceWithRawResponse: + def __init__(self, app: AsyncAppResource) -> None: + self._app = app + + self.session = async_to_raw_response_wrapper( + app.session, + ) + + @cached_property + def login(self) -> AsyncLoginResourceWithRawResponse: + """Complete first-party Beeper app login""" + return AsyncLoginResourceWithRawResponse(self._app.login) + + @cached_property + def verifications(self) -> AsyncVerificationsResourceWithRawResponse: + """Manage device verification transactions""" + return AsyncVerificationsResourceWithRawResponse(self._app.verifications) + + +class AppResourceWithStreamingResponse: + def __init__(self, app: AppResource) -> None: + self._app = app + + self.session = to_streamed_response_wrapper( + app.session, + ) + + @cached_property + def login(self) -> LoginResourceWithStreamingResponse: + """Complete first-party Beeper app login""" + return LoginResourceWithStreamingResponse(self._app.login) + + @cached_property + def verifications(self) -> VerificationsResourceWithStreamingResponse: + """Manage device verification transactions""" + return VerificationsResourceWithStreamingResponse(self._app.verifications) + + +class AsyncAppResourceWithStreamingResponse: + def __init__(self, app: AsyncAppResource) -> None: + self._app = app + + self.session = async_to_streamed_response_wrapper( + app.session, + ) + + @cached_property + def login(self) -> AsyncLoginResourceWithStreamingResponse: + """Complete first-party Beeper app login""" + return AsyncLoginResourceWithStreamingResponse(self._app.login) + + @cached_property + def verifications(self) -> AsyncVerificationsResourceWithStreamingResponse: + """Manage device verification transactions""" + return AsyncVerificationsResourceWithStreamingResponse(self._app.verifications) diff --git a/src/beeper_desktop_api/resources/app/login/__init__.py b/src/beeper_desktop_api/resources/app/login/__init__.py new file mode 100644 index 0000000..fa69ee9 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/login/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .login import ( + LoginResource, + AsyncLoginResource, + LoginResourceWithRawResponse, + AsyncLoginResourceWithRawResponse, + LoginResourceWithStreamingResponse, + AsyncLoginResourceWithStreamingResponse, +) +from .verification import ( + VerificationResource, + AsyncVerificationResource, + VerificationResourceWithRawResponse, + AsyncVerificationResourceWithRawResponse, + VerificationResourceWithStreamingResponse, + AsyncVerificationResourceWithStreamingResponse, +) + +__all__ = [ + "VerificationResource", + "AsyncVerificationResource", + "VerificationResourceWithRawResponse", + "AsyncVerificationResourceWithRawResponse", + "VerificationResourceWithStreamingResponse", + "AsyncVerificationResourceWithStreamingResponse", + "LoginResource", + "AsyncLoginResource", + "LoginResourceWithRawResponse", + "AsyncLoginResourceWithRawResponse", + "LoginResourceWithStreamingResponse", + "AsyncLoginResourceWithStreamingResponse", +] diff --git a/src/beeper_desktop_api/resources/app/login/login.py b/src/beeper_desktop_api/resources/app/login/login.py new file mode 100644 index 0000000..ed63a26 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/login/login.py @@ -0,0 +1,552 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import Literal + +import httpx + +from ...._types import Body, Query, Headers, NoneType, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....types.app import login_email_params, login_register_params, login_response_params +from ...._base_client import make_request_options +from .verification.verification import ( + VerificationResource, + AsyncVerificationResource, + VerificationResourceWithRawResponse, + AsyncVerificationResourceWithRawResponse, + VerificationResourceWithStreamingResponse, + AsyncVerificationResourceWithStreamingResponse, +) +from ....types.app.login_start_response import LoginStartResponse +from ....types.app.login_register_response import LoginRegisterResponse +from ....types.app.login_response_response import LoginResponseResponse + +__all__ = ["LoginResource", "AsyncLoginResource"] + + +class LoginResource(SyncAPIResource): + """Complete first-party Beeper app login""" + + @cached_property + def verification(self) -> VerificationResource: + return VerificationResource(self._client) + + @cached_property + def with_raw_response(self) -> LoginResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return LoginResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LoginResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return LoginResourceWithStreamingResponse(self) + + def email( + self, + *, + email: str, + setup_request_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Send a sign-in code to the user email address for app setup. + + Args: + email: Email address to send the sign-in code to. + + setup_request_id: Setup request ID returned by the start step. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + "/v1/app/setup/email", + body=maybe_transform( + { + "email": email, + "setup_request_id": setup_request_id, + }, + login_email_params.LoginEmailParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=NoneType, + ) + + def register( + self, + *, + accept_terms: Literal[True], + lead_token: str, + setup_request_id: str, + username: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginRegisterResponse: + """ + Create a Beeper account after the user chooses a username and accepts the Terms + of Use. + + Args: + accept_terms: Confirms that the user agreed to our + [terms of use](https://www.beeper.com/terms-onboarding) and has read our + [privacy policy](https://www.beeper.com/privacy). + + lead_token: Registration token returned by Beeper. + + setup_request_id: Setup request ID returned by the start step. + + username: Username selected by the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/app/setup/register", + body=maybe_transform( + { + "accept_terms": accept_terms, + "lead_token": lead_token, + "setup_request_id": setup_request_id, + "username": username, + }, + login_register_params.LoginRegisterParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=LoginRegisterResponse, + ) + + def response( + self, + *, + response: str, + setup_request_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginResponseResponse: + """Finish setup sign-in with the code sent to the user email address. + + If the user + needs a new account, the response includes account creation copy and username + suggestions. + + Args: + response: Sign-in code from the user email. + + setup_request_id: Setup request ID returned by the start step. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return cast( + LoginResponseResponse, + self._post( + "/v1/app/setup/response", + body=maybe_transform( + { + "response": response, + "setup_request_id": setup_request_id, + }, + login_response_params.LoginResponseParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=cast( + Any, LoginResponseResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + def start( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginStartResponse: + """Start setting up Beeper Desktop or Beeper Server. + + The flow supports existing + Beeper accounts and new account creation. + """ + return self._post( + "/v1/app/setup/start", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=LoginStartResponse, + ) + + +class AsyncLoginResource(AsyncAPIResource): + """Complete first-party Beeper app login""" + + @cached_property + def verification(self) -> AsyncVerificationResource: + return AsyncVerificationResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncLoginResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncLoginResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLoginResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncLoginResourceWithStreamingResponse(self) + + async def email( + self, + *, + email: str, + setup_request_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Send a sign-in code to the user email address for app setup. + + Args: + email: Email address to send the sign-in code to. + + setup_request_id: Setup request ID returned by the start step. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + "/v1/app/setup/email", + body=await async_maybe_transform( + { + "email": email, + "setup_request_id": setup_request_id, + }, + login_email_params.LoginEmailParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=NoneType, + ) + + async def register( + self, + *, + accept_terms: Literal[True], + lead_token: str, + setup_request_id: str, + username: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginRegisterResponse: + """ + Create a Beeper account after the user chooses a username and accepts the Terms + of Use. + + Args: + accept_terms: Confirms that the user agreed to our + [terms of use](https://www.beeper.com/terms-onboarding) and has read our + [privacy policy](https://www.beeper.com/privacy). + + lead_token: Registration token returned by Beeper. + + setup_request_id: Setup request ID returned by the start step. + + username: Username selected by the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/app/setup/register", + body=await async_maybe_transform( + { + "accept_terms": accept_terms, + "lead_token": lead_token, + "setup_request_id": setup_request_id, + "username": username, + }, + login_register_params.LoginRegisterParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=LoginRegisterResponse, + ) + + async def response( + self, + *, + response: str, + setup_request_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginResponseResponse: + """Finish setup sign-in with the code sent to the user email address. + + If the user + needs a new account, the response includes account creation copy and username + suggestions. + + Args: + response: Sign-in code from the user email. + + setup_request_id: Setup request ID returned by the start step. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return cast( + LoginResponseResponse, + await self._post( + "/v1/app/setup/response", + body=await async_maybe_transform( + { + "response": response, + "setup_request_id": setup_request_id, + }, + login_response_params.LoginResponseParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=cast( + Any, LoginResponseResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + async def start( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginStartResponse: + """Start setting up Beeper Desktop or Beeper Server. + + The flow supports existing + Beeper accounts and new account creation. + """ + return await self._post( + "/v1/app/setup/start", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={}, + ), + cast_to=LoginStartResponse, + ) + + +class LoginResourceWithRawResponse: + def __init__(self, login: LoginResource) -> None: + self._login = login + + self.email = to_raw_response_wrapper( + login.email, + ) + self.register = to_raw_response_wrapper( + login.register, + ) + self.response = to_raw_response_wrapper( + login.response, + ) + self.start = to_raw_response_wrapper( + login.start, + ) + + @cached_property + def verification(self) -> VerificationResourceWithRawResponse: + return VerificationResourceWithRawResponse(self._login.verification) + + +class AsyncLoginResourceWithRawResponse: + def __init__(self, login: AsyncLoginResource) -> None: + self._login = login + + self.email = async_to_raw_response_wrapper( + login.email, + ) + self.register = async_to_raw_response_wrapper( + login.register, + ) + self.response = async_to_raw_response_wrapper( + login.response, + ) + self.start = async_to_raw_response_wrapper( + login.start, + ) + + @cached_property + def verification(self) -> AsyncVerificationResourceWithRawResponse: + return AsyncVerificationResourceWithRawResponse(self._login.verification) + + +class LoginResourceWithStreamingResponse: + def __init__(self, login: LoginResource) -> None: + self._login = login + + self.email = to_streamed_response_wrapper( + login.email, + ) + self.register = to_streamed_response_wrapper( + login.register, + ) + self.response = to_streamed_response_wrapper( + login.response, + ) + self.start = to_streamed_response_wrapper( + login.start, + ) + + @cached_property + def verification(self) -> VerificationResourceWithStreamingResponse: + return VerificationResourceWithStreamingResponse(self._login.verification) + + +class AsyncLoginResourceWithStreamingResponse: + def __init__(self, login: AsyncLoginResource) -> None: + self._login = login + + self.email = async_to_streamed_response_wrapper( + login.email, + ) + self.register = async_to_streamed_response_wrapper( + login.register, + ) + self.response = async_to_streamed_response_wrapper( + login.response, + ) + self.start = async_to_streamed_response_wrapper( + login.start, + ) + + @cached_property + def verification(self) -> AsyncVerificationResourceWithStreamingResponse: + return AsyncVerificationResourceWithStreamingResponse(self._login.verification) diff --git a/src/beeper_desktop_api/resources/app/login/verification/__init__.py b/src/beeper_desktop_api/resources/app/login/verification/__init__.py new file mode 100644 index 0000000..05bb4b1 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/login/verification/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .recovery_key import ( + RecoveryKeyResource, + AsyncRecoveryKeyResource, + RecoveryKeyResourceWithRawResponse, + AsyncRecoveryKeyResourceWithRawResponse, + RecoveryKeyResourceWithStreamingResponse, + AsyncRecoveryKeyResourceWithStreamingResponse, +) +from .verification import ( + VerificationResource, + AsyncVerificationResource, + VerificationResourceWithRawResponse, + AsyncVerificationResourceWithRawResponse, + VerificationResourceWithStreamingResponse, + AsyncVerificationResourceWithStreamingResponse, +) + +__all__ = [ + "RecoveryKeyResource", + "AsyncRecoveryKeyResource", + "RecoveryKeyResourceWithRawResponse", + "AsyncRecoveryKeyResourceWithRawResponse", + "RecoveryKeyResourceWithStreamingResponse", + "AsyncRecoveryKeyResourceWithStreamingResponse", + "VerificationResource", + "AsyncVerificationResource", + "VerificationResourceWithRawResponse", + "AsyncVerificationResourceWithRawResponse", + "VerificationResourceWithStreamingResponse", + "AsyncVerificationResourceWithStreamingResponse", +] diff --git a/src/beeper_desktop_api/resources/app/login/verification/recovery_key/__init__.py b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/__init__.py new file mode 100644 index 0000000..ec6a210 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .reset import ( + ResetResource, + AsyncResetResource, + ResetResourceWithRawResponse, + AsyncResetResourceWithRawResponse, + ResetResourceWithStreamingResponse, + AsyncResetResourceWithStreamingResponse, +) +from .recovery_key import ( + RecoveryKeyResource, + AsyncRecoveryKeyResource, + RecoveryKeyResourceWithRawResponse, + AsyncRecoveryKeyResourceWithRawResponse, + RecoveryKeyResourceWithStreamingResponse, + AsyncRecoveryKeyResourceWithStreamingResponse, +) + +__all__ = [ + "ResetResource", + "AsyncResetResource", + "ResetResourceWithRawResponse", + "AsyncResetResourceWithRawResponse", + "ResetResourceWithStreamingResponse", + "AsyncResetResourceWithStreamingResponse", + "RecoveryKeyResource", + "AsyncRecoveryKeyResource", + "RecoveryKeyResourceWithRawResponse", + "AsyncRecoveryKeyResourceWithRawResponse", + "RecoveryKeyResourceWithStreamingResponse", + "AsyncRecoveryKeyResourceWithStreamingResponse", +] diff --git a/src/beeper_desktop_api/resources/app/login/verification/recovery_key/recovery_key.py b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/recovery_key.py new file mode 100644 index 0000000..ba347a5 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/recovery_key.py @@ -0,0 +1,227 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .reset import ( + ResetResource, + AsyncResetResource, + ResetResourceWithRawResponse, + AsyncResetResourceWithRawResponse, + ResetResourceWithStreamingResponse, + AsyncResetResourceWithStreamingResponse, +) +from ......_types import Body, Query, Headers, NotGiven, not_given +from ......_utils import maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ......_base_client import make_request_options +from ......types.app.login.verification import recovery_key_verify_params +from ......types.app.login.verification.recovery_key_verify_response import RecoveryKeyVerifyResponse + +__all__ = ["RecoveryKeyResource", "AsyncRecoveryKeyResource"] + + +class RecoveryKeyResource(SyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def reset(self) -> ResetResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return ResetResource(self._client) + + @cached_property + def with_raw_response(self) -> RecoveryKeyResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return RecoveryKeyResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RecoveryKeyResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return RecoveryKeyResourceWithStreamingResponse(self) + + def verify( + self, + *, + recovery_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RecoveryKeyVerifyResponse: + """ + Unlock encrypted messages with the user recovery key. + + Args: + recovery_key: Recovery key saved by the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/app/setup/verification/recovery-key", + body=maybe_transform({"recovery_key": recovery_key}, recovery_key_verify_params.RecoveryKeyVerifyParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RecoveryKeyVerifyResponse, + ) + + +class AsyncRecoveryKeyResource(AsyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def reset(self) -> AsyncResetResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncResetResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRecoveryKeyResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncRecoveryKeyResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRecoveryKeyResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncRecoveryKeyResourceWithStreamingResponse(self) + + async def verify( + self, + *, + recovery_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RecoveryKeyVerifyResponse: + """ + Unlock encrypted messages with the user recovery key. + + Args: + recovery_key: Recovery key saved by the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/app/setup/verification/recovery-key", + body=await async_maybe_transform( + {"recovery_key": recovery_key}, recovery_key_verify_params.RecoveryKeyVerifyParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RecoveryKeyVerifyResponse, + ) + + +class RecoveryKeyResourceWithRawResponse: + def __init__(self, recovery_key: RecoveryKeyResource) -> None: + self._recovery_key = recovery_key + + self.verify = to_raw_response_wrapper( + recovery_key.verify, + ) + + @cached_property + def reset(self) -> ResetResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return ResetResourceWithRawResponse(self._recovery_key.reset) + + +class AsyncRecoveryKeyResourceWithRawResponse: + def __init__(self, recovery_key: AsyncRecoveryKeyResource) -> None: + self._recovery_key = recovery_key + + self.verify = async_to_raw_response_wrapper( + recovery_key.verify, + ) + + @cached_property + def reset(self) -> AsyncResetResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncResetResourceWithRawResponse(self._recovery_key.reset) + + +class RecoveryKeyResourceWithStreamingResponse: + def __init__(self, recovery_key: RecoveryKeyResource) -> None: + self._recovery_key = recovery_key + + self.verify = to_streamed_response_wrapper( + recovery_key.verify, + ) + + @cached_property + def reset(self) -> ResetResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return ResetResourceWithStreamingResponse(self._recovery_key.reset) + + +class AsyncRecoveryKeyResourceWithStreamingResponse: + def __init__(self, recovery_key: AsyncRecoveryKeyResource) -> None: + self._recovery_key = recovery_key + + self.verify = async_to_streamed_response_wrapper( + recovery_key.verify, + ) + + @cached_property + def reset(self) -> AsyncResetResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncResetResourceWithStreamingResponse(self._recovery_key.reset) diff --git a/src/beeper_desktop_api/resources/app/login/verification/recovery_key/reset.py b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/reset.py new file mode 100644 index 0000000..0272f0f --- /dev/null +++ b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/reset.py @@ -0,0 +1,260 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ......_base_client import make_request_options +from ......types.app.login.verification.recovery_key import reset_create_params, reset_confirm_params +from ......types.app.login.verification.recovery_key.reset_create_response import ResetCreateResponse +from ......types.app.login.verification.recovery_key.reset_confirm_response import ResetConfirmResponse + +__all__ = ["ResetResource", "AsyncResetResource"] + + +class ResetResource(SyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def with_raw_response(self) -> ResetResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return ResetResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResetResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return ResetResourceWithStreamingResponse(self) + + def create( + self, + *, + existing_recovery_key: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ResetCreateResponse: + """ + Create a new recovery key when the user cannot use the existing one. + + Args: + existing_recovery_key: Existing recovery key, if the user has it. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/app/setup/verification/recovery-key/reset", + body=maybe_transform( + {"existing_recovery_key": existing_recovery_key}, reset_create_params.ResetCreateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ResetCreateResponse, + ) + + def confirm( + self, + *, + recovery_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ResetConfirmResponse: + """ + Confirm that the new recovery key should be used for this account. + + Args: + recovery_key: New recovery key returned by the reset step. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/app/setup/verification/recovery-key/reset/confirm", + body=maybe_transform({"recovery_key": recovery_key}, reset_confirm_params.ResetConfirmParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ResetConfirmResponse, + ) + + +class AsyncResetResource(AsyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def with_raw_response(self) -> AsyncResetResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncResetResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResetResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncResetResourceWithStreamingResponse(self) + + async def create( + self, + *, + existing_recovery_key: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ResetCreateResponse: + """ + Create a new recovery key when the user cannot use the existing one. + + Args: + existing_recovery_key: Existing recovery key, if the user has it. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/app/setup/verification/recovery-key/reset", + body=await async_maybe_transform( + {"existing_recovery_key": existing_recovery_key}, reset_create_params.ResetCreateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ResetCreateResponse, + ) + + async def confirm( + self, + *, + recovery_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ResetConfirmResponse: + """ + Confirm that the new recovery key should be used for this account. + + Args: + recovery_key: New recovery key returned by the reset step. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/app/setup/verification/recovery-key/reset/confirm", + body=await async_maybe_transform({"recovery_key": recovery_key}, reset_confirm_params.ResetConfirmParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ResetConfirmResponse, + ) + + +class ResetResourceWithRawResponse: + def __init__(self, reset: ResetResource) -> None: + self._reset = reset + + self.create = to_raw_response_wrapper( + reset.create, + ) + self.confirm = to_raw_response_wrapper( + reset.confirm, + ) + + +class AsyncResetResourceWithRawResponse: + def __init__(self, reset: AsyncResetResource) -> None: + self._reset = reset + + self.create = async_to_raw_response_wrapper( + reset.create, + ) + self.confirm = async_to_raw_response_wrapper( + reset.confirm, + ) + + +class ResetResourceWithStreamingResponse: + def __init__(self, reset: ResetResource) -> None: + self._reset = reset + + self.create = to_streamed_response_wrapper( + reset.create, + ) + self.confirm = to_streamed_response_wrapper( + reset.confirm, + ) + + +class AsyncResetResourceWithStreamingResponse: + def __init__(self, reset: AsyncResetResource) -> None: + self._reset = reset + + self.create = async_to_streamed_response_wrapper( + reset.create, + ) + self.confirm = async_to_streamed_response_wrapper( + reset.confirm, + ) diff --git a/src/beeper_desktop_api/resources/app/login/verification/verification.py b/src/beeper_desktop_api/resources/app/login/verification/verification.py new file mode 100644 index 0000000..9c940ce --- /dev/null +++ b/src/beeper_desktop_api/resources/app/login/verification/verification.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from .recovery_key.recovery_key import ( + RecoveryKeyResource, + AsyncRecoveryKeyResource, + RecoveryKeyResourceWithRawResponse, + AsyncRecoveryKeyResourceWithRawResponse, + RecoveryKeyResourceWithStreamingResponse, + AsyncRecoveryKeyResourceWithStreamingResponse, +) + +__all__ = ["VerificationResource", "AsyncVerificationResource"] + + +class VerificationResource(SyncAPIResource): + @cached_property + def recovery_key(self) -> RecoveryKeyResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return RecoveryKeyResource(self._client) + + @cached_property + def with_raw_response(self) -> VerificationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return VerificationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VerificationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return VerificationResourceWithStreamingResponse(self) + + +class AsyncVerificationResource(AsyncAPIResource): + @cached_property + def recovery_key(self) -> AsyncRecoveryKeyResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncRecoveryKeyResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVerificationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncVerificationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVerificationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncVerificationResourceWithStreamingResponse(self) + + +class VerificationResourceWithRawResponse: + def __init__(self, verification: VerificationResource) -> None: + self._verification = verification + + @cached_property + def recovery_key(self) -> RecoveryKeyResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return RecoveryKeyResourceWithRawResponse(self._verification.recovery_key) + + +class AsyncVerificationResourceWithRawResponse: + def __init__(self, verification: AsyncVerificationResource) -> None: + self._verification = verification + + @cached_property + def recovery_key(self) -> AsyncRecoveryKeyResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncRecoveryKeyResourceWithRawResponse(self._verification.recovery_key) + + +class VerificationResourceWithStreamingResponse: + def __init__(self, verification: VerificationResource) -> None: + self._verification = verification + + @cached_property + def recovery_key(self) -> RecoveryKeyResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return RecoveryKeyResourceWithStreamingResponse(self._verification.recovery_key) + + +class AsyncVerificationResourceWithStreamingResponse: + def __init__(self, verification: AsyncVerificationResource) -> None: + self._verification = verification + + @cached_property + def recovery_key(self) -> AsyncRecoveryKeyResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncRecoveryKeyResourceWithStreamingResponse(self._verification.recovery_key) diff --git a/src/beeper_desktop_api/resources/app/verifications/__init__.py b/src/beeper_desktop_api/resources/app/verifications/__init__.py new file mode 100644 index 0000000..b57241e --- /dev/null +++ b/src/beeper_desktop_api/resources/app/verifications/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .qr import ( + QrResource, + AsyncQrResource, + QrResourceWithRawResponse, + AsyncQrResourceWithRawResponse, + QrResourceWithStreamingResponse, + AsyncQrResourceWithStreamingResponse, +) +from .sas import ( + SASResource, + AsyncSASResource, + SASResourceWithRawResponse, + AsyncSASResourceWithRawResponse, + SASResourceWithStreamingResponse, + AsyncSASResourceWithStreamingResponse, +) +from .verifications import ( + VerificationsResource, + AsyncVerificationsResource, + VerificationsResourceWithRawResponse, + AsyncVerificationsResourceWithRawResponse, + VerificationsResourceWithStreamingResponse, + AsyncVerificationsResourceWithStreamingResponse, +) + +__all__ = [ + "QrResource", + "AsyncQrResource", + "QrResourceWithRawResponse", + "AsyncQrResourceWithRawResponse", + "QrResourceWithStreamingResponse", + "AsyncQrResourceWithStreamingResponse", + "SASResource", + "AsyncSASResource", + "SASResourceWithRawResponse", + "AsyncSASResourceWithRawResponse", + "SASResourceWithStreamingResponse", + "AsyncSASResourceWithStreamingResponse", + "VerificationsResource", + "AsyncVerificationsResource", + "VerificationsResourceWithRawResponse", + "AsyncVerificationsResourceWithRawResponse", + "VerificationsResourceWithStreamingResponse", + "AsyncVerificationsResourceWithStreamingResponse", +] diff --git a/src/beeper_desktop_api/resources/app/verifications/qr.py b/src/beeper_desktop_api/resources/app/verifications/qr.py new file mode 100644 index 0000000..d01a234 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/verifications/qr.py @@ -0,0 +1,262 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.app.verifications import qr_scan_params +from ....types.app.verifications.qr_scan_response import QrScanResponse +from ....types.app.verifications.qr_confirm_scanned_response import QrConfirmScannedResponse + +__all__ = ["QrResource", "AsyncQrResource"] + + +class QrResource(SyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def with_raw_response(self) -> QrResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return QrResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> QrResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return QrResourceWithStreamingResponse(self) + + def confirm_scanned( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> QrConfirmScannedResponse: + """ + Confirm that another device scanned this device QR code. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return self._post( + path_template( + "/v1/app/setup/verifications/{verification_id}/qr/confirm-scanned", verification_id=verification_id + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=QrConfirmScannedResponse, + ) + + def scan( + self, + *, + data: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> QrScanResponse: + """ + Submit the QR code scanned from another signed-in device. + + Args: + data: QR code payload scanned from the other device. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/app/setup/verifications/qr/scan", + body=maybe_transform({"data": data}, qr_scan_params.QrScanParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=QrScanResponse, + ) + + +class AsyncQrResource(AsyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def with_raw_response(self) -> AsyncQrResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncQrResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncQrResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncQrResourceWithStreamingResponse(self) + + async def confirm_scanned( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> QrConfirmScannedResponse: + """ + Confirm that another device scanned this device QR code. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return await self._post( + path_template( + "/v1/app/setup/verifications/{verification_id}/qr/confirm-scanned", verification_id=verification_id + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=QrConfirmScannedResponse, + ) + + async def scan( + self, + *, + data: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> QrScanResponse: + """ + Submit the QR code scanned from another signed-in device. + + Args: + data: QR code payload scanned from the other device. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/app/setup/verifications/qr/scan", + body=await async_maybe_transform({"data": data}, qr_scan_params.QrScanParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=QrScanResponse, + ) + + +class QrResourceWithRawResponse: + def __init__(self, qr: QrResource) -> None: + self._qr = qr + + self.confirm_scanned = to_raw_response_wrapper( + qr.confirm_scanned, + ) + self.scan = to_raw_response_wrapper( + qr.scan, + ) + + +class AsyncQrResourceWithRawResponse: + def __init__(self, qr: AsyncQrResource) -> None: + self._qr = qr + + self.confirm_scanned = async_to_raw_response_wrapper( + qr.confirm_scanned, + ) + self.scan = async_to_raw_response_wrapper( + qr.scan, + ) + + +class QrResourceWithStreamingResponse: + def __init__(self, qr: QrResource) -> None: + self._qr = qr + + self.confirm_scanned = to_streamed_response_wrapper( + qr.confirm_scanned, + ) + self.scan = to_streamed_response_wrapper( + qr.scan, + ) + + +class AsyncQrResourceWithStreamingResponse: + def __init__(self, qr: AsyncQrResource) -> None: + self._qr = qr + + self.confirm_scanned = async_to_streamed_response_wrapper( + qr.confirm_scanned, + ) + self.scan = async_to_streamed_response_wrapper( + qr.scan, + ) diff --git a/src/beeper_desktop_api/resources/app/verifications/sas.py b/src/beeper_desktop_api/resources/app/verifications/sas.py new file mode 100644 index 0000000..f5e5143 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/verifications/sas.py @@ -0,0 +1,259 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.app.verifications.sas_start_response import SASStartResponse +from ....types.app.verifications.sas_confirm_response import SASConfirmResponse + +__all__ = ["SASResource", "AsyncSASResource"] + + +class SASResource(SyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def with_raw_response(self) -> SASResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return SASResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SASResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return SASResourceWithStreamingResponse(self) + + def confirm( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SASConfirmResponse: + """ + Confirm that the emoji or number sequence matches on both devices. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return self._post( + path_template("/v1/app/setup/verifications/{verification_id}/sas/confirm", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SASConfirmResponse, + ) + + def start( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SASStartResponse: + """ + Start emoji comparison for device verification. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return self._post( + path_template("/v1/app/setup/verifications/{verification_id}/sas/start", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SASStartResponse, + ) + + +class AsyncSASResource(AsyncAPIResource): + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + + @cached_property + def with_raw_response(self) -> AsyncSASResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncSASResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSASResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncSASResourceWithStreamingResponse(self) + + async def confirm( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SASConfirmResponse: + """ + Confirm that the emoji or number sequence matches on both devices. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return await self._post( + path_template("/v1/app/setup/verifications/{verification_id}/sas/confirm", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SASConfirmResponse, + ) + + async def start( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SASStartResponse: + """ + Start emoji comparison for device verification. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return await self._post( + path_template("/v1/app/setup/verifications/{verification_id}/sas/start", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SASStartResponse, + ) + + +class SASResourceWithRawResponse: + def __init__(self, sas: SASResource) -> None: + self._sas = sas + + self.confirm = to_raw_response_wrapper( + sas.confirm, + ) + self.start = to_raw_response_wrapper( + sas.start, + ) + + +class AsyncSASResourceWithRawResponse: + def __init__(self, sas: AsyncSASResource) -> None: + self._sas = sas + + self.confirm = async_to_raw_response_wrapper( + sas.confirm, + ) + self.start = async_to_raw_response_wrapper( + sas.start, + ) + + +class SASResourceWithStreamingResponse: + def __init__(self, sas: SASResource) -> None: + self._sas = sas + + self.confirm = to_streamed_response_wrapper( + sas.confirm, + ) + self.start = to_streamed_response_wrapper( + sas.start, + ) + + +class AsyncSASResourceWithStreamingResponse: + def __init__(self, sas: AsyncSASResource) -> None: + self._sas = sas + + self.confirm = async_to_streamed_response_wrapper( + sas.confirm, + ) + self.start = async_to_streamed_response_wrapper( + sas.start, + ) diff --git a/src/beeper_desktop_api/resources/app/verifications/verifications.py b/src/beeper_desktop_api/resources/app/verifications/verifications.py new file mode 100644 index 0000000..3854036 --- /dev/null +++ b/src/beeper_desktop_api/resources/app/verifications/verifications.py @@ -0,0 +1,625 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .qr import ( + QrResource, + AsyncQrResource, + QrResourceWithRawResponse, + AsyncQrResourceWithRawResponse, + QrResourceWithStreamingResponse, + AsyncQrResourceWithStreamingResponse, +) +from .sas import ( + SASResource, + AsyncSASResource, + SASResourceWithRawResponse, + AsyncSASResourceWithRawResponse, + SASResourceWithStreamingResponse, + AsyncSASResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....types.app import verification_cancel_params, verification_create_params +from ...._base_client import make_request_options +from ....types.app.verification_list_response import VerificationListResponse +from ....types.app.verification_accept_response import VerificationAcceptResponse +from ....types.app.verification_cancel_response import VerificationCancelResponse +from ....types.app.verification_create_response import VerificationCreateResponse +from ....types.app.verification_retrieve_response import VerificationRetrieveResponse + +__all__ = ["VerificationsResource", "AsyncVerificationsResource"] + + +class VerificationsResource(SyncAPIResource): + """Manage device verification transactions""" + + @cached_property + def qr(self) -> QrResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return QrResource(self._client) + + @cached_property + def sas(self) -> SASResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return SASResource(self._client) + + @cached_property + def with_raw_response(self) -> VerificationsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return VerificationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VerificationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return VerificationsResourceWithStreamingResponse(self) + + def create( + self, + *, + purpose: Literal["login", "device"] | Omit = omit, + user_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationCreateResponse: + """ + Start verifying this device from another signed-in device. + + Args: + purpose: Why this verification is being started. + + user_id: Beeper user ID to verify. Defaults to the signed-in user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/app/setup/verifications", + body=maybe_transform( + { + "purpose": purpose, + "user_id": user_id, + }, + verification_create_params.VerificationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationCreateResponse, + ) + + def retrieve( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationRetrieveResponse: + """ + Get the current state of a device verification transaction. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return self._get( + path_template("/v1/app/setup/verifications/{verification_id}", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationRetrieveResponse, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationListResponse: + """List pending and active device verifications. + + Use this to recover state without + a WebSocket connection. + """ + return self._get( + "/v1/app/setup/verifications", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationListResponse, + ) + + def accept( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationAcceptResponse: + """ + Accept an incoming device verification request. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return self._post( + path_template("/v1/app/setup/verifications/{verification_id}/accept", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationAcceptResponse, + ) + + def cancel( + self, + verification_id: str, + *, + code: str | Omit = omit, + reason: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationCancelResponse: + """ + Cancel an active device verification request. + + Args: + verification_id: Verification ID. + + code: Optional cancellation code. + + reason: Optional user-facing cancellation reason. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return self._post( + path_template("/v1/app/setup/verifications/{verification_id}/cancel", verification_id=verification_id), + body=maybe_transform( + { + "code": code, + "reason": reason, + }, + verification_cancel_params.VerificationCancelParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationCancelResponse, + ) + + +class AsyncVerificationsResource(AsyncAPIResource): + """Manage device verification transactions""" + + @cached_property + def qr(self) -> AsyncQrResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncQrResource(self._client) + + @cached_property + def sas(self) -> AsyncSASResource: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncSASResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVerificationsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncVerificationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVerificationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncVerificationsResourceWithStreamingResponse(self) + + async def create( + self, + *, + purpose: Literal["login", "device"] | Omit = omit, + user_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationCreateResponse: + """ + Start verifying this device from another signed-in device. + + Args: + purpose: Why this verification is being started. + + user_id: Beeper user ID to verify. Defaults to the signed-in user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/app/setup/verifications", + body=await async_maybe_transform( + { + "purpose": purpose, + "user_id": user_id, + }, + verification_create_params.VerificationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationCreateResponse, + ) + + async def retrieve( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationRetrieveResponse: + """ + Get the current state of a device verification transaction. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return await self._get( + path_template("/v1/app/setup/verifications/{verification_id}", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationRetrieveResponse, + ) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationListResponse: + """List pending and active device verifications. + + Use this to recover state without + a WebSocket connection. + """ + return await self._get( + "/v1/app/setup/verifications", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationListResponse, + ) + + async def accept( + self, + verification_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationAcceptResponse: + """ + Accept an incoming device verification request. + + Args: + verification_id: Verification ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return await self._post( + path_template("/v1/app/setup/verifications/{verification_id}/accept", verification_id=verification_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationAcceptResponse, + ) + + async def cancel( + self, + verification_id: str, + *, + code: str | Omit = omit, + reason: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VerificationCancelResponse: + """ + Cancel an active device verification request. + + Args: + verification_id: Verification ID. + + code: Optional cancellation code. + + reason: Optional user-facing cancellation reason. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not verification_id: + raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}") + return await self._post( + path_template("/v1/app/setup/verifications/{verification_id}/cancel", verification_id=verification_id), + body=await async_maybe_transform( + { + "code": code, + "reason": reason, + }, + verification_cancel_params.VerificationCancelParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VerificationCancelResponse, + ) + + +class VerificationsResourceWithRawResponse: + def __init__(self, verifications: VerificationsResource) -> None: + self._verifications = verifications + + self.create = to_raw_response_wrapper( + verifications.create, + ) + self.retrieve = to_raw_response_wrapper( + verifications.retrieve, + ) + self.list = to_raw_response_wrapper( + verifications.list, + ) + self.accept = to_raw_response_wrapper( + verifications.accept, + ) + self.cancel = to_raw_response_wrapper( + verifications.cancel, + ) + + @cached_property + def qr(self) -> QrResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return QrResourceWithRawResponse(self._verifications.qr) + + @cached_property + def sas(self) -> SASResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return SASResourceWithRawResponse(self._verifications.sas) + + +class AsyncVerificationsResourceWithRawResponse: + def __init__(self, verifications: AsyncVerificationsResource) -> None: + self._verifications = verifications + + self.create = async_to_raw_response_wrapper( + verifications.create, + ) + self.retrieve = async_to_raw_response_wrapper( + verifications.retrieve, + ) + self.list = async_to_raw_response_wrapper( + verifications.list, + ) + self.accept = async_to_raw_response_wrapper( + verifications.accept, + ) + self.cancel = async_to_raw_response_wrapper( + verifications.cancel, + ) + + @cached_property + def qr(self) -> AsyncQrResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncQrResourceWithRawResponse(self._verifications.qr) + + @cached_property + def sas(self) -> AsyncSASResourceWithRawResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncSASResourceWithRawResponse(self._verifications.sas) + + +class VerificationsResourceWithStreamingResponse: + def __init__(self, verifications: VerificationsResource) -> None: + self._verifications = verifications + + self.create = to_streamed_response_wrapper( + verifications.create, + ) + self.retrieve = to_streamed_response_wrapper( + verifications.retrieve, + ) + self.list = to_streamed_response_wrapper( + verifications.list, + ) + self.accept = to_streamed_response_wrapper( + verifications.accept, + ) + self.cancel = to_streamed_response_wrapper( + verifications.cancel, + ) + + @cached_property + def qr(self) -> QrResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return QrResourceWithStreamingResponse(self._verifications.qr) + + @cached_property + def sas(self) -> SASResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return SASResourceWithStreamingResponse(self._verifications.sas) + + +class AsyncVerificationsResourceWithStreamingResponse: + def __init__(self, verifications: AsyncVerificationsResource) -> None: + self._verifications = verifications + + self.create = async_to_streamed_response_wrapper( + verifications.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + verifications.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + verifications.list, + ) + self.accept = async_to_streamed_response_wrapper( + verifications.accept, + ) + self.cancel = async_to_streamed_response_wrapper( + verifications.cancel, + ) + + @cached_property + def qr(self) -> AsyncQrResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncQrResourceWithStreamingResponse(self._verifications.qr) + + @cached_property + def sas(self) -> AsyncSASResourceWithStreamingResponse: + """ + First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server. + """ + return AsyncSASResourceWithStreamingResponse(self._verifications.sas) diff --git a/src/beeper_desktop_api/resources/assets.py b/src/beeper_desktop_api/resources/assets.py index ccfeab4..a67081f 100644 --- a/src/beeper_desktop_api/resources/assets.py +++ b/src/beeper_desktop_api/resources/assets.py @@ -68,11 +68,11 @@ def download( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AssetDownloadResponse: """ - Download a Matrix file using its mxc:// or localmxc:// URL to the device running - Beeper Desktop and return the local file URL. + Download a file from an mxc:// or localmxc:// URL to the device running the + Beeper Client API and return the local file URL. Args: - url: Matrix content URL (mxc:// or localmxc://) for the file to download. + url: Beeper media URL (mxc:// or localmxc://) for the file to download. extra_headers: Send extra headers @@ -147,7 +147,7 @@ def upload( """Upload a file to a temporary location using multipart/form-data. Returns an - uploadID that can be referenced when sending a message or materializing a draft + uploadID that can be referenced when sending a message or creating a draft attachment. Args: @@ -204,8 +204,8 @@ def upload_base64( """Upload a file using a JSON body with base64-encoded content. Returns an uploadID - that can be referenced when sending a message or materializing a draft - attachment. Alternative to the multipart upload endpoint. + that can be referenced when sending a message or creating a draft attachment. + Alternative to the multipart upload endpoint. Args: content: Base64-encoded file content (max ~500MB decoded) @@ -273,11 +273,11 @@ async def download( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AssetDownloadResponse: """ - Download a Matrix file using its mxc:// or localmxc:// URL to the device running - Beeper Desktop and return the local file URL. + Download a file from an mxc:// or localmxc:// URL to the device running the + Beeper Client API and return the local file URL. Args: - url: Matrix content URL (mxc:// or localmxc://) for the file to download. + url: Beeper media URL (mxc:// or localmxc://) for the file to download. extra_headers: Send extra headers @@ -352,7 +352,7 @@ async def upload( """Upload a file to a temporary location using multipart/form-data. Returns an - uploadID that can be referenced when sending a message or materializing a draft + uploadID that can be referenced when sending a message or creating a draft attachment. Args: @@ -409,8 +409,8 @@ async def upload_base64( """Upload a file using a JSON body with base64-encoded content. Returns an uploadID - that can be referenced when sending a message or materializing a draft - attachment. Alternative to the multipart upload endpoint. + that can be referenced when sending a message or creating a draft attachment. + Alternative to the multipart upload endpoint. Args: content: Base64-encoded file content (max ~500MB decoded) diff --git a/src/beeper_desktop_api/resources/bridges/__init__.py b/src/beeper_desktop_api/resources/bridges/__init__.py new file mode 100644 index 0000000..e0e3ac7 --- /dev/null +++ b/src/beeper_desktop_api/resources/bridges/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .bridges import ( + BridgesResource, + AsyncBridgesResource, + BridgesResourceWithRawResponse, + AsyncBridgesResourceWithRawResponse, + BridgesResourceWithStreamingResponse, + AsyncBridgesResourceWithStreamingResponse, +) +from .login_flows import ( + LoginFlowsResource, + AsyncLoginFlowsResource, + LoginFlowsResourceWithRawResponse, + AsyncLoginFlowsResourceWithRawResponse, + LoginFlowsResourceWithStreamingResponse, + AsyncLoginFlowsResourceWithStreamingResponse, +) +from .login_sessions import ( + LoginSessionsResource, + AsyncLoginSessionsResource, + LoginSessionsResourceWithRawResponse, + AsyncLoginSessionsResourceWithRawResponse, + LoginSessionsResourceWithStreamingResponse, + AsyncLoginSessionsResourceWithStreamingResponse, +) + +__all__ = [ + "LoginFlowsResource", + "AsyncLoginFlowsResource", + "LoginFlowsResourceWithRawResponse", + "AsyncLoginFlowsResourceWithRawResponse", + "LoginFlowsResourceWithStreamingResponse", + "AsyncLoginFlowsResourceWithStreamingResponse", + "LoginSessionsResource", + "AsyncLoginSessionsResource", + "LoginSessionsResourceWithRawResponse", + "AsyncLoginSessionsResourceWithRawResponse", + "LoginSessionsResourceWithStreamingResponse", + "AsyncLoginSessionsResourceWithStreamingResponse", + "BridgesResource", + "AsyncBridgesResource", + "BridgesResourceWithRawResponse", + "AsyncBridgesResourceWithRawResponse", + "BridgesResourceWithStreamingResponse", + "AsyncBridgesResourceWithStreamingResponse", +] diff --git a/src/beeper_desktop_api/resources/bridges/bridges.py b/src/beeper_desktop_api/resources/bridges/bridges.py new file mode 100644 index 0000000..c34145c --- /dev/null +++ b/src/beeper_desktop_api/resources/bridges/bridges.py @@ -0,0 +1,420 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .login_flows import ( + LoginFlowsResource, + AsyncLoginFlowsResource, + LoginFlowsResourceWithRawResponse, + AsyncLoginFlowsResourceWithRawResponse, + LoginFlowsResourceWithStreamingResponse, + AsyncLoginFlowsResourceWithStreamingResponse, +) +from ..._base_client import make_request_options +from ...types.bridge_list_response import BridgeListResponse +from .login_sessions.login_sessions import ( + LoginSessionsResource, + AsyncLoginSessionsResource, + LoginSessionsResourceWithRawResponse, + AsyncLoginSessionsResourceWithRawResponse, + LoginSessionsResourceWithStreamingResponse, + AsyncLoginSessionsResourceWithStreamingResponse, +) +from ...types.bridge_retrieve_response import BridgeRetrieveResponse +from ...types.provisioning_capabilities import ProvisioningCapabilities + +__all__ = ["BridgesResource", "AsyncBridgesResource"] + + +class BridgesResource(SyncAPIResource): + """Manage bridge-backed account types, connections, and login sessions""" + + @cached_property + def login_flows(self) -> LoginFlowsResource: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return LoginFlowsResource(self._client) + + @cached_property + def login_sessions(self) -> LoginSessionsResource: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return LoginSessionsResource(self._client) + + @cached_property + def with_raw_response(self) -> BridgesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return BridgesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BridgesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return BridgesResourceWithStreamingResponse(self) + + def retrieve( + self, + bridge_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BridgeRetrieveResponse: + """ + Get one bridge, including the chat accounts connected through it. + + Args: + bridge_id: Bridge ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return self._get( + path_template("/v1/bridges/{bridge_id}", bridge_id=bridge_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BridgeRetrieveResponse, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BridgeListResponse: + """List available bridges. + + A bridge is a chat-network connector that can connect or + reconnect chat accounts. Connected accounts use the same Account schema as GET + /v1/accounts. + """ + return self._get( + "/v1/bridges", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BridgeListResponse, + ) + + def retrieve_capabilities( + self, + bridge_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProvisioningCapabilities: + """Get advanced network capabilities for a bridge. + + This endpoint is intended for + clients that build custom connect or chat-creation flows. + + Args: + bridge_id: Bridge ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return self._get( + path_template("/v1/bridges/{bridge_id}/capabilities", bridge_id=bridge_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProvisioningCapabilities, + ) + + +class AsyncBridgesResource(AsyncAPIResource): + """Manage bridge-backed account types, connections, and login sessions""" + + @cached_property + def login_flows(self) -> AsyncLoginFlowsResource: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncLoginFlowsResource(self._client) + + @cached_property + def login_sessions(self) -> AsyncLoginSessionsResource: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncLoginSessionsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncBridgesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncBridgesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBridgesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncBridgesResourceWithStreamingResponse(self) + + async def retrieve( + self, + bridge_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BridgeRetrieveResponse: + """ + Get one bridge, including the chat accounts connected through it. + + Args: + bridge_id: Bridge ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return await self._get( + path_template("/v1/bridges/{bridge_id}", bridge_id=bridge_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BridgeRetrieveResponse, + ) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BridgeListResponse: + """List available bridges. + + A bridge is a chat-network connector that can connect or + reconnect chat accounts. Connected accounts use the same Account schema as GET + /v1/accounts. + """ + return await self._get( + "/v1/bridges", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BridgeListResponse, + ) + + async def retrieve_capabilities( + self, + bridge_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProvisioningCapabilities: + """Get advanced network capabilities for a bridge. + + This endpoint is intended for + clients that build custom connect or chat-creation flows. + + Args: + bridge_id: Bridge ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return await self._get( + path_template("/v1/bridges/{bridge_id}/capabilities", bridge_id=bridge_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProvisioningCapabilities, + ) + + +class BridgesResourceWithRawResponse: + def __init__(self, bridges: BridgesResource) -> None: + self._bridges = bridges + + self.retrieve = to_raw_response_wrapper( + bridges.retrieve, + ) + self.list = to_raw_response_wrapper( + bridges.list, + ) + self.retrieve_capabilities = to_raw_response_wrapper( + bridges.retrieve_capabilities, + ) + + @cached_property + def login_flows(self) -> LoginFlowsResourceWithRawResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return LoginFlowsResourceWithRawResponse(self._bridges.login_flows) + + @cached_property + def login_sessions(self) -> LoginSessionsResourceWithRawResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return LoginSessionsResourceWithRawResponse(self._bridges.login_sessions) + + +class AsyncBridgesResourceWithRawResponse: + def __init__(self, bridges: AsyncBridgesResource) -> None: + self._bridges = bridges + + self.retrieve = async_to_raw_response_wrapper( + bridges.retrieve, + ) + self.list = async_to_raw_response_wrapper( + bridges.list, + ) + self.retrieve_capabilities = async_to_raw_response_wrapper( + bridges.retrieve_capabilities, + ) + + @cached_property + def login_flows(self) -> AsyncLoginFlowsResourceWithRawResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncLoginFlowsResourceWithRawResponse(self._bridges.login_flows) + + @cached_property + def login_sessions(self) -> AsyncLoginSessionsResourceWithRawResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncLoginSessionsResourceWithRawResponse(self._bridges.login_sessions) + + +class BridgesResourceWithStreamingResponse: + def __init__(self, bridges: BridgesResource) -> None: + self._bridges = bridges + + self.retrieve = to_streamed_response_wrapper( + bridges.retrieve, + ) + self.list = to_streamed_response_wrapper( + bridges.list, + ) + self.retrieve_capabilities = to_streamed_response_wrapper( + bridges.retrieve_capabilities, + ) + + @cached_property + def login_flows(self) -> LoginFlowsResourceWithStreamingResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return LoginFlowsResourceWithStreamingResponse(self._bridges.login_flows) + + @cached_property + def login_sessions(self) -> LoginSessionsResourceWithStreamingResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return LoginSessionsResourceWithStreamingResponse(self._bridges.login_sessions) + + +class AsyncBridgesResourceWithStreamingResponse: + def __init__(self, bridges: AsyncBridgesResource) -> None: + self._bridges = bridges + + self.retrieve = async_to_streamed_response_wrapper( + bridges.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + bridges.list, + ) + self.retrieve_capabilities = async_to_streamed_response_wrapper( + bridges.retrieve_capabilities, + ) + + @cached_property + def login_flows(self) -> AsyncLoginFlowsResourceWithStreamingResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncLoginFlowsResourceWithStreamingResponse(self._bridges.login_flows) + + @cached_property + def login_sessions(self) -> AsyncLoginSessionsResourceWithStreamingResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncLoginSessionsResourceWithStreamingResponse(self._bridges.login_sessions) diff --git a/src/beeper_desktop_api/resources/bridges/login_flows.py b/src/beeper_desktop_api/resources/bridges/login_flows.py new file mode 100644 index 0000000..bc14e73 --- /dev/null +++ b/src/beeper_desktop_api/resources/bridges/login_flows.py @@ -0,0 +1,180 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.bridges.login_flow_list_response import LoginFlowListResponse + +__all__ = ["LoginFlowsResource", "AsyncLoginFlowsResource"] + + +class LoginFlowsResource(SyncAPIResource): + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + + @cached_property + def with_raw_response(self) -> LoginFlowsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return LoginFlowsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LoginFlowsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return LoginFlowsResourceWithStreamingResponse(self) + + def list( + self, + bridge_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginFlowListResponse: + """List connect and reconnect flow options for a bridge. + + Use a flowID when creating + a bridge login session. + + Args: + bridge_id: Bridge ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return self._get( + path_template("/v1/bridges/{bridge_id}/login-flows", bridge_id=bridge_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginFlowListResponse, + ) + + +class AsyncLoginFlowsResource(AsyncAPIResource): + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + + @cached_property + def with_raw_response(self) -> AsyncLoginFlowsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncLoginFlowsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLoginFlowsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncLoginFlowsResourceWithStreamingResponse(self) + + async def list( + self, + bridge_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginFlowListResponse: + """List connect and reconnect flow options for a bridge. + + Use a flowID when creating + a bridge login session. + + Args: + bridge_id: Bridge ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return await self._get( + path_template("/v1/bridges/{bridge_id}/login-flows", bridge_id=bridge_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginFlowListResponse, + ) + + +class LoginFlowsResourceWithRawResponse: + def __init__(self, login_flows: LoginFlowsResource) -> None: + self._login_flows = login_flows + + self.list = to_raw_response_wrapper( + login_flows.list, + ) + + +class AsyncLoginFlowsResourceWithRawResponse: + def __init__(self, login_flows: AsyncLoginFlowsResource) -> None: + self._login_flows = login_flows + + self.list = async_to_raw_response_wrapper( + login_flows.list, + ) + + +class LoginFlowsResourceWithStreamingResponse: + def __init__(self, login_flows: LoginFlowsResource) -> None: + self._login_flows = login_flows + + self.list = to_streamed_response_wrapper( + login_flows.list, + ) + + +class AsyncLoginFlowsResourceWithStreamingResponse: + def __init__(self, login_flows: AsyncLoginFlowsResource) -> None: + self._login_flows = login_flows + + self.list = async_to_streamed_response_wrapper( + login_flows.list, + ) diff --git a/src/beeper_desktop_api/resources/bridges/login_sessions/__init__.py b/src/beeper_desktop_api/resources/bridges/login_sessions/__init__.py new file mode 100644 index 0000000..18becaa --- /dev/null +++ b/src/beeper_desktop_api/resources/bridges/login_sessions/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .steps import ( + StepsResource, + AsyncStepsResource, + StepsResourceWithRawResponse, + AsyncStepsResourceWithRawResponse, + StepsResourceWithStreamingResponse, + AsyncStepsResourceWithStreamingResponse, +) +from .login_sessions import ( + LoginSessionsResource, + AsyncLoginSessionsResource, + LoginSessionsResourceWithRawResponse, + AsyncLoginSessionsResourceWithRawResponse, + LoginSessionsResourceWithStreamingResponse, + AsyncLoginSessionsResourceWithStreamingResponse, +) + +__all__ = [ + "StepsResource", + "AsyncStepsResource", + "StepsResourceWithRawResponse", + "AsyncStepsResourceWithRawResponse", + "StepsResourceWithStreamingResponse", + "AsyncStepsResourceWithStreamingResponse", + "LoginSessionsResource", + "AsyncLoginSessionsResource", + "LoginSessionsResourceWithRawResponse", + "AsyncLoginSessionsResourceWithRawResponse", + "LoginSessionsResourceWithStreamingResponse", + "AsyncLoginSessionsResourceWithStreamingResponse", +] diff --git a/src/beeper_desktop_api/resources/bridges/login_sessions/login_sessions.py b/src/beeper_desktop_api/resources/bridges/login_sessions/login_sessions.py new file mode 100644 index 0000000..5fd6051 --- /dev/null +++ b/src/beeper_desktop_api/resources/bridges/login_sessions/login_sessions.py @@ -0,0 +1,468 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .steps import ( + StepsResource, + AsyncStepsResource, + StepsResourceWithRawResponse, + AsyncStepsResourceWithRawResponse, + StepsResourceWithStreamingResponse, + AsyncStepsResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.bridges import login_session_create_params +from ....types.login_session import LoginSession +from ....types.bridges.login_session_cancel_response import LoginSessionCancelResponse + +__all__ = ["LoginSessionsResource", "AsyncLoginSessionsResource"] + + +class LoginSessionsResource(SyncAPIResource): + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + + @cached_property + def steps(self) -> StepsResource: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return StepsResource(self._client) + + @cached_property + def with_raw_response(self) -> LoginSessionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return LoginSessionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LoginSessionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return LoginSessionsResourceWithStreamingResponse(self) + + def create( + self, + bridge_id: str, + *, + account_id: str | Omit = omit, + flow_id: str | Omit = omit, + login_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSession: + """ + Start a temporary bridge login session to connect a new chat account or + reconnect an existing bridge login. Omit loginID and accountID to connect a new + account. + + Args: + bridge_id: Bridge ID. + + account_id: Existing chat account ID to reconnect. Omit to connect a new account. + + flow_id: Optional flow ID returned by the list login flows endpoint. If omitted, Beeper + chooses the default flow. + + login_id: Existing bridge login ID to reconnect. Omit to connect a new account. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return self._post( + path_template("/v1/bridges/{bridge_id}/login-sessions", bridge_id=bridge_id), + body=maybe_transform( + { + "account_id": account_id, + "flow_id": flow_id, + "login_id": login_id, + }, + login_session_create_params.LoginSessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSession, + ) + + def retrieve( + self, + login_session_id: str, + *, + bridge_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSession: + """ + Get the current state of a temporary bridge login session. + + Args: + bridge_id: Bridge ID. + + login_session_id: Temporary bridge login session ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + if not login_session_id: + raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}") + return self._get( + path_template( + "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}", + bridge_id=bridge_id, + login_session_id=login_session_id, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSession, + ) + + def cancel( + self, + login_session_id: str, + *, + bridge_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSessionCancelResponse: + """ + Cancel a temporary bridge login session. + + Args: + bridge_id: Bridge ID. + + login_session_id: Temporary bridge login session ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + if not login_session_id: + raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}") + return self._delete( + path_template( + "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}", + bridge_id=bridge_id, + login_session_id=login_session_id, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSessionCancelResponse, + ) + + +class AsyncLoginSessionsResource(AsyncAPIResource): + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + + @cached_property + def steps(self) -> AsyncStepsResource: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncStepsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncLoginSessionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncLoginSessionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLoginSessionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncLoginSessionsResourceWithStreamingResponse(self) + + async def create( + self, + bridge_id: str, + *, + account_id: str | Omit = omit, + flow_id: str | Omit = omit, + login_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSession: + """ + Start a temporary bridge login session to connect a new chat account or + reconnect an existing bridge login. Omit loginID and accountID to connect a new + account. + + Args: + bridge_id: Bridge ID. + + account_id: Existing chat account ID to reconnect. Omit to connect a new account. + + flow_id: Optional flow ID returned by the list login flows endpoint. If omitted, Beeper + chooses the default flow. + + login_id: Existing bridge login ID to reconnect. Omit to connect a new account. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + return await self._post( + path_template("/v1/bridges/{bridge_id}/login-sessions", bridge_id=bridge_id), + body=await async_maybe_transform( + { + "account_id": account_id, + "flow_id": flow_id, + "login_id": login_id, + }, + login_session_create_params.LoginSessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSession, + ) + + async def retrieve( + self, + login_session_id: str, + *, + bridge_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSession: + """ + Get the current state of a temporary bridge login session. + + Args: + bridge_id: Bridge ID. + + login_session_id: Temporary bridge login session ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + if not login_session_id: + raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}") + return await self._get( + path_template( + "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}", + bridge_id=bridge_id, + login_session_id=login_session_id, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSession, + ) + + async def cancel( + self, + login_session_id: str, + *, + bridge_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSessionCancelResponse: + """ + Cancel a temporary bridge login session. + + Args: + bridge_id: Bridge ID. + + login_session_id: Temporary bridge login session ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + if not login_session_id: + raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}") + return await self._delete( + path_template( + "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}", + bridge_id=bridge_id, + login_session_id=login_session_id, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSessionCancelResponse, + ) + + +class LoginSessionsResourceWithRawResponse: + def __init__(self, login_sessions: LoginSessionsResource) -> None: + self._login_sessions = login_sessions + + self.create = to_raw_response_wrapper( + login_sessions.create, + ) + self.retrieve = to_raw_response_wrapper( + login_sessions.retrieve, + ) + self.cancel = to_raw_response_wrapper( + login_sessions.cancel, + ) + + @cached_property + def steps(self) -> StepsResourceWithRawResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return StepsResourceWithRawResponse(self._login_sessions.steps) + + +class AsyncLoginSessionsResourceWithRawResponse: + def __init__(self, login_sessions: AsyncLoginSessionsResource) -> None: + self._login_sessions = login_sessions + + self.create = async_to_raw_response_wrapper( + login_sessions.create, + ) + self.retrieve = async_to_raw_response_wrapper( + login_sessions.retrieve, + ) + self.cancel = async_to_raw_response_wrapper( + login_sessions.cancel, + ) + + @cached_property + def steps(self) -> AsyncStepsResourceWithRawResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncStepsResourceWithRawResponse(self._login_sessions.steps) + + +class LoginSessionsResourceWithStreamingResponse: + def __init__(self, login_sessions: LoginSessionsResource) -> None: + self._login_sessions = login_sessions + + self.create = to_streamed_response_wrapper( + login_sessions.create, + ) + self.retrieve = to_streamed_response_wrapper( + login_sessions.retrieve, + ) + self.cancel = to_streamed_response_wrapper( + login_sessions.cancel, + ) + + @cached_property + def steps(self) -> StepsResourceWithStreamingResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return StepsResourceWithStreamingResponse(self._login_sessions.steps) + + +class AsyncLoginSessionsResourceWithStreamingResponse: + def __init__(self, login_sessions: AsyncLoginSessionsResource) -> None: + self._login_sessions = login_sessions + + self.create = async_to_streamed_response_wrapper( + login_sessions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + login_sessions.retrieve, + ) + self.cancel = async_to_streamed_response_wrapper( + login_sessions.cancel, + ) + + @cached_property + def steps(self) -> AsyncStepsResourceWithStreamingResponse: + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + return AsyncStepsResourceWithStreamingResponse(self._login_sessions.steps) diff --git a/src/beeper_desktop_api/resources/bridges/login_sessions/steps.py b/src/beeper_desktop_api/resources/bridges/login_sessions/steps.py new file mode 100644 index 0000000..6bc590e --- /dev/null +++ b/src/beeper_desktop_api/resources/bridges/login_sessions/steps.py @@ -0,0 +1,250 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Literal + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.login_session import LoginSession +from ....types.bridges.login_sessions import step_submit_params + +__all__ = ["StepsResource", "AsyncStepsResource"] + + +class StepsResource(SyncAPIResource): + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + + @cached_property + def with_raw_response(self) -> StepsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return StepsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> StepsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return StepsResourceWithStreamingResponse(self) + + def submit( + self, + step_id: str, + *, + bridge_id: str, + login_session_id: str, + type: Literal["user_input", "cookies", "display_and_wait"], + fields: Dict[str, str] | Omit = omit, + last_url: str | Omit = omit, + source: Literal["api", "webview", "browser_extension"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSession: + """ + Submit input for the current step of a bridge login session. + + Args: + bridge_id: Bridge ID. + + login_session_id: Temporary bridge login session ID. + + step_id: Current bridge login session step ID. + + fields: Field values keyed by the field IDs from the current step. + + last_url: Last browser URL reached during a cookies step, if available. + + source: How the step was completed. Omit unless the client needs to distinguish an + embedded webview or browser extension. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + if not login_session_id: + raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}") + if not step_id: + raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") + return self._post( + path_template( + "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}/steps/{step_id}", + bridge_id=bridge_id, + login_session_id=login_session_id, + step_id=step_id, + ), + body=maybe_transform( + { + "type": type, + "fields": fields, + "last_url": last_url, + "source": source, + }, + step_submit_params.StepSubmitParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSession, + ) + + +class AsyncStepsResource(AsyncAPIResource): + """ + Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities. + """ + + @cached_property + def with_raw_response(self) -> AsyncStepsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers + """ + return AsyncStepsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncStepsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response + """ + return AsyncStepsResourceWithStreamingResponse(self) + + async def submit( + self, + step_id: str, + *, + bridge_id: str, + login_session_id: str, + type: Literal["user_input", "cookies", "display_and_wait"], + fields: Dict[str, str] | Omit = omit, + last_url: str | Omit = omit, + source: Literal["api", "webview", "browser_extension"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoginSession: + """ + Submit input for the current step of a bridge login session. + + Args: + bridge_id: Bridge ID. + + login_session_id: Temporary bridge login session ID. + + step_id: Current bridge login session step ID. + + fields: Field values keyed by the field IDs from the current step. + + last_url: Last browser URL reached during a cookies step, if available. + + source: How the step was completed. Omit unless the client needs to distinguish an + embedded webview or browser extension. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not bridge_id: + raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}") + if not login_session_id: + raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}") + if not step_id: + raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") + return await self._post( + path_template( + "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}/steps/{step_id}", + bridge_id=bridge_id, + login_session_id=login_session_id, + step_id=step_id, + ), + body=await async_maybe_transform( + { + "type": type, + "fields": fields, + "last_url": last_url, + "source": source, + }, + step_submit_params.StepSubmitParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoginSession, + ) + + +class StepsResourceWithRawResponse: + def __init__(self, steps: StepsResource) -> None: + self._steps = steps + + self.submit = to_raw_response_wrapper( + steps.submit, + ) + + +class AsyncStepsResourceWithRawResponse: + def __init__(self, steps: AsyncStepsResource) -> None: + self._steps = steps + + self.submit = async_to_raw_response_wrapper( + steps.submit, + ) + + +class StepsResourceWithStreamingResponse: + def __init__(self, steps: StepsResource) -> None: + self._steps = steps + + self.submit = to_streamed_response_wrapper( + steps.submit, + ) + + +class AsyncStepsResourceWithStreamingResponse: + def __init__(self, steps: AsyncStepsResource) -> None: + self._steps = steps + + self.submit = async_to_streamed_response_wrapper( + steps.submit, + ) diff --git a/src/beeper_desktop_api/resources/chats/chats.py b/src/beeper_desktop_api/resources/chats/chats.py index 38775e0..66dd5e7 100644 --- a/src/beeper_desktop_api/resources/chats/chats.py +++ b/src/beeper_desktop_api/resources/chats/chats.py @@ -157,11 +157,11 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Chat: """ - Retrieve chat details including metadata, participants, and latest message + Retrieve chat details, including metadata, participants, and the latest message. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. max_participant_count: Maximum number of participants to return. Use -1 for all; otherwise 0-500. Defaults to 100. List and search endpoints return up to 20 participants per @@ -213,13 +213,13 @@ def update( ) -> Chat: """Update supported chat fields. - Non-empty draft objects are accepted only when the + Non-empty drafts are accepted only when the current draft is empty. Send draft=null to clear the draft before setting new draft text or attachments. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. description: Group chat description/topic. Support depends on the chat account and chat permissions. @@ -343,12 +343,12 @@ def archive( ) -> None: """Archive or unarchive a chat. - Set archived=true to move to archive, - archived=false to move back to inbox + Set archived=true to move it to Archive, or + archived=false to move it back to the inbox. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. archived: True to archive, false to unarchive @@ -388,8 +388,8 @@ def mark_read( Mark a chat as read, optionally through a specific message ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Optional message ID to mark read through. @@ -428,8 +428,8 @@ def mark_unread( Mark a chat as unread, optionally from a specific message ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Optional message ID to mark unread from. @@ -464,12 +464,13 @@ def notify_anyway( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Chat: """ - Force a delivery notification when supported by the underlying network. - Currently intended for iMessage on macOS; unsupported networks return an error. + Send a notification despite the recipient focus state when the network supports + it. Currently intended for iMessage on macOS; unsupported networks return an + error. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. extra_headers: Send extra headers @@ -483,6 +484,7 @@ def notify_anyway( raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}") return self._post( path_template("/v1/chats/{chat_id}/notify-anyway", chat_id=chat_id), + body={}, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -515,8 +517,7 @@ def search( Search chats by title, network, or participant names. Args: - account_ids: Provide an array of account IDs to filter chats from specific messaging accounts - only + account_ids: Limit results to specific chat accounts. cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'. @@ -529,16 +530,14 @@ def search( include_muted: Include chats marked as Muted by the user, which are usually less important. Default: true. Set to false if the user wants a more refined search. - last_activity_after: Provide an ISO datetime string to only retrieve chats with last activity after - this time + last_activity_after: Only include chats with last activity after this ISO 8601 datetime. - last_activity_before: Provide an ISO datetime string to only retrieve chats with last activity before - this time + last_activity_before: Only include chats with last activity before this ISO 8601 datetime. limit: Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50 - query: Literal token search (non-semantic). Use single words users type (e.g., - "dinner"). When multiple words provided, ALL must match. Case-insensitive. + query: Literal chat search. Use words the user typed, such as "dinner". When multiple + words are provided, all must match. Case-insensitive. scope: Search scope: 'titles' matches title + network; 'participants' matches participant names. @@ -602,12 +601,12 @@ def start( """Resolve a user/contact and open a direct chat. Reuses and returns an existing - direct chat when one is found. Available in Beeper Desktop v4.2.808+. + direct chat when one is found. Available in Beeper v4.2.808+. Args: account_id: Account to create or start the chat on. - user: Merged user-like contact payload used to resolve the best identifier. + user: Contact-like user payload used to resolve the best identifier. allow_invite: Whether invite-based DM creation is allowed when required by the platform. @@ -741,11 +740,11 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Chat: """ - Retrieve chat details including metadata, participants, and latest message + Retrieve chat details, including metadata, participants, and the latest message. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. max_participant_count: Maximum number of participants to return. Use -1 for all; otherwise 0-500. Defaults to 100. List and search endpoints return up to 20 participants per @@ -797,13 +796,13 @@ async def update( ) -> Chat: """Update supported chat fields. - Non-empty draft objects are accepted only when the + Non-empty drafts are accepted only when the current draft is empty. Send draft=null to clear the draft before setting new draft text or attachments. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. description: Group chat description/topic. Support depends on the chat account and chat permissions. @@ -927,12 +926,12 @@ async def archive( ) -> None: """Archive or unarchive a chat. - Set archived=true to move to archive, - archived=false to move back to inbox + Set archived=true to move it to Archive, or + archived=false to move it back to the inbox. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. archived: True to archive, false to unarchive @@ -972,8 +971,8 @@ async def mark_read( Mark a chat as read, optionally through a specific message ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Optional message ID to mark read through. @@ -1012,8 +1011,8 @@ async def mark_unread( Mark a chat as unread, optionally from a specific message ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Optional message ID to mark unread from. @@ -1048,12 +1047,13 @@ async def notify_anyway( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Chat: """ - Force a delivery notification when supported by the underlying network. - Currently intended for iMessage on macOS; unsupported networks return an error. + Send a notification despite the recipient focus state when the network supports + it. Currently intended for iMessage on macOS; unsupported networks return an + error. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. extra_headers: Send extra headers @@ -1067,6 +1067,7 @@ async def notify_anyway( raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}") return await self._post( path_template("/v1/chats/{chat_id}/notify-anyway", chat_id=chat_id), + body={}, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1099,8 +1100,7 @@ def search( Search chats by title, network, or participant names. Args: - account_ids: Provide an array of account IDs to filter chats from specific messaging accounts - only + account_ids: Limit results to specific chat accounts. cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'. @@ -1113,16 +1113,14 @@ def search( include_muted: Include chats marked as Muted by the user, which are usually less important. Default: true. Set to false if the user wants a more refined search. - last_activity_after: Provide an ISO datetime string to only retrieve chats with last activity after - this time + last_activity_after: Only include chats with last activity after this ISO 8601 datetime. - last_activity_before: Provide an ISO datetime string to only retrieve chats with last activity before - this time + last_activity_before: Only include chats with last activity before this ISO 8601 datetime. limit: Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50 - query: Literal token search (non-semantic). Use single words users type (e.g., - "dinner"). When multiple words provided, ALL must match. Case-insensitive. + query: Literal chat search. Use words the user typed, such as "dinner". When multiple + words are provided, all must match. Case-insensitive. scope: Search scope: 'titles' matches title + network; 'participants' matches participant names. @@ -1186,12 +1184,12 @@ async def start( """Resolve a user/contact and open a direct chat. Reuses and returns an existing - direct chat when one is found. Available in Beeper Desktop v4.2.808+. + direct chat when one is found. Available in Beeper v4.2.808+. Args: account_id: Account to create or start the chat on. - user: Merged user-like contact payload used to resolve the best identifier. + user: Contact-like user payload used to resolve the best identifier. allow_invite: Whether invite-based DM creation is allowed when required by the platform. diff --git a/src/beeper_desktop_api/resources/chats/messages/reactions.py b/src/beeper_desktop_api/resources/chats/messages/reactions.py index 974d9f6..c9199da 100644 --- a/src/beeper_desktop_api/resources/chats/messages/reactions.py +++ b/src/beeper_desktop_api/resources/chats/messages/reactions.py @@ -61,8 +61,8 @@ def delete( Remove the reaction added by the authenticated user from an existing message. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -114,8 +114,8 @@ def add( Args: chat_id: Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -192,8 +192,8 @@ async def delete( Remove the reaction added by the authenticated user from an existing message. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -245,8 +245,8 @@ async def add( Args: chat_id: Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. diff --git a/src/beeper_desktop_api/resources/chats/reminders.py b/src/beeper_desktop_api/resources/chats/reminders.py index ef502a6..fb02508 100644 --- a/src/beeper_desktop_api/resources/chats/reminders.py +++ b/src/beeper_desktop_api/resources/chats/reminders.py @@ -54,13 +54,13 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: - """Set a reminder for a chat at a specific time + """Set a reminder for a chat at a specific time. Args: chat_id: Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + Input routes also accept the local chat ID from this installation when + available. reminder: Reminder configuration @@ -95,13 +95,13 @@ def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: - """Clear an existing reminder from a chat + """Clear an existing reminder from a chat. Args: chat_id: Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + Input routes also accept the local chat ID from this installation when + available. extra_headers: Send extra headers @@ -157,13 +157,13 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: - """Set a reminder for a chat at a specific time + """Set a reminder for a chat at a specific time. Args: chat_id: Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + Input routes also accept the local chat ID from this installation when + available. reminder: Reminder configuration @@ -198,13 +198,13 @@ async def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: - """Clear an existing reminder from a chat + """Clear an existing reminder from a chat. Args: chat_id: Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + Input routes also accept the local chat ID from this installation when + available. extra_headers: Send extra headers diff --git a/src/beeper_desktop_api/resources/info.py b/src/beeper_desktop_api/resources/info.py index d7eaf8d..c642841 100644 --- a/src/beeper_desktop_api/resources/info.py +++ b/src/beeper_desktop_api/resources/info.py @@ -56,7 +56,7 @@ def retrieve( ) -> InfoRetrieveResponse: """ Returns app, platform, server, endpoint discovery, OAuth, and WebSocket metadata - for this Beeper Desktop instance. + for this Beeper Client API server. """ return self._get( "/v1/info", @@ -108,7 +108,7 @@ async def retrieve( ) -> InfoRetrieveResponse: """ Returns app, platform, server, endpoint discovery, OAuth, and WebSocket metadata - for this Beeper Desktop instance. + for this Beeper Client API server. """ return await self._get( "/v1/info", diff --git a/src/beeper_desktop_api/resources/messages.py b/src/beeper_desktop_api/resources/messages.py index 290c923..7c4e7e6 100644 --- a/src/beeper_desktop_api/resources/messages.py +++ b/src/beeper_desktop_api/resources/messages.py @@ -70,11 +70,11 @@ def retrieve( ) -> Message: """ Retrieve a message by final message ID, pendingMessageID, or Matrix event ID. - Chat ID may be a Beeper chat ID or local chat ID. + chatID may be a Beeper chat ID or a local chat ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -117,8 +117,8 @@ def update( be edited. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -163,8 +163,8 @@ def list( Sorted by timestamp. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'. @@ -219,8 +219,8 @@ def delete( because messages cannot be deleted while sending. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -307,10 +307,10 @@ def search( media_types: Filter messages by media types. Use ['any'] for any media type, or specify exact types like ['video', 'image']. Omit for no media filtering. - query: Literal word search (non-semantic). Finds messages containing these EXACT words - in any order. Use single words users actually type, not concepts or phrases. - Example: use "dinner" not "dinner plans", use "sick" not "health issues". If - omitted, returns results filtered only by other parameters. + query: Literal word search. Finds messages containing these words in any order. Use + words the user actually typed, not inferred concepts. Example: use "dinner" + rather than "dinner plans". If omitted, returns results filtered only by the + other parameters. sender: Filter by sender: 'me' (messages sent by the authenticated user), 'others' (messages sent by others), or a specific user ID string (user.id). @@ -373,15 +373,15 @@ def send( Returns a pending message ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. attachment: Single attachment to send with the message reply_to_message_id: Provide a message ID to send this as a reply to an existing message - text: Draft text. Plain text and Markdown are converted to Matrix HTML with the same - rules used by send and edit. + text: Draft text. Plain text and Markdown are converted to Beeper rich text with the + same rules used by send and edit. extra_headers: Send extra headers @@ -446,11 +446,11 @@ async def retrieve( ) -> Message: """ Retrieve a message by final message ID, pendingMessageID, or Matrix event ID. - Chat ID may be a Beeper chat ID or local chat ID. + chatID may be a Beeper chat ID or a local chat ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -493,8 +493,8 @@ async def update( be edited. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -539,8 +539,8 @@ def list( Sorted by timestamp. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'. @@ -595,8 +595,8 @@ async def delete( because messages cannot be deleted while sending. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. message_id: Message ID. @@ -685,10 +685,10 @@ def search( media_types: Filter messages by media types. Use ['any'] for any media type, or specify exact types like ['video', 'image']. Omit for no media filtering. - query: Literal word search (non-semantic). Finds messages containing these EXACT words - in any order. Use single words users actually type, not concepts or phrases. - Example: use "dinner" not "dinner plans", use "sick" not "health issues". If - omitted, returns results filtered only by other parameters. + query: Literal word search. Finds messages containing these words in any order. Use + words the user actually typed, not inferred concepts. Example: use "dinner" + rather than "dinner plans". If omitted, returns results filtered only by the + other parameters. sender: Filter by sender: 'me' (messages sent by the authenticated user), 'others' (messages sent by others), or a specific user ID string (user.id). @@ -751,15 +751,15 @@ async def send( Returns a pending message ID. Args: - chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop - installation when available. + chat_id: Chat ID. Input routes also accept the local chat ID from this installation when + available. attachment: Single attachment to send with the message reply_to_message_id: Provide a message ID to send this as a reply to an existing message - text: Draft text. Plain text and Markdown are converted to Matrix HTML with the same - rules used by send and edit. + text: Draft text. Plain text and Markdown are converted to Beeper rich text with the + same rules used by send and edit. extra_headers: Send extra headers diff --git a/src/beeper_desktop_api/types/__init__.py b/src/beeper_desktop_api/types/__init__.py index 78ec780..137c214 100644 --- a/src/beeper_desktop_api/types/__init__.py +++ b/src/beeper_desktop_api/types/__init__.py @@ -3,12 +3,26 @@ from __future__ import annotations from .chat import Chat as Chat -from .shared import User as User, Error as Error, Message as Message, Reaction as Reaction, Attachment as Attachment +from .bridge import Bridge as Bridge +from .shared import ( + User as User, + Error as Error, + Message as Message, + APIError as APIError, + Reaction as Reaction, + Attachment as Attachment, + AppStateSnapshot as AppStateSnapshot, +) from .account import Account as Account +from .login_flow import LoginFlow as LoginFlow +from .cookie_field import CookieField as CookieField +from .login_session import LoginSession as LoginSession +from .account_bridge import AccountBridge as AccountBridge from .focus_response import FocusResponse as FocusResponse from .search_response import SearchResponse as SearchResponse from .chat_list_params import ChatListParams as ChatListParams from .chat_start_params import ChatStartParams as ChatStartParams +from .login_input_field import LoginInputField as LoginInputField from .asset_serve_params import AssetServeParams as AssetServeParams from .chat_create_params import ChatCreateParams as ChatCreateParams from .chat_list_response import ChatListResponse as ChatListResponse @@ -20,6 +34,8 @@ from .client_focus_params import ClientFocusParams as ClientFocusParams from .message_list_params import MessageListParams as MessageListParams from .message_send_params import MessageSendParams as MessageSendParams +from .app_session_response import AppSessionResponse as AppSessionResponse +from .bridge_list_response import BridgeListResponse as BridgeListResponse from .chat_create_response import ChatCreateResponse as ChatCreateResponse from .chat_retrieve_params import ChatRetrieveParams as ChatRetrieveParams from .client_search_params import ClientSearchParams as ClientSearchParams @@ -31,9 +47,16 @@ from .message_search_params import MessageSearchParams as MessageSearchParams from .message_send_response import MessageSendResponse as MessageSendResponse from .message_update_params import MessageUpdateParams as MessageUpdateParams +from .group_field_capability import GroupFieldCapability as GroupFieldCapability from .info_retrieve_response import InfoRetrieveResponse as InfoRetrieveResponse from .asset_download_response import AssetDownloadResponse as AssetDownloadResponse from .chat_mark_unread_params import ChatMarkUnreadParams as ChatMarkUnreadParams +from .group_type_capabilities import GroupTypeCapabilities as GroupTypeCapabilities from .message_update_response import MessageUpdateResponse as MessageUpdateResponse +from .bridge_retrieve_response import BridgeRetrieveResponse as BridgeRetrieveResponse +from .account_retrieve_response import AccountRetrieveResponse as AccountRetrieveResponse +from .provisioning_capabilities import ProvisioningCapabilities as ProvisioningCapabilities from .asset_upload_base64_params import AssetUploadBase64Params as AssetUploadBase64Params from .asset_upload_base64_response import AssetUploadBase64Response as AssetUploadBase64Response +from .disappearing_timer_capability import DisappearingTimerCapability as DisappearingTimerCapability +from .resolve_identifier_capabilities import ResolveIdentifierCapabilities as ResolveIdentifierCapabilities diff --git a/src/beeper_desktop_api/types/account.py b/src/beeper_desktop_api/types/account.py index fdc1a4b..a469ec4 100644 --- a/src/beeper_desktop_api/types/account.py +++ b/src/beeper_desktop_api/types/account.py @@ -1,35 +1,15 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal from pydantic import Field as FieldInfo from .._models import BaseModel from .shared.user import User +from .account_bridge import AccountBridge -__all__ = ["Account", "Bridge"] - - -class Bridge(BaseModel): - """Bridge metadata for the account. Available in Beeper Desktop v4.2.785+.""" - - id: str - """Bridge instance identifier. - - Matrix and cloud bridges often use the bridge type (for example matrix or - discordgo); local bridges use a local bridge ID (for example local-whatsapp). - Available in Beeper Desktop v4.2.785+. - """ - - provider: Literal["cloud", "self-hosted", "local", "platform-sdk"] - """Bridge provider for the account. Available in Beeper Desktop v4.2.785+.""" - - type: str - """Bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram, or twitter. - - Available in Beeper Desktop v4.2.785+. - """ +__all__ = ["Account"] class Account(BaseModel): @@ -43,14 +23,38 @@ class Account(BaseModel): workspace-scoped cloud bridges, and local-whatsapp*ba*... for local bridges. """ - bridge: Bridge + bridge: AccountBridge """Bridge metadata for the account. Available in Beeper Desktop v4.2.785+.""" + status: Literal[ + "connected", + "connecting", + "backfilling", + "connection_required", + "reconnect_required", + "attention_required", + "disconnected", + "disabled", + ] + """Current connection status for this account.""" + user: User """User the account belongs to.""" + capabilities: Optional[Dict[str, Optional[object]]] = None + """Runtime chat/message capabilities for this connected account, when available.""" + + login_id: Optional[str] = FieldInfo(alias="loginID", default=None) + """Bridge login ID for this account, when known. + + One bridge login can contain multiple chat accounts. + """ + network: Optional[str] = None """Human-friendly network name for the account. Omitted when the network is unknown. """ + + status_text: Optional[str] = FieldInfo(alias="statusText", default=None) + """Human-friendly account status text.""" diff --git a/src/beeper_desktop_api/types/account_bridge.py b/src/beeper_desktop_api/types/account_bridge.py new file mode 100644 index 0000000..d507424 --- /dev/null +++ b/src/beeper_desktop_api/types/account_bridge.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["AccountBridge"] + + +class AccountBridge(BaseModel): + """Bridge metadata for the account. Available in Beeper Desktop v4.2.785+.""" + + id: str + """Bridge identifier. + + Beeper Cloud accounts often use the network type (for example matrix or + discordgo); on-device accounts use a local bridge ID (for example + local-whatsapp). Available in Beeper Desktop v4.2.785+. + """ + + provider: Literal["cloud", "self-hosted", "local", "platform-sdk"] + """Where this account runs: on this device or in Beeper Cloud. + + Available in Beeper Desktop v4.2.785+. + """ + + type: str + """Bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram, or twitter. + + Available in Beeper Desktop v4.2.785+. + """ diff --git a/src/beeper_desktop_api/types/account_retrieve_response.py b/src/beeper_desktop_api/types/account_retrieve_response.py new file mode 100644 index 0000000..20b59e8 --- /dev/null +++ b/src/beeper_desktop_api/types/account_retrieve_response.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .shared.user import User +from .account_bridge import AccountBridge + +__all__ = ["AccountRetrieveResponse"] + + +class AccountRetrieveResponse(BaseModel): + """A chat account added to Beeper.""" + + account_id: str = FieldInfo(alias="accountID") + """Chat account added to Beeper. + + Use this to route account-scoped actions. Examples include matrix for + Beeper/Matrix, discordgo for a cloud bridge, slackgo.TEAM-USER for + workspace-scoped cloud bridges, and local-whatsapp*ba*... for local bridges. + """ + + bridge: AccountBridge + """Bridge metadata for the account. Available in Beeper Desktop v4.2.785+.""" + + status: Literal[ + "connected", + "connecting", + "backfilling", + "connection_required", + "reconnect_required", + "attention_required", + "disconnected", + "disabled", + ] + """Current connection status for this account.""" + + user: User + """User the account belongs to.""" + + capabilities: Optional[Dict[str, Optional[object]]] = None + """Runtime chat/message capabilities for this connected account, when available.""" + + login_id: Optional[str] = FieldInfo(alias="loginID", default=None) + """Bridge login ID for this account, when known. + + One bridge login can contain multiple chat accounts. + """ + + network: Optional[str] = None + """Human-friendly network name for the account. + + Omitted when the network is unknown. + """ + + status_text: Optional[str] = FieldInfo(alias="statusText", default=None) + """Human-friendly account status text.""" diff --git a/src/beeper_desktop_api/types/accounts/contact_list_params.py b/src/beeper_desktop_api/types/accounts/contact_list_params.py index e452ddb..983aea0 100644 --- a/src/beeper_desktop_api/types/accounts/contact_list_params.py +++ b/src/beeper_desktop_api/types/accounts/contact_list_params.py @@ -21,4 +21,4 @@ class ContactListParams(TypedDict, total=False): """Maximum contacts to return per page.""" query: str - """Optional search query for blended contact lookup.""" + """Optional search query for contact lookup.""" diff --git a/src/beeper_desktop_api/types/accounts/contact_search_params.py b/src/beeper_desktop_api/types/accounts/contact_search_params.py index f9063e0..b663175 100644 --- a/src/beeper_desktop_api/types/accounts/contact_search_params.py +++ b/src/beeper_desktop_api/types/accounts/contact_search_params.py @@ -9,4 +9,4 @@ class ContactSearchParams(TypedDict, total=False): query: Required[str] - """Text to search users by. Network-specific behavior.""" + """Text to search contacts by. Matching behavior depends on the network.""" diff --git a/src/beeper_desktop_api/types/app/__init__.py b/src/beeper_desktop_api/types/app/__init__.py new file mode 100644 index 0000000..2944080 --- /dev/null +++ b/src/beeper_desktop_api/types/app/__init__.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .login_email_params import LoginEmailParams as LoginEmailParams +from .login_start_response import LoginStartResponse as LoginStartResponse +from .login_register_params import LoginRegisterParams as LoginRegisterParams +from .login_response_params import LoginResponseParams as LoginResponseParams +from .login_register_response import LoginRegisterResponse as LoginRegisterResponse +from .login_response_response import LoginResponseResponse as LoginResponseResponse +from .verification_cancel_params import VerificationCancelParams as VerificationCancelParams +from .verification_create_params import VerificationCreateParams as VerificationCreateParams +from .verification_list_response import VerificationListResponse as VerificationListResponse +from .verification_accept_response import VerificationAcceptResponse as VerificationAcceptResponse +from .verification_cancel_response import VerificationCancelResponse as VerificationCancelResponse +from .verification_create_response import VerificationCreateResponse as VerificationCreateResponse +from .verification_retrieve_response import VerificationRetrieveResponse as VerificationRetrieveResponse diff --git a/src/beeper_desktop_api/types/app/login/__init__.py b/src/beeper_desktop_api/types/app/login/__init__.py new file mode 100644 index 0000000..f8ee8b1 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/beeper_desktop_api/types/app/login/verification/__init__.py b/src/beeper_desktop_api/types/app/login/verification/__init__.py new file mode 100644 index 0000000..d979259 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .recovery_key_verify_params import RecoveryKeyVerifyParams as RecoveryKeyVerifyParams +from .recovery_key_verify_response import RecoveryKeyVerifyResponse as RecoveryKeyVerifyResponse diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/__init__.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/__init__.py new file mode 100644 index 0000000..0299c46 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .reset_create_params import ResetCreateParams as ResetCreateParams +from .reset_confirm_params import ResetConfirmParams as ResetConfirmParams +from .reset_create_response import ResetCreateResponse as ResetCreateResponse +from .reset_confirm_response import ResetConfirmResponse as ResetConfirmResponse diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_params.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_params.py new file mode 100644 index 0000000..8c5ce40 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ......_utils import PropertyInfo + +__all__ = ["ResetConfirmParams"] + + +class ResetConfirmParams(TypedDict, total=False): + recovery_key: Required[Annotated[str, PropertyInfo(alias="recoveryKey")]] + """New recovery key returned by the reset step.""" diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_response.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_response.py new file mode 100644 index 0000000..bc6cb66 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_response.py @@ -0,0 +1,192 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ......_models import BaseModel + +__all__ = [ + "ResetConfirmResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class ResetConfirmResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_params.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_params.py new file mode 100644 index 0000000..5d9fd5b --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ......_utils import PropertyInfo + +__all__ = ["ResetCreateParams"] + + +class ResetCreateParams(TypedDict, total=False): + existing_recovery_key: Annotated[str, PropertyInfo(alias="existingRecoveryKey")] + """Existing recovery key, if the user has it.""" diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_response.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_response.py new file mode 100644 index 0000000..166f642 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_response.py @@ -0,0 +1,200 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ......_models import BaseModel + +__all__ = [ + "ResetCreateResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """ + Current app sign-in and encrypted messaging setup state after creating the new recovery key. + """ + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class ResetCreateResponse(BaseModel): + recovery_key: str = FieldInfo(alias="recoveryKey") + """New recovery key. Show it once and ask the user to save it.""" + + session: Session + """ + Current app sign-in and encrypted messaging setup state after creating the new + recovery key. + """ diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_params.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_params.py new file mode 100644 index 0000000..cdb0e08 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ....._utils import PropertyInfo + +__all__ = ["RecoveryKeyVerifyParams"] + + +class RecoveryKeyVerifyParams(TypedDict, total=False): + recovery_key: Required[Annotated[str, PropertyInfo(alias="recoveryKey")]] + """Recovery key saved by the user.""" diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_response.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_response.py new file mode 100644 index 0000000..1c3baaa --- /dev/null +++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_response.py @@ -0,0 +1,192 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ....._models import BaseModel + +__all__ = [ + "RecoveryKeyVerifyResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class RecoveryKeyVerifyResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" diff --git a/src/beeper_desktop_api/types/app/login_email_params.py b/src/beeper_desktop_api/types/app/login_email_params.py new file mode 100644 index 0000000..30062dd --- /dev/null +++ b/src/beeper_desktop_api/types/app/login_email_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["LoginEmailParams"] + + +class LoginEmailParams(TypedDict, total=False): + email: Required[str] + """Email address to send the sign-in code to.""" + + setup_request_id: Required[Annotated[str, PropertyInfo(alias="setupRequestID")]] + """Setup request ID returned by the start step.""" diff --git a/src/beeper_desktop_api/types/app/login_register_params.py b/src/beeper_desktop_api/types/app/login_register_params.py new file mode 100644 index 0000000..7848626 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login_register_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["LoginRegisterParams"] + + +class LoginRegisterParams(TypedDict, total=False): + accept_terms: Required[Annotated[Literal[True], PropertyInfo(alias="acceptTerms")]] + """ + Confirms that the user agreed to our + [terms of use](https://www.beeper.com/terms-onboarding) and has read our + [privacy policy](https://www.beeper.com/privacy). + """ + + lead_token: Required[Annotated[str, PropertyInfo(alias="leadToken")]] + """Registration token returned by Beeper.""" + + setup_request_id: Required[Annotated[str, PropertyInfo(alias="setupRequestID")]] + """Setup request ID returned by the start step.""" + + username: Required[str] + """Username selected by the user.""" diff --git a/src/beeper_desktop_api/types/app/login_register_response.py b/src/beeper_desktop_api/types/app/login_register_response.py new file mode 100644 index 0000000..c67f552 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login_register_response.py @@ -0,0 +1,212 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "LoginRegisterResponse", + "Matrix", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", +] + + +class Matrix(BaseModel): + """Account credentials for first-party app setup.""" + + access_token: str = FieldInfo(alias="accessToken") + """Beeper account access token. Returned once for first-party app setup.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state after sign-in.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class LoginRegisterResponse(BaseModel): + matrix: Matrix + """Account credentials for first-party app setup.""" + + session: Session + """Current app sign-in and encrypted messaging setup state after sign-in.""" diff --git a/src/beeper_desktop_api/types/app/login_response_params.py b/src/beeper_desktop_api/types/app/login_response_params.py new file mode 100644 index 0000000..72a2b3a --- /dev/null +++ b/src/beeper_desktop_api/types/app/login_response_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["LoginResponseParams"] + + +class LoginResponseParams(TypedDict, total=False): + response: Required[str] + """Sign-in code from the user email.""" + + setup_request_id: Required[Annotated[str, PropertyInfo(alias="setupRequestID")]] + """Setup request ID returned by the start step.""" diff --git a/src/beeper_desktop_api/types/app/login_response_response.py b/src/beeper_desktop_api/types/app/login_response_response.py new file mode 100644 index 0000000..fb75746 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login_response_response.py @@ -0,0 +1,251 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "LoginResponseResponse", + "Success", + "SuccessMatrix", + "SuccessSession", + "SuccessSessionE2EE", + "SuccessSessionE2EESecrets", + "SuccessSessionMatrix", + "SuccessSessionVerification", + "SuccessSessionVerificationError", + "SuccessSessionVerificationOtherDevice", + "SuccessSessionVerificationQr", + "SuccessSessionVerificationSAS", + "RegistrationRequired", + "RegistrationRequiredCopy", +] + + +class SuccessMatrix(BaseModel): + """Account credentials for first-party app setup.""" + + access_token: str = FieldInfo(alias="accessToken") + """Beeper account access token. Returned once for first-party app setup.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SuccessSessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SuccessSessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SuccessSessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SuccessSessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SuccessSessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SuccessSessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SuccessSessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SuccessSessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SuccessSessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SuccessSessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SuccessSessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SuccessSessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SuccessSessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class SuccessSession(BaseModel): + """Current app sign-in and encrypted messaging setup state after sign-in.""" + + e2ee: SuccessSessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SuccessSessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SuccessSessionVerification] = None + """Trusted device verification progress.""" + + +class Success(BaseModel): + matrix: SuccessMatrix + """Account credentials for first-party app setup.""" + + session: SuccessSession + """Current app sign-in and encrypted messaging setup state after sign-in.""" + + +class RegistrationRequiredCopy(BaseModel): + """Copy to display during account creation.""" + + submit: Literal["Continue"] + """Submit button label.""" + + terms: Literal["By continuing, you agree to the Terms of Use and acknowledge the Privacy Policy."] + """Terms and privacy notice to show before account creation.""" + + title: Literal["Choose your username"] + """Title for the username step.""" + + username_placeholder: Literal["Username"] = FieldInfo(alias="usernamePlaceholder") + """Placeholder for the username field.""" + + +class RegistrationRequired(BaseModel): + copy_: RegistrationRequiredCopy = FieldInfo(alias="copy") + """Copy to display during account creation.""" + + lead_token: str = FieldInfo(alias="leadToken") + """Registration token returned by Beeper.""" + + registration_required: Literal[True] = FieldInfo(alias="registrationRequired") + """Indicates that the user needs to create a Beeper account.""" + + setup_request_id: str = FieldInfo(alias="setupRequestID") + """Setup request ID to use when creating the account.""" + + username_suggestions: Optional[List[str]] = FieldInfo(alias="usernameSuggestions", default=None) + """Suggested usernames for the new account.""" + + +LoginResponseResponse: TypeAlias = Union[Success, RegistrationRequired] diff --git a/src/beeper_desktop_api/types/app/login_start_response.py b/src/beeper_desktop_api/types/app/login_start_response.py new file mode 100644 index 0000000..7a2d5d9 --- /dev/null +++ b/src/beeper_desktop_api/types/app/login_start_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["LoginStartResponse"] + + +class LoginStartResponse(BaseModel): + setup_request_id: str = FieldInfo(alias="setupRequestID") + """Setup request ID to use in the next sign-in step.""" + + sign_in_methods: List[str] = FieldInfo(alias="signInMethods") + """Available sign-in methods for this setup request.""" diff --git a/src/beeper_desktop_api/types/app/verification_accept_response.py b/src/beeper_desktop_api/types/app/verification_accept_response.py new file mode 100644 index 0000000..4691a54 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verification_accept_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "VerificationAcceptResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class VerificationAcceptResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app/verification_cancel_params.py b/src/beeper_desktop_api/types/app/verification_cancel_params.py new file mode 100644 index 0000000..d954b97 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verification_cancel_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["VerificationCancelParams"] + + +class VerificationCancelParams(TypedDict, total=False): + code: str + """Optional cancellation code.""" + + reason: str + """Optional user-facing cancellation reason.""" diff --git a/src/beeper_desktop_api/types/app/verification_cancel_response.py b/src/beeper_desktop_api/types/app/verification_cancel_response.py new file mode 100644 index 0000000..68dfc2f --- /dev/null +++ b/src/beeper_desktop_api/types/app/verification_cancel_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "VerificationCancelResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class VerificationCancelResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app/verification_create_params.py b/src/beeper_desktop_api/types/app/verification_create_params.py new file mode 100644 index 0000000..6359e67 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verification_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["VerificationCreateParams"] + + +class VerificationCreateParams(TypedDict, total=False): + purpose: Literal["login", "device"] + """Why this verification is being started.""" + + user_id: Annotated[str, PropertyInfo(alias="userID")] + """Beeper user ID to verify. Defaults to the signed-in user.""" diff --git a/src/beeper_desktop_api/types/app/verification_create_response.py b/src/beeper_desktop_api/types/app/verification_create_response.py new file mode 100644 index 0000000..a88c143 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verification_create_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "VerificationCreateResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class VerificationCreateResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app/verification_list_response.py b/src/beeper_desktop_api/types/app/verification_list_response.py new file mode 100644 index 0000000..8b138f4 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verification_list_response.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["VerificationListResponse", "Item", "ItemError", "ItemOtherDevice", "ItemQr", "ItemSAS"] + + +class ItemError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class ItemOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class ItemQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class ItemSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Item(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[ItemError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[ItemOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[ItemQr] = None + """QR verification data.""" + + sas: Optional[ItemSAS] = None + """Emoji or number comparison data for verification.""" + + +class VerificationListResponse(BaseModel): + items: List[Item] diff --git a/src/beeper_desktop_api/types/app/verification_retrieve_response.py b/src/beeper_desktop_api/types/app/verification_retrieve_response.py new file mode 100644 index 0000000..baff10c --- /dev/null +++ b/src/beeper_desktop_api/types/app/verification_retrieve_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "VerificationRetrieveResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class VerificationRetrieveResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app/verifications/__init__.py b/src/beeper_desktop_api/types/app/verifications/__init__.py new file mode 100644 index 0000000..72e2812 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verifications/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .qr_scan_params import QrScanParams as QrScanParams +from .qr_scan_response import QrScanResponse as QrScanResponse +from .sas_start_response import SASStartResponse as SASStartResponse +from .sas_confirm_response import SASConfirmResponse as SASConfirmResponse +from .qr_confirm_scanned_response import QrConfirmScannedResponse as QrConfirmScannedResponse diff --git a/src/beeper_desktop_api/types/app/verifications/qr_confirm_scanned_response.py b/src/beeper_desktop_api/types/app/verifications/qr_confirm_scanned_response.py new file mode 100644 index 0000000..c63b4f9 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verifications/qr_confirm_scanned_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = [ + "QrConfirmScannedResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class QrConfirmScannedResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app/verifications/qr_scan_params.py b/src/beeper_desktop_api/types/app/verifications/qr_scan_params.py new file mode 100644 index 0000000..a16f78e --- /dev/null +++ b/src/beeper_desktop_api/types/app/verifications/qr_scan_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["QrScanParams"] + + +class QrScanParams(TypedDict, total=False): + data: Required[str] + """QR code payload scanned from the other device.""" diff --git a/src/beeper_desktop_api/types/app/verifications/qr_scan_response.py b/src/beeper_desktop_api/types/app/verifications/qr_scan_response.py new file mode 100644 index 0000000..46c3399 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verifications/qr_scan_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = [ + "QrScanResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class QrScanResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app/verifications/sas_confirm_response.py b/src/beeper_desktop_api/types/app/verifications/sas_confirm_response.py new file mode 100644 index 0000000..340de20 --- /dev/null +++ b/src/beeper_desktop_api/types/app/verifications/sas_confirm_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = [ + "SASConfirmResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class SASConfirmResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app/verifications/sas_start_response.py b/src/beeper_desktop_api/types/app/verifications/sas_start_response.py new file mode 100644 index 0000000..544c4ad --- /dev/null +++ b/src/beeper_desktop_api/types/app/verifications/sas_start_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = [ + "SASStartResponse", + "Session", + "SessionE2EE", + "SessionE2EESecrets", + "SessionMatrix", + "SessionVerification", + "SessionVerificationError", + "SessionVerificationOtherDevice", + "SessionVerificationQr", + "SessionVerificationSAS", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class SessionE2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class SessionE2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: SessionE2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class SessionMatrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class SessionVerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class SessionVerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class SessionVerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class SessionVerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class SessionVerification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[SessionVerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[SessionVerificationQr] = None + """QR verification data.""" + + sas: Optional[SessionVerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class Session(BaseModel): + """Current app sign-in and encrypted messaging setup state.""" + + e2ee: SessionE2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[SessionMatrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[SessionVerification] = None + """Trusted device verification progress.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class SASStartResponse(BaseModel): + session: Session + """Current app sign-in and encrypted messaging setup state.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/app_session_response.py b/src/beeper_desktop_api/types/app_session_response.py new file mode 100644 index 0000000..1b5deee --- /dev/null +++ b/src/beeper_desktop_api/types/app_session_response.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = [ + "AppSessionResponse", + "E2EE", + "E2EESecrets", + "Matrix", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class E2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class E2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: E2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class Matrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class AppSessionResponse(BaseModel): + e2ee: E2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[Matrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/asset_download_params.py b/src/beeper_desktop_api/types/asset_download_params.py index 62e7c02..58a04a1 100644 --- a/src/beeper_desktop_api/types/asset_download_params.py +++ b/src/beeper_desktop_api/types/asset_download_params.py @@ -9,4 +9,4 @@ class AssetDownloadParams(TypedDict, total=False): url: Required[str] - """Matrix content URL (mxc:// or localmxc://) for the file to download.""" + """Beeper media URL (mxc:// or localmxc://) for the file to download.""" diff --git a/src/beeper_desktop_api/types/bridge.py b/src/beeper_desktop_api/types/bridge.py new file mode 100644 index 0000000..38adb1b --- /dev/null +++ b/src/beeper_desktop_api/types/bridge.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .account import Account +from .._models import BaseModel + +__all__ = ["Bridge"] + + +class Bridge(BaseModel): + """Available bridge that can connect or reconnect chat accounts.""" + + id: str + """Bridge ID. Use with bridge endpoints.""" + + accounts: List[Account] + """Connected accounts for this bridge. + + Uses the same Account schema as GET /v1/accounts. + """ + + active_account_count: int = FieldInfo(alias="activeAccountCount") + """Number of active accounts for this network on this device.""" + + display_name: str = FieldInfo(alias="displayName") + """Human-friendly bridge name shown in Beeper.""" + + provider: Literal["cloud", "self-hosted", "local", "platform-sdk"] + """Where accounts for this bridge run: on this device or in Beeper Cloud.""" + + status: Literal["available", "connected", "limit_reached", "temporarily_unavailable", "disabled"] + """Whether this bridge can currently be used to connect new accounts.""" + + supports_multiple_accounts: bool = FieldInfo(alias="supportsMultipleAccounts") + """Whether this bridge can have multiple active accounts for the same network.""" + + type: str + """ + Underlying bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram, + or twitter. + """ + + network: Optional[str] = None + """Network grouping used for account counts and limits.""" + + status_text: Optional[str] = FieldInfo(alias="statusText", default=None) + """Human-friendly status text matching Beeper account management language.""" diff --git a/src/beeper_desktop_api/types/bridge_list_response.py b/src/beeper_desktop_api/types/bridge_list_response.py new file mode 100644 index 0000000..ed0d497 --- /dev/null +++ b/src/beeper_desktop_api/types/bridge_list_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .bridge import Bridge +from .._models import BaseModel + +__all__ = ["BridgeListResponse"] + + +class BridgeListResponse(BaseModel): + """Available bridges and their connected accounts.""" + + items: List[Bridge] diff --git a/src/beeper_desktop_api/types/bridge_retrieve_response.py b/src/beeper_desktop_api/types/bridge_retrieve_response.py new file mode 100644 index 0000000..89b4512 --- /dev/null +++ b/src/beeper_desktop_api/types/bridge_retrieve_response.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .account import Account +from .._models import BaseModel + +__all__ = ["BridgeRetrieveResponse"] + + +class BridgeRetrieveResponse(BaseModel): + """Available bridge that can connect or reconnect chat accounts.""" + + id: str + """Bridge ID. Use with bridge endpoints.""" + + accounts: List[Account] + """Connected accounts for this bridge. + + Uses the same Account schema as GET /v1/accounts. + """ + + active_account_count: int = FieldInfo(alias="activeAccountCount") + """Number of active accounts for this network on this device.""" + + display_name: str = FieldInfo(alias="displayName") + """Human-friendly bridge name shown in Beeper.""" + + provider: Literal["cloud", "self-hosted", "local", "platform-sdk"] + """Where accounts for this bridge run: on this device or in Beeper Cloud.""" + + status: Literal["available", "connected", "limit_reached", "temporarily_unavailable", "disabled"] + """Whether this bridge can currently be used to connect new accounts.""" + + supports_multiple_accounts: bool = FieldInfo(alias="supportsMultipleAccounts") + """Whether this bridge can have multiple active accounts for the same network.""" + + type: str + """ + Underlying bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram, + or twitter. + """ + + network: Optional[str] = None + """Network grouping used for account counts and limits.""" + + status_text: Optional[str] = FieldInfo(alias="statusText", default=None) + """Human-friendly status text matching Beeper account management language.""" diff --git a/src/beeper_desktop_api/types/bridges/__init__.py b/src/beeper_desktop_api/types/bridges/__init__.py new file mode 100644 index 0000000..4efdabc --- /dev/null +++ b/src/beeper_desktop_api/types/bridges/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .login_flow_list_response import LoginFlowListResponse as LoginFlowListResponse +from .login_session_create_params import LoginSessionCreateParams as LoginSessionCreateParams +from .login_session_cancel_response import LoginSessionCancelResponse as LoginSessionCancelResponse diff --git a/src/beeper_desktop_api/types/bridges/login_flow_list_response.py b/src/beeper_desktop_api/types/bridges/login_flow_list_response.py new file mode 100644 index 0000000..1d625e3 --- /dev/null +++ b/src/beeper_desktop_api/types/bridges/login_flow_list_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from ..login_flow import LoginFlow + +__all__ = ["LoginFlowListResponse"] + + +class LoginFlowListResponse(BaseModel): + items: List[LoginFlow] diff --git a/src/beeper_desktop_api/types/bridges/login_session_cancel_response.py b/src/beeper_desktop_api/types/bridges/login_session_cancel_response.py new file mode 100644 index 0000000..a3aaee6 --- /dev/null +++ b/src/beeper_desktop_api/types/bridges/login_session_cancel_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["LoginSessionCancelResponse"] + + +class LoginSessionCancelResponse(BaseModel): + bridge_id: str = FieldInfo(alias="bridgeID") + + login_session_id: str = FieldInfo(alias="loginSessionID") + + status: Literal["cancelled"] diff --git a/src/beeper_desktop_api/types/bridges/login_session_create_params.py b/src/beeper_desktop_api/types/bridges/login_session_create_params.py new file mode 100644 index 0000000..8da4234 --- /dev/null +++ b/src/beeper_desktop_api/types/bridges/login_session_create_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["LoginSessionCreateParams"] + + +class LoginSessionCreateParams(TypedDict, total=False): + account_id: Annotated[str, PropertyInfo(alias="accountID")] + """Existing chat account ID to reconnect. Omit to connect a new account.""" + + flow_id: Annotated[str, PropertyInfo(alias="flowID")] + """Optional flow ID returned by the list login flows endpoint. + + If omitted, Beeper chooses the default flow. + """ + + login_id: Annotated[str, PropertyInfo(alias="loginID")] + """Existing bridge login ID to reconnect. Omit to connect a new account.""" diff --git a/src/beeper_desktop_api/types/bridges/login_sessions/__init__.py b/src/beeper_desktop_api/types/bridges/login_sessions/__init__.py new file mode 100644 index 0000000..a83b5fb --- /dev/null +++ b/src/beeper_desktop_api/types/bridges/login_sessions/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .step_submit_params import StepSubmitParams as StepSubmitParams diff --git a/src/beeper_desktop_api/types/bridges/login_sessions/step_submit_params.py b/src/beeper_desktop_api/types/bridges/login_sessions/step_submit_params.py new file mode 100644 index 0000000..e099810 --- /dev/null +++ b/src/beeper_desktop_api/types/bridges/login_sessions/step_submit_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["StepSubmitParams"] + + +class StepSubmitParams(TypedDict, total=False): + bridge_id: Required[Annotated[str, PropertyInfo(alias="bridgeID")]] + """Bridge ID.""" + + login_session_id: Required[Annotated[str, PropertyInfo(alias="loginSessionID")]] + """Temporary bridge login session ID.""" + + type: Required[Literal["user_input", "cookies", "display_and_wait"]] + + fields: Dict[str, str] + """Field values keyed by the field IDs from the current step.""" + + last_url: Annotated[str, PropertyInfo(alias="lastURL")] + """Last browser URL reached during a cookies step, if available.""" + + source: Literal["api", "webview", "browser_extension"] + """How the step was completed. + + Omit unless the client needs to distinguish an embedded webview or browser + extension. + """ diff --git a/src/beeper_desktop_api/types/chat.py b/src/beeper_desktop_api/types/chat.py index 4cd922c..788c891 100644 --- a/src/beeper_desktop_api/types/chat.py +++ b/src/beeper_desktop_api/types/chat.py @@ -38,7 +38,7 @@ class ParticipantsItem(User): """True if this participant has admin privileges in the chat.""" is_network_bot: Optional[bool] = FieldInfo(alias="isNetworkBot", default=None) - """True if this participant represents a network or bridge bot.""" + """True if this participant represents an automated network account.""" is_pending: Optional[bool] = FieldInfo(alias="isPending", default=None) """True if this participant has been invited but has not joined yet.""" @@ -370,7 +370,7 @@ class Draft(BaseModel): """Current draft object for this chat, or null when no draft is set.""" text: str - """Matrix HTML draft body.""" + """Rich-text draft body as returned by Beeper.""" attachments: Optional[Dict[str, DraftAttachments]] = None """Draft attachments keyed by attachment ID.""" @@ -455,7 +455,7 @@ class Chat(BaseModel): """Last read message sortKey.""" local_chat_id: Optional[str] = FieldInfo(alias="localChatID", default=None) - """Local chat ID specific to this Beeper Desktop installation.""" + """Local chat ID specific to this installation.""" message_expiry_seconds: Optional[int] = FieldInfo(alias="messageExpirySeconds", default=None) """Disappearing-message timer in seconds when available.""" diff --git a/src/beeper_desktop_api/types/chat_search_params.py b/src/beeper_desktop_api/types/chat_search_params.py index 661e9de..b3d8876 100644 --- a/src/beeper_desktop_api/types/chat_search_params.py +++ b/src/beeper_desktop_api/types/chat_search_params.py @@ -14,10 +14,7 @@ class ChatSearchParams(TypedDict, total=False): account_ids: Annotated[SequenceNotStr[str], PropertyInfo(alias="accountIDs")] - """ - Provide an array of account IDs to filter chats from specific messaging accounts - only - """ + """Limit results to specific chat accounts.""" cursor: str """Opaque pagination cursor; do not inspect. Use together with 'direction'.""" @@ -41,25 +38,19 @@ class ChatSearchParams(TypedDict, total=False): """ last_activity_after: Annotated[Union[str, datetime], PropertyInfo(alias="lastActivityAfter", format="iso8601")] - """ - Provide an ISO datetime string to only retrieve chats with last activity after - this time - """ + """Only include chats with last activity after this ISO 8601 datetime.""" last_activity_before: Annotated[Union[str, datetime], PropertyInfo(alias="lastActivityBefore", format="iso8601")] - """ - Provide an ISO datetime string to only retrieve chats with last activity before - this time - """ + """Only include chats with last activity before this ISO 8601 datetime.""" limit: int """Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50""" query: str - """Literal token search (non-semantic). + """Literal chat search. - Use single words users type (e.g., "dinner"). When multiple words provided, ALL - must match. Case-insensitive. + Use words the user typed, such as "dinner". When multiple words are provided, + all must match. Case-insensitive. """ scope: Literal["titles", "participants"] diff --git a/src/beeper_desktop_api/types/chat_start_params.py b/src/beeper_desktop_api/types/chat_start_params.py index e70216d..69030f1 100644 --- a/src/beeper_desktop_api/types/chat_start_params.py +++ b/src/beeper_desktop_api/types/chat_start_params.py @@ -14,7 +14,7 @@ class ChatStartParams(TypedDict, total=False): """Account to create or start the chat on.""" user: Required[User] - """Merged user-like contact payload used to resolve the best identifier.""" + """Contact-like user payload used to resolve the best identifier.""" allow_invite: Annotated[bool, PropertyInfo(alias="allowInvite")] """Whether invite-based DM creation is allowed when required by the platform.""" @@ -24,7 +24,7 @@ class ChatStartParams(TypedDict, total=False): class User(TypedDict, total=False): - """Merged user-like contact payload used to resolve the best identifier.""" + """Contact-like user payload used to resolve the best identifier.""" id: str """Known user ID when available.""" diff --git a/src/beeper_desktop_api/types/chat_update_params.py b/src/beeper_desktop_api/types/chat_update_params.py index d485a1b..1e6da03 100644 --- a/src/beeper_desktop_api/types/chat_update_params.py +++ b/src/beeper_desktop_api/types/chat_update_params.py @@ -95,8 +95,8 @@ class Draft(TypedDict, total=False): text: Required[str] """Draft text. - Plain text and Markdown are converted to Matrix HTML with the same rules used by - send and edit. + Plain text and Markdown are converted to Beeper rich text with the same rules + used by send and edit. """ attachments: Dict[str, DraftAttachments] diff --git a/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py b/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py index 976e0a0..24b3585 100644 --- a/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py +++ b/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py @@ -13,8 +13,8 @@ class ReactionAddParams(TypedDict, total=False): chat_id: Required[Annotated[str, PropertyInfo(alias="chatID")]] """Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop installation - when available. + Input routes also accept the local chat ID from this installation when + available. """ reaction_key: Required[Annotated[str, PropertyInfo(alias="reactionKey")]] diff --git a/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py b/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py index 3e0a638..d465e10 100644 --- a/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py +++ b/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py @@ -13,8 +13,8 @@ class ReactionAddResponse(BaseModel): chat_id: str = FieldInfo(alias="chatID") """Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop installation - when available. + Input routes also accept the local chat ID from this installation when + available. """ message_id: str = FieldInfo(alias="messageID") diff --git a/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py b/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py index 196a739..48b65de 100644 --- a/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py +++ b/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py @@ -13,8 +13,8 @@ class ReactionDeleteResponse(BaseModel): chat_id: str = FieldInfo(alias="chatID") """Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop installation - when available. + Input routes also accept the local chat ID from this installation when + available. """ message_id: str = FieldInfo(alias="messageID") diff --git a/src/beeper_desktop_api/types/client_focus_params.py b/src/beeper_desktop_api/types/client_focus_params.py index df3106f..c8e0d9d 100644 --- a/src/beeper_desktop_api/types/client_focus_params.py +++ b/src/beeper_desktop_api/types/client_focus_params.py @@ -17,7 +17,7 @@ class ClientFocusParams(TypedDict, total=False): """ draft_attachment_path: Annotated[str, PropertyInfo(alias="draftAttachmentPath")] - """Optional image path to populate in the message input field.""" + """Optional local image path to populate in the message input field.""" draft_text: Annotated[str, PropertyInfo(alias="draftText")] """Optional plain text to populate in the message input field.""" diff --git a/src/beeper_desktop_api/types/client_search_params.py b/src/beeper_desktop_api/types/client_search_params.py index 6135164..fb7f161 100644 --- a/src/beeper_desktop_api/types/client_search_params.py +++ b/src/beeper_desktop_api/types/client_search_params.py @@ -9,4 +9,4 @@ class ClientSearchParams(TypedDict, total=False): query: Required[str] - """User-typed search text. Literal word matching (non-semantic).""" + """User-typed search text. Uses literal word matching.""" diff --git a/src/beeper_desktop_api/types/cookie_field.py b/src/beeper_desktop_api/types/cookie_field.py new file mode 100644 index 0000000..5c20149 --- /dev/null +++ b/src/beeper_desktop_api/types/cookie_field.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["CookieField"] + + +class CookieField(BaseModel): + id: str + """Field ID to send back in the fields object.""" + + name: Optional[str] = None + """Cookie, header, or local storage key to collect.""" + + type: Optional[Literal["cookie", "header", "local_storage"]] = None + """Browser storage source for this value.""" diff --git a/src/beeper_desktop_api/types/disappearing_timer_capability.py b/src/beeper_desktop_api/types/disappearing_timer_capability.py new file mode 100644 index 0000000..9e86c25 --- /dev/null +++ b/src/beeper_desktop_api/types/disappearing_timer_capability.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["DisappearingTimerCapability"] + + +class DisappearingTimerCapability(BaseModel): + """Disappearing-message timer capability.""" + + types: List[Literal["", "after_read", "after_send"]] + + omit_empty_timer: Optional[Literal[True]] = None + + timers: Optional[List[int]] = None diff --git a/src/beeper_desktop_api/types/group_field_capability.py b/src/beeper_desktop_api/types/group_field_capability.py new file mode 100644 index 0000000..2ecbb92 --- /dev/null +++ b/src/beeper_desktop_api/types/group_field_capability.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .disappearing_timer_capability import DisappearingTimerCapability + +__all__ = ["GroupFieldCapability"] + + +class GroupFieldCapability(BaseModel): + """Group creation field capability.""" + + allowed: bool + + max_length: Optional[int] = None + + min_length: Optional[int] = None + + required: Optional[bool] = None + + settings: Optional[DisappearingTimerCapability] = None + """Disappearing-message timer capability.""" diff --git a/src/beeper_desktop_api/types/group_type_capabilities.py b/src/beeper_desktop_api/types/group_type_capabilities.py new file mode 100644 index 0000000..abb841f --- /dev/null +++ b/src/beeper_desktop_api/types/group_type_capabilities.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .group_field_capability import GroupFieldCapability + +__all__ = ["GroupTypeCapabilities"] + + +class GroupTypeCapabilities(BaseModel): + """Group creation capabilities for one group type.""" + + type_description: str + + avatar: Optional[GroupFieldCapability] = None + """Group creation field capability.""" + + disappear: Optional[GroupFieldCapability] = None + """Group creation field capability.""" + + name: Optional[GroupFieldCapability] = None + """Group creation field capability.""" + + parent: Optional[GroupFieldCapability] = None + """Group creation field capability.""" + + participants: Optional[GroupFieldCapability] = None + """Group creation field capability.""" + + topic: Optional[GroupFieldCapability] = None + """Group creation field capability.""" + + username: Optional[GroupFieldCapability] = None + """Group creation field capability.""" diff --git a/src/beeper_desktop_api/types/info_retrieve_response.py b/src/beeper_desktop_api/types/info_retrieve_response.py index 9a643aa..74124de 100644 --- a/src/beeper_desktop_api/types/info_retrieve_response.py +++ b/src/beeper_desktop_api/types/info_retrieve_response.py @@ -64,7 +64,7 @@ class Platform(BaseModel): class Server(BaseModel): base_url: str - """Base URL of the Beeper Desktop API server""" + """Base URL of the Beeper Client API server""" hostname: str """Listening host""" diff --git a/src/beeper_desktop_api/types/login_flow.py b/src/beeper_desktop_api/types/login_flow.py new file mode 100644 index 0000000..fe0c71b --- /dev/null +++ b/src/beeper_desktop_api/types/login_flow.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["LoginFlow"] + + +class LoginFlow(BaseModel): + """Connect or reconnect flow option for a bridge.""" + + id: str + """Flow ID to pass when creating a bridge login session.""" + + description: Optional[str] = None + """Short explanation for when to use this flow, when provided.""" + + name: Optional[str] = None + """Display name for the flow, when provided.""" diff --git a/src/beeper_desktop_api/types/login_input_field.py b/src/beeper_desktop_api/types/login_input_field.py new file mode 100644 index 0000000..4407f27 --- /dev/null +++ b/src/beeper_desktop_api/types/login_input_field.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["LoginInputField"] + + +class LoginInputField(BaseModel): + id: str + """Field ID to send back in the fields object.""" + + initial_value: Optional[str] = FieldInfo(alias="initialValue", default=None) + """Initial field value, when provided by the network.""" + + label: Optional[str] = None + """Field label to show to the user.""" + + optional: Optional[bool] = None + """True if the user can leave this field empty.""" + + placeholder: Optional[str] = None + """Placeholder text to show when the field is empty.""" + + type: Optional[str] = None + """Suggested input type, such as text, password, or email.""" diff --git a/src/beeper_desktop_api/types/login_session.py b/src/beeper_desktop_api/types/login_session.py new file mode 100644 index 0000000..1a5fe9f --- /dev/null +++ b/src/beeper_desktop_api/types/login_session.py @@ -0,0 +1,207 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from pydantic import Field as FieldInfo + +from .account import Account +from .._models import BaseModel +from .shared.user import User +from .cookie_field import CookieField +from .shared.api_error import APIError +from .login_input_field import LoginInputField + +__all__ = [ + "LoginSession", + "CurrentStep", + "CurrentStepUserInput", + "CurrentStepCookies", + "CurrentStepDisplayAndWait", + "CurrentStepDisplayAndWaitDisplay", + "CurrentStepDisplayAndWaitDisplayQrCode", + "CurrentStepDisplayAndWaitDisplayEmoji", + "CurrentStepDisplayAndWaitDisplayEmpty", + "CurrentStepComplete", + "CurrentStepCompleteLogin", + "Login", +] + + +class CurrentStepUserInput(BaseModel): + fields: List[LoginInputField] + + step_id: str = FieldInfo(alias="stepID") + + type: Literal["user_input"] + + attachments: Optional[List[Optional[object]]] = None + + instructions: Optional[str] = None + """User-facing instructions for this step.""" + + +class CurrentStepCookies(BaseModel): + fields: List[CookieField] + + step_id: str = FieldInfo(alias="stepID") + + type: Literal["cookies"] + + url: str + """URL to open for the user.""" + + expected_final_url_regex: Optional[str] = FieldInfo(alias="expectedFinalURLRegex", default=None) + """Regular expression that identifies the final URL after sign-in.""" + + extract_js: Optional[str] = FieldInfo(alias="extractJS", default=None) + """Optional extraction script for browser-based sign-in helpers. + + Treat as an opaque helper value. + """ + + instructions: Optional[str] = None + """User-facing instructions for this browser step.""" + + user_agent: Optional[str] = FieldInfo(alias="userAgent", default=None) + """Suggested user agent for the browser session.""" + + +class CurrentStepDisplayAndWaitDisplayQrCode(BaseModel): + data: str + + type: Literal["qr"] + + +class CurrentStepDisplayAndWaitDisplayEmoji(BaseModel): + image_url: str = FieldInfo(alias="imageURL") + + type: Literal["emoji"] + + +class CurrentStepDisplayAndWaitDisplayEmpty(BaseModel): + type: Literal["nothing"] + + +CurrentStepDisplayAndWaitDisplay: TypeAlias = Union[ + CurrentStepDisplayAndWaitDisplayQrCode, CurrentStepDisplayAndWaitDisplayEmoji, CurrentStepDisplayAndWaitDisplayEmpty +] + + +class CurrentStepDisplayAndWait(BaseModel): + display: CurrentStepDisplayAndWaitDisplay + + step_id: str = FieldInfo(alias="stepID") + + type: Literal["display_and_wait"] + + instructions: Optional[str] = None + """User-facing instructions for this step.""" + + +class CurrentStepCompleteLogin(BaseModel): + """Signed-in identity for a bridge. + + One bridge login can contain multiple chat accounts. + """ + + bridge_id: str = FieldInfo(alias="bridgeID") + """Bridge ID.""" + + login_id: str = FieldInfo(alias="loginID") + """Bridge login ID.""" + + remove_scopes: List[Literal["current-device", "all-devices"]] = FieldInfo(alias="removeScopes") + + status: Literal["connected", "connecting", "needs_login", "logged_out", "unknown"] + + account_ids: Optional[List[str]] = FieldInfo(alias="accountIDs", default=None) + """Chat accounts that belong to this bridge login, when known.""" + + status_text: Optional[str] = FieldInfo(alias="statusText", default=None) + """Human-friendly bridge login status text.""" + + user: Optional[User] = None + """User the account belongs to.""" + + +class CurrentStepComplete(BaseModel): + type: Literal["complete"] + + account: Optional[Account] = None + """A chat account added to Beeper.""" + + instructions: Optional[str] = None + """Completion instructions, when provided.""" + + login: Optional[CurrentStepCompleteLogin] = None + """Signed-in identity for a bridge. + + One bridge login can contain multiple chat accounts. + """ + + step_id: Optional[str] = FieldInfo(alias="stepID", default=None) + + +CurrentStep: TypeAlias = Union[CurrentStepUserInput, CurrentStepCookies, CurrentStepDisplayAndWait, CurrentStepComplete] + + +class Login(BaseModel): + """Signed-in identity for a bridge. + + One bridge login can contain multiple chat accounts. + """ + + bridge_id: str = FieldInfo(alias="bridgeID") + """Bridge ID.""" + + login_id: str = FieldInfo(alias="loginID") + """Bridge login ID.""" + + remove_scopes: List[Literal["current-device", "all-devices"]] = FieldInfo(alias="removeScopes") + + status: Literal["connected", "connecting", "needs_login", "logged_out", "unknown"] + + account_ids: Optional[List[str]] = FieldInfo(alias="accountIDs", default=None) + """Chat accounts that belong to this bridge login, when known.""" + + status_text: Optional[str] = FieldInfo(alias="statusText", default=None) + """Human-friendly bridge login status text.""" + + user: Optional[User] = None + """User the account belongs to.""" + + +class LoginSession(BaseModel): + bridge_id: str = FieldInfo(alias="bridgeID") + """Bridge ID.""" + + login_session_id: str = FieldInfo(alias="loginSessionID") + """Temporary bridge login session ID.""" + + status: Literal[ + "waiting_for_input", "waiting_for_cookies", "waiting_for_display", "complete", "cancelled", "failed" + ] + + account: Optional[Account] = None + """A chat account added to Beeper.""" + + account_id: Optional[str] = FieldInfo(alias="accountID", default=None) + """Chat account ID for reconnect flows, when known.""" + + current_step: Optional[CurrentStep] = FieldInfo(alias="currentStep", default=None) + """Step the client should show or complete next. + + Omitted when the session is complete, cancelled, or failed. + """ + + error: Optional[APIError] = None + + login: Optional[Login] = None + """Signed-in identity for a bridge. + + One bridge login can contain multiple chat accounts. + """ + + login_id: Optional[str] = FieldInfo(alias="loginID", default=None) + """Bridge login ID for reconnect flows, when known.""" diff --git a/src/beeper_desktop_api/types/message_delete_params.py b/src/beeper_desktop_api/types/message_delete_params.py index f8c7fff..ee4ca5e 100644 --- a/src/beeper_desktop_api/types/message_delete_params.py +++ b/src/beeper_desktop_api/types/message_delete_params.py @@ -14,8 +14,8 @@ class MessageDeleteParams(TypedDict, total=False): chat_id: Required[Annotated[str, PropertyInfo(alias="chatID")]] """Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop installation - when available. + Input routes also accept the local chat ID from this installation when + available. """ for_everyone: Annotated[Optional[bool], PropertyInfo(alias="forEveryone")] diff --git a/src/beeper_desktop_api/types/message_search_params.py b/src/beeper_desktop_api/types/message_search_params.py index e9bab35..09b3336 100644 --- a/src/beeper_desktop_api/types/message_search_params.py +++ b/src/beeper_desktop_api/types/message_search_params.py @@ -66,12 +66,11 @@ class MessageSearchParams(TypedDict, total=False): """ query: str - """Literal word search (non-semantic). + """Literal word search. - Finds messages containing these EXACT words in any order. Use single words users - actually type, not concepts or phrases. Example: use "dinner" not "dinner - plans", use "sick" not "health issues". If omitted, returns results filtered - only by other parameters. + Finds messages containing these words in any order. Use words the user actually + typed, not inferred concepts. Example: use "dinner" rather than "dinner plans". + If omitted, returns results filtered only by the other parameters. """ sender: str diff --git a/src/beeper_desktop_api/types/message_send_params.py b/src/beeper_desktop_api/types/message_send_params.py index 78de9d1..f73d26d 100644 --- a/src/beeper_desktop_api/types/message_send_params.py +++ b/src/beeper_desktop_api/types/message_send_params.py @@ -19,8 +19,8 @@ class MessageSendParams(TypedDict, total=False): text: str """Draft text. - Plain text and Markdown are converted to Matrix HTML with the same rules used by - send and edit. + Plain text and Markdown are converted to Beeper rich text with the same rules + used by send and edit. """ diff --git a/src/beeper_desktop_api/types/message_send_response.py b/src/beeper_desktop_api/types/message_send_response.py index 37b739c..82ae9ce 100644 --- a/src/beeper_desktop_api/types/message_send_response.py +++ b/src/beeper_desktop_api/types/message_send_response.py @@ -11,8 +11,8 @@ class MessageSendResponse(BaseModel): chat_id: str = FieldInfo(alias="chatID") """Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop installation - when available. + Input routes also accept the local chat ID from this installation when + available. """ pending_message_id: str = FieldInfo(alias="pendingMessageID") diff --git a/src/beeper_desktop_api/types/message_update_params.py b/src/beeper_desktop_api/types/message_update_params.py index 9f62d93..024259b 100644 --- a/src/beeper_desktop_api/types/message_update_params.py +++ b/src/beeper_desktop_api/types/message_update_params.py @@ -13,8 +13,8 @@ class MessageUpdateParams(TypedDict, total=False): chat_id: Required[Annotated[str, PropertyInfo(alias="chatID")]] """Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop installation - when available. + Input routes also accept the local chat ID from this installation when + available. """ text: Required[str] diff --git a/src/beeper_desktop_api/types/provisioning_capabilities.py b/src/beeper_desktop_api/types/provisioning_capabilities.py new file mode 100644 index 0000000..2a6d15a --- /dev/null +++ b/src/beeper_desktop_api/types/provisioning_capabilities.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from .._models import BaseModel +from .group_type_capabilities import GroupTypeCapabilities +from .resolve_identifier_capabilities import ResolveIdentifierCapabilities + +__all__ = ["ProvisioningCapabilities"] + + +class ProvisioningCapabilities(BaseModel): + """Advanced network capabilities for account lookup and group creation.""" + + group_creation: Dict[str, GroupTypeCapabilities] + + resolve_identifier: ResolveIdentifierCapabilities + """Identifier lookup capabilities for this bridge.""" + + image_pack_import: Optional[bool] = None diff --git a/src/beeper_desktop_api/types/resolve_identifier_capabilities.py b/src/beeper_desktop_api/types/resolve_identifier_capabilities.py new file mode 100644 index 0000000..23038a6 --- /dev/null +++ b/src/beeper_desktop_api/types/resolve_identifier_capabilities.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["ResolveIdentifierCapabilities"] + + +class ResolveIdentifierCapabilities(BaseModel): + """Identifier lookup capabilities for this bridge.""" + + any_phone: bool + + contact_list: bool + + create_dm: bool + + lookup_email: bool + + lookup_phone: bool + + lookup_username: bool + + search: bool diff --git a/src/beeper_desktop_api/types/shared/__init__.py b/src/beeper_desktop_api/types/shared/__init__.py index cb669ed..c29fad6 100644 --- a/src/beeper_desktop_api/types/shared/__init__.py +++ b/src/beeper_desktop_api/types/shared/__init__.py @@ -4,4 +4,6 @@ from .error import Error as Error from .message import Message as Message from .reaction import Reaction as Reaction +from .api_error import APIError as APIError from .attachment import Attachment as Attachment +from .app_state_snapshot import AppStateSnapshot as AppStateSnapshot diff --git a/src/beeper_desktop_api/types/shared/api_error.py b/src/beeper_desktop_api/types/shared/api_error.py new file mode 100644 index 0000000..1f000f0 --- /dev/null +++ b/src/beeper_desktop_api/types/shared/api_error.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from ..._models import BaseModel + +__all__ = ["APIError"] + + +class APIError(BaseModel): + code: str + + message: str + + details: Optional[Dict[str, Optional[object]]] = None diff --git a/src/beeper_desktop_api/types/shared/app_state_snapshot.py b/src/beeper_desktop_api/types/shared/app_state_snapshot.py new file mode 100644 index 0000000..58eea37 --- /dev/null +++ b/src/beeper_desktop_api/types/shared/app_state_snapshot.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "AppStateSnapshot", + "E2EE", + "E2EESecrets", + "Matrix", + "Verification", + "VerificationError", + "VerificationOtherDevice", + "VerificationQr", + "VerificationSAS", +] + + +class E2EESecrets(BaseModel): + """Encrypted messaging keys available on this device.""" + + master_key: bool = FieldInfo(alias="masterKey") + """Whether the account identity key is available.""" + + megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey") + """Whether the encrypted message backup key is available.""" + + recovery_key: bool = FieldInfo(alias="recoveryKey") + """Whether a recovery key is available.""" + + self_signing_key: bool = FieldInfo(alias="selfSigningKey") + """Whether the device trust key is available.""" + + user_signing_key: bool = FieldInfo(alias="userSigningKey") + """Whether the user trust key is available.""" + + +class E2EE(BaseModel): + """Encrypted messaging setup status.""" + + cross_signing: bool = FieldInfo(alias="crossSigning") + """Whether this account can verify trusted devices.""" + + first_sync_done: bool = FieldInfo(alias="firstSyncDone") + """Whether the first encrypted message sync is complete.""" + + has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey") + """Whether the user confirmed that they saved their recovery key.""" + + initialized: bool + """Whether encrypted messaging setup has started.""" + + key_backup: bool = FieldInfo(alias="keyBackup") + """Whether encrypted message backup is available.""" + + secrets: E2EESecrets + """Encrypted messaging keys available on this device.""" + + secret_storage: bool = FieldInfo(alias="secretStorage") + """Whether secure key storage is available.""" + + verified: bool + """Whether this device is trusted for encrypted messages.""" + + recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None) + """Unix timestamp for when the recovery key was created.""" + + +class Matrix(BaseModel): + """Signed-in account details. Omitted until sign-in is complete.""" + + device_id: str = FieldInfo(alias="deviceID") + """Current device ID.""" + + homeserver: str + """Beeper homeserver URL for this account.""" + + user_id: str = FieldInfo(alias="userID") + """Signed-in Beeper user ID.""" + + +class VerificationError(BaseModel): + """Verification error details, if verification stopped.""" + + code: str + """Verification error code.""" + + reason: str + """User-facing verification error message.""" + + +class VerificationOtherDevice(BaseModel): + """Other device participating in verification.""" + + id: str + """Other device ID.""" + + name: Optional[str] = None + """Other device display name, if known.""" + + +class VerificationQr(BaseModel): + """QR verification data.""" + + data: str + """QR code payload to display for verification.""" + + +class VerificationSAS(BaseModel): + """Emoji or number comparison data for verification.""" + + emojis: str + """Emoji sequence to compare on both devices.""" + + decimals: Optional[str] = None + """Number sequence to compare on both devices.""" + + +class Verification(BaseModel): + """Trusted device verification progress.""" + + id: str + """Verification ID to pass in verification action paths.""" + + available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo( + alias="availableActions" + ) + """Verification actions that are valid for the current state.""" + + direction: Literal["incoming", "outgoing"] + """Whether this device started or received the verification.""" + + methods: List[Literal["qr", "sas"]] + """Verification methods supported for this transaction.""" + + purpose: Literal["login", "device"] + """Why this verification exists.""" + + state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"] + """Current trusted-device verification state.""" + + error: Optional[VerificationError] = None + """Verification error details, if verification stopped.""" + + other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None) + """Other device participating in verification.""" + + other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None) + """Other Beeper user participating in verification.""" + + qr: Optional[VerificationQr] = None + """QR verification data.""" + + sas: Optional[VerificationSAS] = None + """Emoji or number comparison data for verification.""" + + +class AppStateSnapshot(BaseModel): + e2ee: E2EE + """Encrypted messaging setup status.""" + + state: Literal[ + "needs-login", + "initializing", + "needs-cross-signing-setup", + "needs-verification", + "needs-secrets", + "needs-first-sync", + "ready", + ] + """ + Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper + Server. + """ + + matrix: Optional[Matrix] = None + """Signed-in account details. Omitted until sign-in is complete.""" + + verification: Optional[Verification] = None + """Trusted device verification progress.""" diff --git a/src/beeper_desktop_api/types/shared/attachment.py b/src/beeper_desktop_api/types/shared/attachment.py index 1ec39f6..cd7ed3b 100644 --- a/src/beeper_desktop_api/types/shared/attachment.py +++ b/src/beeper_desktop_api/types/shared/attachment.py @@ -36,7 +36,7 @@ class Attachment(BaseModel): """Attachment type.""" id: Optional[str] = None - """Attachment identifier (typically an mxc:// URL). + """Attachment identifier, typically an mxc:// URL. Use the download file endpoint to get a local file path. """ @@ -65,7 +65,7 @@ class Attachment(BaseModel): poster_img: Optional[str] = FieldInfo(alias="posterImg", default=None) """Preview image URL for video attachments (poster frame). - May be temporary or local-only to this device; download promptly if durable + May be temporary or available only on this device; download promptly if durable access is needed. """ @@ -75,7 +75,7 @@ class Attachment(BaseModel): src_url: Optional[str] = FieldInfo(alias="srcURL", default=None) """Public URL or local file path to fetch the file. - May be temporary or local-only to this device; download promptly if durable + May be temporary or available only on this device; download promptly if durable access is needed. """ diff --git a/src/beeper_desktop_api/types/shared/message.py b/src/beeper_desktop_api/types/shared/message.py index 40206de..2f74fe4 100644 --- a/src/beeper_desktop_api/types/shared/message.py +++ b/src/beeper_desktop_api/types/shared/message.py @@ -33,14 +33,14 @@ class Link(BaseModel): favicon: Optional[str] = None """Favicon URL if available. - May be temporary or local-only to this device; download promptly if durable + May be temporary or available only on this device; download promptly if durable access is needed. """ img: Optional[str] = None """Preview image URL if available. - May be temporary or local-only to this device; download promptly if durable + May be temporary or available only on this device; download promptly if durable access is needed. """ @@ -67,7 +67,10 @@ class SendStatus(BaseModel): """User IDs the message was delivered to, when reported by the network.""" internal_error: Optional[str] = FieldInfo(alias="internalError", default=None) - """Internal bridge error detail. Intended for diagnostics, not end-user display.""" + """Diagnostic error detail from the messaging network adapter. + + Do not show directly to users. + """ message: Optional[str] = None """Human-readable send status or failure message.""" @@ -86,14 +89,14 @@ class Message(BaseModel): chat_id: str = FieldInfo(alias="chatID") """Chat ID. - Input routes also accept the local chat ID from this Beeper Desktop installation - when available. + Input routes also accept the local chat ID from this installation when + available. """ sender_id: str = FieldInfo(alias="senderID") - """ - Matrix-style fully-qualified sender user ID, usually including a bridge prefix - and homeserver. + """Fully qualified sender user ID. + + Network-backed IDs usually include the network prefix and homeserver. """ sort_key: str = FieldInfo(alias="sortKey") @@ -139,15 +142,13 @@ class Message(BaseModel): """Read receipt state for this message, when available.""" sender_name: Optional[str] = FieldInfo(alias="senderName", default=None) - """ - Resolved sender display name (impersonator/full name/username/participant name). - """ + """Resolved sender display name.""" send_status: Optional[SendStatus] = FieldInfo(alias="sendStatus", default=None) """Message send status for this message, when reported by the bridge.""" text: Optional[str] = None - """Matrix HTML body if present.""" + """Rich-text message body if present.""" type: Optional[ Literal["TEXT", "NOTICE", "IMAGE", "VIDEO", "VOICE", "AUDIO", "FILE", "STICKER", "LOCATION", "REACTION"] diff --git a/src/beeper_desktop_api/types/shared/reaction.py b/src/beeper_desktop_api/types/shared/reaction.py index e28fb69..751db4e 100644 --- a/src/beeper_desktop_api/types/shared/reaction.py +++ b/src/beeper_desktop_api/types/shared/reaction.py @@ -32,6 +32,6 @@ class Reaction(BaseModel): img_url: Optional[str] = FieldInfo(alias="imgURL", default=None) """URL to the reaction's image. - May be temporary or local-only to this device; download promptly if durable + May be temporary or available only on this device; download promptly if durable access is needed. """ diff --git a/src/beeper_desktop_api/types/shared/user.py b/src/beeper_desktop_api/types/shared/user.py index f77a31f..2f170ab 100644 --- a/src/beeper_desktop_api/types/shared/user.py +++ b/src/beeper_desktop_api/types/shared/user.py @@ -30,9 +30,9 @@ class User(BaseModel): img_url: Optional[str] = FieldInfo(alias="imgURL", default=None) """Avatar image URL if available. - This may be a remote URL, Matrix media URL, data URL, or local filesystem URL - depending on source and endpoint. May be temporary or local-only to this device; - download promptly if durable access is needed. + This may be a remote URL, media URL, data URL, or local file URL depending on + the source. May be temporary or available only on this device; download promptly + if durable access is needed. """ is_self: Optional[bool] = FieldInfo(alias="isSelf", default=None) diff --git a/tests/api_resources/app/__init__.py b/tests/api_resources/app/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/app/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/app/login/__init__.py b/tests/api_resources/app/login/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/app/login/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/app/login/verification/__init__.py b/tests/api_resources/app/login/verification/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/app/login/verification/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/app/login/verification/recovery_key/__init__.py b/tests/api_resources/app/login/verification/recovery_key/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/app/login/verification/recovery_key/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/app/login/verification/recovery_key/test_reset.py b/tests/api_resources/app/login/verification/recovery_key/test_reset.py new file mode 100644 index 0000000..9b96cbd --- /dev/null +++ b/tests/api_resources/app/login/verification/recovery_key/test_reset.py @@ -0,0 +1,153 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types.app.login.verification.recovery_key import ( + ResetCreateResponse, + ResetConfirmResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestReset: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: BeeperDesktop) -> None: + reset = client.app.login.verification.recovery_key.reset.create() + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: BeeperDesktop) -> None: + reset = client.app.login.verification.recovery_key.reset.create( + existing_recovery_key="existingRecoveryKey", + ) + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: BeeperDesktop) -> None: + response = client.app.login.verification.recovery_key.reset.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + reset = response.parse() + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: BeeperDesktop) -> None: + with client.app.login.verification.recovery_key.reset.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + reset = response.parse() + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_confirm(self, client: BeeperDesktop) -> None: + reset = client.app.login.verification.recovery_key.reset.confirm( + recovery_key="x", + ) + assert_matches_type(ResetConfirmResponse, reset, path=["response"]) + + @parametrize + def test_raw_response_confirm(self, client: BeeperDesktop) -> None: + response = client.app.login.verification.recovery_key.reset.with_raw_response.confirm( + recovery_key="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + reset = response.parse() + assert_matches_type(ResetConfirmResponse, reset, path=["response"]) + + @parametrize + def test_streaming_response_confirm(self, client: BeeperDesktop) -> None: + with client.app.login.verification.recovery_key.reset.with_streaming_response.confirm( + recovery_key="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + reset = response.parse() + assert_matches_type(ResetConfirmResponse, reset, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncReset: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncBeeperDesktop) -> None: + reset = await async_client.app.login.verification.recovery_key.reset.create() + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncBeeperDesktop) -> None: + reset = await async_client.app.login.verification.recovery_key.reset.create( + existing_recovery_key="existingRecoveryKey", + ) + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.login.verification.recovery_key.reset.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + reset = await response.parse() + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.login.verification.recovery_key.reset.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + reset = await response.parse() + assert_matches_type(ResetCreateResponse, reset, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_confirm(self, async_client: AsyncBeeperDesktop) -> None: + reset = await async_client.app.login.verification.recovery_key.reset.confirm( + recovery_key="x", + ) + assert_matches_type(ResetConfirmResponse, reset, path=["response"]) + + @parametrize + async def test_raw_response_confirm(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.login.verification.recovery_key.reset.with_raw_response.confirm( + recovery_key="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + reset = await response.parse() + assert_matches_type(ResetConfirmResponse, reset, path=["response"]) + + @parametrize + async def test_streaming_response_confirm(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.login.verification.recovery_key.reset.with_streaming_response.confirm( + recovery_key="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + reset = await response.parse() + assert_matches_type(ResetConfirmResponse, reset, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/app/login/verification/test_recovery_key.py b/tests/api_resources/app/login/verification/test_recovery_key.py new file mode 100644 index 0000000..5778b2a --- /dev/null +++ b/tests/api_resources/app/login/verification/test_recovery_key.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types.app.login.verification import RecoveryKeyVerifyResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRecoveryKey: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_verify(self, client: BeeperDesktop) -> None: + recovery_key = client.app.login.verification.recovery_key.verify( + recovery_key="x", + ) + assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"]) + + @parametrize + def test_raw_response_verify(self, client: BeeperDesktop) -> None: + response = client.app.login.verification.recovery_key.with_raw_response.verify( + recovery_key="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + recovery_key = response.parse() + assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"]) + + @parametrize + def test_streaming_response_verify(self, client: BeeperDesktop) -> None: + with client.app.login.verification.recovery_key.with_streaming_response.verify( + recovery_key="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + recovery_key = response.parse() + assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncRecoveryKey: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_verify(self, async_client: AsyncBeeperDesktop) -> None: + recovery_key = await async_client.app.login.verification.recovery_key.verify( + recovery_key="x", + ) + assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"]) + + @parametrize + async def test_raw_response_verify(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.login.verification.recovery_key.with_raw_response.verify( + recovery_key="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + recovery_key = await response.parse() + assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"]) + + @parametrize + async def test_streaming_response_verify(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.login.verification.recovery_key.with_streaming_response.verify( + recovery_key="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + recovery_key = await response.parse() + assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/app/test_login.py b/tests/api_resources/app/test_login.py new file mode 100644 index 0000000..343ef54 --- /dev/null +++ b/tests/api_resources/app/test_login.py @@ -0,0 +1,294 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types.app import ( + LoginStartResponse, + LoginRegisterResponse, + LoginResponseResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLogin: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_email(self, client: BeeperDesktop) -> None: + login = client.app.login.email( + email="dev@stainless.com", + setup_request_id="setupRequestID", + ) + assert login is None + + @parametrize + def test_raw_response_email(self, client: BeeperDesktop) -> None: + response = client.app.login.with_raw_response.email( + email="dev@stainless.com", + setup_request_id="setupRequestID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = response.parse() + assert login is None + + @parametrize + def test_streaming_response_email(self, client: BeeperDesktop) -> None: + with client.app.login.with_streaming_response.email( + email="dev@stainless.com", + setup_request_id="setupRequestID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = response.parse() + assert login is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_register(self, client: BeeperDesktop) -> None: + login = client.app.login.register( + accept_terms=True, + lead_token="leadToken", + setup_request_id="setupRequestID", + username="x", + ) + assert_matches_type(LoginRegisterResponse, login, path=["response"]) + + @parametrize + def test_raw_response_register(self, client: BeeperDesktop) -> None: + response = client.app.login.with_raw_response.register( + accept_terms=True, + lead_token="leadToken", + setup_request_id="setupRequestID", + username="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = response.parse() + assert_matches_type(LoginRegisterResponse, login, path=["response"]) + + @parametrize + def test_streaming_response_register(self, client: BeeperDesktop) -> None: + with client.app.login.with_streaming_response.register( + accept_terms=True, + lead_token="leadToken", + setup_request_id="setupRequestID", + username="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = response.parse() + assert_matches_type(LoginRegisterResponse, login, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_response(self, client: BeeperDesktop) -> None: + login = client.app.login.response( + response="response", + setup_request_id="setupRequestID", + ) + assert_matches_type(LoginResponseResponse, login, path=["response"]) + + @parametrize + def test_raw_response_response(self, client: BeeperDesktop) -> None: + response = client.app.login.with_raw_response.response( + response="response", + setup_request_id="setupRequestID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = response.parse() + assert_matches_type(LoginResponseResponse, login, path=["response"]) + + @parametrize + def test_streaming_response_response(self, client: BeeperDesktop) -> None: + with client.app.login.with_streaming_response.response( + response="response", + setup_request_id="setupRequestID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = response.parse() + assert_matches_type(LoginResponseResponse, login, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_start(self, client: BeeperDesktop) -> None: + login = client.app.login.start() + assert_matches_type(LoginStartResponse, login, path=["response"]) + + @parametrize + def test_raw_response_start(self, client: BeeperDesktop) -> None: + response = client.app.login.with_raw_response.start() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = response.parse() + assert_matches_type(LoginStartResponse, login, path=["response"]) + + @parametrize + def test_streaming_response_start(self, client: BeeperDesktop) -> None: + with client.app.login.with_streaming_response.start() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = response.parse() + assert_matches_type(LoginStartResponse, login, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncLogin: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_email(self, async_client: AsyncBeeperDesktop) -> None: + login = await async_client.app.login.email( + email="dev@stainless.com", + setup_request_id="setupRequestID", + ) + assert login is None + + @parametrize + async def test_raw_response_email(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.login.with_raw_response.email( + email="dev@stainless.com", + setup_request_id="setupRequestID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = await response.parse() + assert login is None + + @parametrize + async def test_streaming_response_email(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.login.with_streaming_response.email( + email="dev@stainless.com", + setup_request_id="setupRequestID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = await response.parse() + assert login is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_register(self, async_client: AsyncBeeperDesktop) -> None: + login = await async_client.app.login.register( + accept_terms=True, + lead_token="leadToken", + setup_request_id="setupRequestID", + username="x", + ) + assert_matches_type(LoginRegisterResponse, login, path=["response"]) + + @parametrize + async def test_raw_response_register(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.login.with_raw_response.register( + accept_terms=True, + lead_token="leadToken", + setup_request_id="setupRequestID", + username="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = await response.parse() + assert_matches_type(LoginRegisterResponse, login, path=["response"]) + + @parametrize + async def test_streaming_response_register(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.login.with_streaming_response.register( + accept_terms=True, + lead_token="leadToken", + setup_request_id="setupRequestID", + username="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = await response.parse() + assert_matches_type(LoginRegisterResponse, login, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_response(self, async_client: AsyncBeeperDesktop) -> None: + login = await async_client.app.login.response( + response="response", + setup_request_id="setupRequestID", + ) + assert_matches_type(LoginResponseResponse, login, path=["response"]) + + @parametrize + async def test_raw_response_response(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.login.with_raw_response.response( + response="response", + setup_request_id="setupRequestID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = await response.parse() + assert_matches_type(LoginResponseResponse, login, path=["response"]) + + @parametrize + async def test_streaming_response_response(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.login.with_streaming_response.response( + response="response", + setup_request_id="setupRequestID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = await response.parse() + assert_matches_type(LoginResponseResponse, login, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_start(self, async_client: AsyncBeeperDesktop) -> None: + login = await async_client.app.login.start() + assert_matches_type(LoginStartResponse, login, path=["response"]) + + @parametrize + async def test_raw_response_start(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.login.with_raw_response.start() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login = await response.parse() + assert_matches_type(LoginStartResponse, login, path=["response"]) + + @parametrize + async def test_streaming_response_start(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.login.with_streaming_response.start() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login = await response.parse() + assert_matches_type(LoginStartResponse, login, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/app/test_verifications.py b/tests/api_resources/app/test_verifications.py new file mode 100644 index 0000000..b94df48 --- /dev/null +++ b/tests/api_resources/app/test_verifications.py @@ -0,0 +1,392 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types.app import ( + VerificationListResponse, + VerificationAcceptResponse, + VerificationCancelResponse, + VerificationCreateResponse, + VerificationRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestVerifications: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: BeeperDesktop) -> None: + verification = client.app.verifications.create() + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: BeeperDesktop) -> None: + verification = client.app.verifications.create( + purpose="login", + user_id="userID", + ) + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: BeeperDesktop) -> None: + response = client.app.verifications.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = response.parse() + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: BeeperDesktop) -> None: + with client.app.verifications.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = response.parse() + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: BeeperDesktop) -> None: + verification = client.app.verifications.retrieve( + "x", + ) + assert_matches_type(VerificationRetrieveResponse, verification, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: BeeperDesktop) -> None: + response = client.app.verifications.with_raw_response.retrieve( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = response.parse() + assert_matches_type(VerificationRetrieveResponse, verification, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None: + with client.app.verifications.with_streaming_response.retrieve( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = response.parse() + assert_matches_type(VerificationRetrieveResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + client.app.verifications.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: BeeperDesktop) -> None: + verification = client.app.verifications.list() + assert_matches_type(VerificationListResponse, verification, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: BeeperDesktop) -> None: + response = client.app.verifications.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = response.parse() + assert_matches_type(VerificationListResponse, verification, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: BeeperDesktop) -> None: + with client.app.verifications.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = response.parse() + assert_matches_type(VerificationListResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_accept(self, client: BeeperDesktop) -> None: + verification = client.app.verifications.accept( + "x", + ) + assert_matches_type(VerificationAcceptResponse, verification, path=["response"]) + + @parametrize + def test_raw_response_accept(self, client: BeeperDesktop) -> None: + response = client.app.verifications.with_raw_response.accept( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = response.parse() + assert_matches_type(VerificationAcceptResponse, verification, path=["response"]) + + @parametrize + def test_streaming_response_accept(self, client: BeeperDesktop) -> None: + with client.app.verifications.with_streaming_response.accept( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = response.parse() + assert_matches_type(VerificationAcceptResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_accept(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + client.app.verifications.with_raw_response.accept( + "", + ) + + @parametrize + def test_method_cancel(self, client: BeeperDesktop) -> None: + verification = client.app.verifications.cancel( + verification_id="x", + ) + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + @parametrize + def test_method_cancel_with_all_params(self, client: BeeperDesktop) -> None: + verification = client.app.verifications.cancel( + verification_id="x", + code="code", + reason="reason", + ) + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: BeeperDesktop) -> None: + response = client.app.verifications.with_raw_response.cancel( + verification_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = response.parse() + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: BeeperDesktop) -> None: + with client.app.verifications.with_streaming_response.cancel( + verification_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = response.parse() + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + client.app.verifications.with_raw_response.cancel( + verification_id="", + ) + + +class TestAsyncVerifications: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncBeeperDesktop) -> None: + verification = await async_client.app.verifications.create() + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncBeeperDesktop) -> None: + verification = await async_client.app.verifications.create( + purpose="login", + user_id="userID", + ) + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = await response.parse() + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = await response.parse() + assert_matches_type(VerificationCreateResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + verification = await async_client.app.verifications.retrieve( + "x", + ) + assert_matches_type(VerificationRetrieveResponse, verification, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.with_raw_response.retrieve( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = await response.parse() + assert_matches_type(VerificationRetrieveResponse, verification, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.with_streaming_response.retrieve( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = await response.parse() + assert_matches_type(VerificationRetrieveResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + await async_client.app.verifications.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None: + verification = await async_client.app.verifications.list() + assert_matches_type(VerificationListResponse, verification, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = await response.parse() + assert_matches_type(VerificationListResponse, verification, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = await response.parse() + assert_matches_type(VerificationListResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_accept(self, async_client: AsyncBeeperDesktop) -> None: + verification = await async_client.app.verifications.accept( + "x", + ) + assert_matches_type(VerificationAcceptResponse, verification, path=["response"]) + + @parametrize + async def test_raw_response_accept(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.with_raw_response.accept( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = await response.parse() + assert_matches_type(VerificationAcceptResponse, verification, path=["response"]) + + @parametrize + async def test_streaming_response_accept(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.with_streaming_response.accept( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = await response.parse() + assert_matches_type(VerificationAcceptResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_accept(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + await async_client.app.verifications.with_raw_response.accept( + "", + ) + + @parametrize + async def test_method_cancel(self, async_client: AsyncBeeperDesktop) -> None: + verification = await async_client.app.verifications.cancel( + verification_id="x", + ) + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + @parametrize + async def test_method_cancel_with_all_params(self, async_client: AsyncBeeperDesktop) -> None: + verification = await async_client.app.verifications.cancel( + verification_id="x", + code="code", + reason="reason", + ) + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.with_raw_response.cancel( + verification_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + verification = await response.parse() + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.with_streaming_response.cancel( + verification_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + verification = await response.parse() + assert_matches_type(VerificationCancelResponse, verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + await async_client.app.verifications.with_raw_response.cancel( + verification_id="", + ) diff --git a/tests/api_resources/app/verifications/__init__.py b/tests/api_resources/app/verifications/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/app/verifications/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/app/verifications/test_qr.py b/tests/api_resources/app/verifications/test_qr.py new file mode 100644 index 0000000..2678078 --- /dev/null +++ b/tests/api_resources/app/verifications/test_qr.py @@ -0,0 +1,162 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types.app.verifications import QrScanResponse, QrConfirmScannedResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestQr: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_confirm_scanned(self, client: BeeperDesktop) -> None: + qr = client.app.verifications.qr.confirm_scanned( + "x", + ) + assert_matches_type(QrConfirmScannedResponse, qr, path=["response"]) + + @parametrize + def test_raw_response_confirm_scanned(self, client: BeeperDesktop) -> None: + response = client.app.verifications.qr.with_raw_response.confirm_scanned( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + qr = response.parse() + assert_matches_type(QrConfirmScannedResponse, qr, path=["response"]) + + @parametrize + def test_streaming_response_confirm_scanned(self, client: BeeperDesktop) -> None: + with client.app.verifications.qr.with_streaming_response.confirm_scanned( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + qr = response.parse() + assert_matches_type(QrConfirmScannedResponse, qr, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_confirm_scanned(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + client.app.verifications.qr.with_raw_response.confirm_scanned( + "", + ) + + @parametrize + def test_method_scan(self, client: BeeperDesktop) -> None: + qr = client.app.verifications.qr.scan( + data="x", + ) + assert_matches_type(QrScanResponse, qr, path=["response"]) + + @parametrize + def test_raw_response_scan(self, client: BeeperDesktop) -> None: + response = client.app.verifications.qr.with_raw_response.scan( + data="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + qr = response.parse() + assert_matches_type(QrScanResponse, qr, path=["response"]) + + @parametrize + def test_streaming_response_scan(self, client: BeeperDesktop) -> None: + with client.app.verifications.qr.with_streaming_response.scan( + data="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + qr = response.parse() + assert_matches_type(QrScanResponse, qr, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncQr: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None: + qr = await async_client.app.verifications.qr.confirm_scanned( + "x", + ) + assert_matches_type(QrConfirmScannedResponse, qr, path=["response"]) + + @parametrize + async def test_raw_response_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.qr.with_raw_response.confirm_scanned( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + qr = await response.parse() + assert_matches_type(QrConfirmScannedResponse, qr, path=["response"]) + + @parametrize + async def test_streaming_response_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.qr.with_streaming_response.confirm_scanned( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + qr = await response.parse() + assert_matches_type(QrConfirmScannedResponse, qr, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + await async_client.app.verifications.qr.with_raw_response.confirm_scanned( + "", + ) + + @parametrize + async def test_method_scan(self, async_client: AsyncBeeperDesktop) -> None: + qr = await async_client.app.verifications.qr.scan( + data="x", + ) + assert_matches_type(QrScanResponse, qr, path=["response"]) + + @parametrize + async def test_raw_response_scan(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.qr.with_raw_response.scan( + data="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + qr = await response.parse() + assert_matches_type(QrScanResponse, qr, path=["response"]) + + @parametrize + async def test_streaming_response_scan(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.qr.with_streaming_response.scan( + data="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + qr = await response.parse() + assert_matches_type(QrScanResponse, qr, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/app/verifications/test_sas.py b/tests/api_resources/app/verifications/test_sas.py new file mode 100644 index 0000000..020880b --- /dev/null +++ b/tests/api_resources/app/verifications/test_sas.py @@ -0,0 +1,176 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types.app.verifications import SASStartResponse, SASConfirmResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSAS: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_confirm(self, client: BeeperDesktop) -> None: + sas = client.app.verifications.sas.confirm( + "x", + ) + assert_matches_type(SASConfirmResponse, sas, path=["response"]) + + @parametrize + def test_raw_response_confirm(self, client: BeeperDesktop) -> None: + response = client.app.verifications.sas.with_raw_response.confirm( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sas = response.parse() + assert_matches_type(SASConfirmResponse, sas, path=["response"]) + + @parametrize + def test_streaming_response_confirm(self, client: BeeperDesktop) -> None: + with client.app.verifications.sas.with_streaming_response.confirm( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sas = response.parse() + assert_matches_type(SASConfirmResponse, sas, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_confirm(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + client.app.verifications.sas.with_raw_response.confirm( + "", + ) + + @parametrize + def test_method_start(self, client: BeeperDesktop) -> None: + sas = client.app.verifications.sas.start( + "x", + ) + assert_matches_type(SASStartResponse, sas, path=["response"]) + + @parametrize + def test_raw_response_start(self, client: BeeperDesktop) -> None: + response = client.app.verifications.sas.with_raw_response.start( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sas = response.parse() + assert_matches_type(SASStartResponse, sas, path=["response"]) + + @parametrize + def test_streaming_response_start(self, client: BeeperDesktop) -> None: + with client.app.verifications.sas.with_streaming_response.start( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sas = response.parse() + assert_matches_type(SASStartResponse, sas, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_start(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + client.app.verifications.sas.with_raw_response.start( + "", + ) + + +class TestAsyncSAS: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_confirm(self, async_client: AsyncBeeperDesktop) -> None: + sas = await async_client.app.verifications.sas.confirm( + "x", + ) + assert_matches_type(SASConfirmResponse, sas, path=["response"]) + + @parametrize + async def test_raw_response_confirm(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.sas.with_raw_response.confirm( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sas = await response.parse() + assert_matches_type(SASConfirmResponse, sas, path=["response"]) + + @parametrize + async def test_streaming_response_confirm(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.sas.with_streaming_response.confirm( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sas = await response.parse() + assert_matches_type(SASConfirmResponse, sas, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_confirm(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + await async_client.app.verifications.sas.with_raw_response.confirm( + "", + ) + + @parametrize + async def test_method_start(self, async_client: AsyncBeeperDesktop) -> None: + sas = await async_client.app.verifications.sas.start( + "x", + ) + assert_matches_type(SASStartResponse, sas, path=["response"]) + + @parametrize + async def test_raw_response_start(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.verifications.sas.with_raw_response.start( + "x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sas = await response.parse() + assert_matches_type(SASStartResponse, sas, path=["response"]) + + @parametrize + async def test_streaming_response_start(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.verifications.sas.with_streaming_response.start( + "x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sas = await response.parse() + assert_matches_type(SASStartResponse, sas, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_start(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"): + await async_client.app.verifications.sas.with_raw_response.start( + "", + ) diff --git a/tests/api_resources/bridges/__init__.py b/tests/api_resources/bridges/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/bridges/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/bridges/login_sessions/__init__.py b/tests/api_resources/bridges/login_sessions/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/bridges/login_sessions/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/bridges/login_sessions/test_steps.py b/tests/api_resources/bridges/login_sessions/test_steps.py new file mode 100644 index 0000000..7b8f9b4 --- /dev/null +++ b/tests/api_resources/bridges/login_sessions/test_steps.py @@ -0,0 +1,182 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types import LoginSession + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSteps: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_submit(self, client: BeeperDesktop) -> None: + step = client.bridges.login_sessions.steps.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) + assert_matches_type(LoginSession, step, path=["response"]) + + @parametrize + def test_method_submit_with_all_params(self, client: BeeperDesktop) -> None: + step = client.bridges.login_sessions.steps.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + fields={"foo": "string"}, + last_url="lastURL", + source="api", + ) + assert_matches_type(LoginSession, step, path=["response"]) + + @parametrize + def test_raw_response_submit(self, client: BeeperDesktop) -> None: + response = client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + step = response.parse() + assert_matches_type(LoginSession, step, path=["response"]) + + @parametrize + def test_streaming_response_submit(self, client: BeeperDesktop) -> None: + with client.bridges.login_sessions.steps.with_streaming_response.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + step = response.parse() + assert_matches_type(LoginSession, step, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_submit(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="x", + bridge_id="", + login_session_id="123", + type="user_input", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"): + client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="", + type="user_input", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `step_id` but received ''"): + client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) + + +class TestAsyncSteps: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_submit(self, async_client: AsyncBeeperDesktop) -> None: + step = await async_client.bridges.login_sessions.steps.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) + assert_matches_type(LoginSession, step, path=["response"]) + + @parametrize + async def test_method_submit_with_all_params(self, async_client: AsyncBeeperDesktop) -> None: + step = await async_client.bridges.login_sessions.steps.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + fields={"foo": "string"}, + last_url="lastURL", + source="api", + ) + assert_matches_type(LoginSession, step, path=["response"]) + + @parametrize + async def test_raw_response_submit(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + step = await response.parse() + assert_matches_type(LoginSession, step, path=["response"]) + + @parametrize + async def test_streaming_response_submit(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.login_sessions.steps.with_streaming_response.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + step = await response.parse() + assert_matches_type(LoginSession, step, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_submit(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + await async_client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="x", + bridge_id="", + login_session_id="123", + type="user_input", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"): + await async_client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="x", + bridge_id="local-whatsapp", + login_session_id="", + type="user_input", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `step_id` but received ''"): + await async_client.bridges.login_sessions.steps.with_raw_response.submit( + step_id="", + bridge_id="local-whatsapp", + login_session_id="123", + type="user_input", + ) diff --git a/tests/api_resources/bridges/test_login_flows.py b/tests/api_resources/bridges/test_login_flows.py new file mode 100644 index 0000000..d14a6fb --- /dev/null +++ b/tests/api_resources/bridges/test_login_flows.py @@ -0,0 +1,100 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types.bridges import LoginFlowListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLoginFlows: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: BeeperDesktop) -> None: + login_flow = client.bridges.login_flows.list( + "local-whatsapp", + ) + assert_matches_type(LoginFlowListResponse, login_flow, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: BeeperDesktop) -> None: + response = client.bridges.login_flows.with_raw_response.list( + "local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_flow = response.parse() + assert_matches_type(LoginFlowListResponse, login_flow, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: BeeperDesktop) -> None: + with client.bridges.login_flows.with_streaming_response.list( + "local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_flow = response.parse() + assert_matches_type(LoginFlowListResponse, login_flow, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + client.bridges.login_flows.with_raw_response.list( + "", + ) + + +class TestAsyncLoginFlows: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None: + login_flow = await async_client.bridges.login_flows.list( + "local-whatsapp", + ) + assert_matches_type(LoginFlowListResponse, login_flow, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.login_flows.with_raw_response.list( + "local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_flow = await response.parse() + assert_matches_type(LoginFlowListResponse, login_flow, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.login_flows.with_streaming_response.list( + "local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_flow = await response.parse() + assert_matches_type(LoginFlowListResponse, login_flow, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + await async_client.bridges.login_flows.with_raw_response.list( + "", + ) diff --git a/tests/api_resources/bridges/test_login_sessions.py b/tests/api_resources/bridges/test_login_sessions.py new file mode 100644 index 0000000..adf87f8 --- /dev/null +++ b/tests/api_resources/bridges/test_login_sessions.py @@ -0,0 +1,313 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types import LoginSession +from beeper_desktop_api.types.bridges import LoginSessionCancelResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLoginSessions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: BeeperDesktop) -> None: + login_session = client.bridges.login_sessions.create( + bridge_id="local-whatsapp", + ) + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: BeeperDesktop) -> None: + login_session = client.bridges.login_sessions.create( + bridge_id="local-whatsapp", + account_id="x", + flow_id="x", + login_id="x", + ) + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: BeeperDesktop) -> None: + response = client.bridges.login_sessions.with_raw_response.create( + bridge_id="local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_session = response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: BeeperDesktop) -> None: + with client.bridges.login_sessions.with_streaming_response.create( + bridge_id="local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_session = response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + client.bridges.login_sessions.with_raw_response.create( + bridge_id="", + ) + + @parametrize + def test_method_retrieve(self, client: BeeperDesktop) -> None: + login_session = client.bridges.login_sessions.retrieve( + login_session_id="123", + bridge_id="local-whatsapp", + ) + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: BeeperDesktop) -> None: + response = client.bridges.login_sessions.with_raw_response.retrieve( + login_session_id="123", + bridge_id="local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_session = response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None: + with client.bridges.login_sessions.with_streaming_response.retrieve( + login_session_id="123", + bridge_id="local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_session = response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + client.bridges.login_sessions.with_raw_response.retrieve( + login_session_id="123", + bridge_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"): + client.bridges.login_sessions.with_raw_response.retrieve( + login_session_id="", + bridge_id="local-whatsapp", + ) + + @parametrize + def test_method_cancel(self, client: BeeperDesktop) -> None: + login_session = client.bridges.login_sessions.cancel( + login_session_id="123", + bridge_id="local-whatsapp", + ) + assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: BeeperDesktop) -> None: + response = client.bridges.login_sessions.with_raw_response.cancel( + login_session_id="123", + bridge_id="local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_session = response.parse() + assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: BeeperDesktop) -> None: + with client.bridges.login_sessions.with_streaming_response.cancel( + login_session_id="123", + bridge_id="local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_session = response.parse() + assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + client.bridges.login_sessions.with_raw_response.cancel( + login_session_id="123", + bridge_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"): + client.bridges.login_sessions.with_raw_response.cancel( + login_session_id="", + bridge_id="local-whatsapp", + ) + + +class TestAsyncLoginSessions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncBeeperDesktop) -> None: + login_session = await async_client.bridges.login_sessions.create( + bridge_id="local-whatsapp", + ) + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncBeeperDesktop) -> None: + login_session = await async_client.bridges.login_sessions.create( + bridge_id="local-whatsapp", + account_id="x", + flow_id="x", + login_id="x", + ) + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.login_sessions.with_raw_response.create( + bridge_id="local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_session = await response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.login_sessions.with_streaming_response.create( + bridge_id="local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_session = await response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + await async_client.bridges.login_sessions.with_raw_response.create( + bridge_id="", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + login_session = await async_client.bridges.login_sessions.retrieve( + login_session_id="123", + bridge_id="local-whatsapp", + ) + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.login_sessions.with_raw_response.retrieve( + login_session_id="123", + bridge_id="local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_session = await response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.login_sessions.with_streaming_response.retrieve( + login_session_id="123", + bridge_id="local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_session = await response.parse() + assert_matches_type(LoginSession, login_session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + await async_client.bridges.login_sessions.with_raw_response.retrieve( + login_session_id="123", + bridge_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"): + await async_client.bridges.login_sessions.with_raw_response.retrieve( + login_session_id="", + bridge_id="local-whatsapp", + ) + + @parametrize + async def test_method_cancel(self, async_client: AsyncBeeperDesktop) -> None: + login_session = await async_client.bridges.login_sessions.cancel( + login_session_id="123", + bridge_id="local-whatsapp", + ) + assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.login_sessions.with_raw_response.cancel( + login_session_id="123", + bridge_id="local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + login_session = await response.parse() + assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.login_sessions.with_streaming_response.cancel( + login_session_id="123", + bridge_id="local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + login_session = await response.parse() + assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + await async_client.bridges.login_sessions.with_raw_response.cancel( + login_session_id="123", + bridge_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"): + await async_client.bridges.login_sessions.with_raw_response.cancel( + login_session_id="", + bridge_id="local-whatsapp", + ) diff --git a/tests/api_resources/test_accounts.py b/tests/api_resources/test_accounts.py index 46ac702..dfe06af 100644 --- a/tests/api_resources/test_accounts.py +++ b/tests/api_resources/test_accounts.py @@ -9,7 +9,7 @@ from tests.utils import assert_matches_type from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop -from beeper_desktop_api.types import AccountListResponse +from beeper_desktop_api.types import AccountListResponse, AccountRetrieveResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,6 +17,44 @@ class TestAccounts: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @parametrize + def test_method_retrieve(self, client: BeeperDesktop) -> None: + account = client.accounts.retrieve( + "accountID", + ) + assert_matches_type(AccountRetrieveResponse, account, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: BeeperDesktop) -> None: + response = client.accounts.with_raw_response.retrieve( + "accountID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account = response.parse() + assert_matches_type(AccountRetrieveResponse, account, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None: + with client.accounts.with_streaming_response.retrieve( + "accountID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account = response.parse() + assert_matches_type(AccountRetrieveResponse, account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.accounts.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_list(self, client: BeeperDesktop) -> None: account = client.accounts.list() @@ -48,6 +86,44 @@ class TestAsyncAccounts: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + account = await async_client.accounts.retrieve( + "accountID", + ) + assert_matches_type(AccountRetrieveResponse, account, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.accounts.with_raw_response.retrieve( + "accountID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account = await response.parse() + assert_matches_type(AccountRetrieveResponse, account, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.accounts.with_streaming_response.retrieve( + "accountID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account = await response.parse() + assert_matches_type(AccountRetrieveResponse, account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.accounts.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None: account = await async_client.accounts.list() diff --git a/tests/api_resources/test_app.py b/tests/api_resources/test_app.py new file mode 100644 index 0000000..5fe739f --- /dev/null +++ b/tests/api_resources/test_app.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types import AppSessionResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestApp: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_session(self, client: BeeperDesktop) -> None: + app = client.app.session() + assert_matches_type(AppSessionResponse, app, path=["response"]) + + @parametrize + def test_raw_response_session(self, client: BeeperDesktop) -> None: + response = client.app.with_raw_response.session() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + app = response.parse() + assert_matches_type(AppSessionResponse, app, path=["response"]) + + @parametrize + def test_streaming_response_session(self, client: BeeperDesktop) -> None: + with client.app.with_streaming_response.session() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + app = response.parse() + assert_matches_type(AppSessionResponse, app, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncApp: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_session(self, async_client: AsyncBeeperDesktop) -> None: + app = await async_client.app.session() + assert_matches_type(AppSessionResponse, app, path=["response"]) + + @parametrize + async def test_raw_response_session(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.app.with_raw_response.session() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + app = await response.parse() + assert_matches_type(AppSessionResponse, app, path=["response"]) + + @parametrize + async def test_streaming_response_session(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.app.with_streaming_response.session() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + app = await response.parse() + assert_matches_type(AppSessionResponse, app, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_bridges.py b/tests/api_resources/test_bridges.py new file mode 100644 index 0000000..91654f1 --- /dev/null +++ b/tests/api_resources/test_bridges.py @@ -0,0 +1,226 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop +from beeper_desktop_api.types import BridgeListResponse, BridgeRetrieveResponse, ProvisioningCapabilities + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBridges: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: BeeperDesktop) -> None: + bridge = client.bridges.retrieve( + "local-whatsapp", + ) + assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: BeeperDesktop) -> None: + response = client.bridges.with_raw_response.retrieve( + "local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bridge = response.parse() + assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None: + with client.bridges.with_streaming_response.retrieve( + "local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bridge = response.parse() + assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + client.bridges.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: BeeperDesktop) -> None: + bridge = client.bridges.list() + assert_matches_type(BridgeListResponse, bridge, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: BeeperDesktop) -> None: + response = client.bridges.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bridge = response.parse() + assert_matches_type(BridgeListResponse, bridge, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: BeeperDesktop) -> None: + with client.bridges.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bridge = response.parse() + assert_matches_type(BridgeListResponse, bridge, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve_capabilities(self, client: BeeperDesktop) -> None: + bridge = client.bridges.retrieve_capabilities( + "local-whatsapp", + ) + assert_matches_type(ProvisioningCapabilities, bridge, path=["response"]) + + @parametrize + def test_raw_response_retrieve_capabilities(self, client: BeeperDesktop) -> None: + response = client.bridges.with_raw_response.retrieve_capabilities( + "local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bridge = response.parse() + assert_matches_type(ProvisioningCapabilities, bridge, path=["response"]) + + @parametrize + def test_streaming_response_retrieve_capabilities(self, client: BeeperDesktop) -> None: + with client.bridges.with_streaming_response.retrieve_capabilities( + "local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bridge = response.parse() + assert_matches_type(ProvisioningCapabilities, bridge, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve_capabilities(self, client: BeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + client.bridges.with_raw_response.retrieve_capabilities( + "", + ) + + +class TestAsyncBridges: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + bridge = await async_client.bridges.retrieve( + "local-whatsapp", + ) + assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.with_raw_response.retrieve( + "local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bridge = await response.parse() + assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.with_streaming_response.retrieve( + "local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bridge = await response.parse() + assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + await async_client.bridges.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None: + bridge = await async_client.bridges.list() + assert_matches_type(BridgeListResponse, bridge, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bridge = await response.parse() + assert_matches_type(BridgeListResponse, bridge, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bridge = await response.parse() + assert_matches_type(BridgeListResponse, bridge, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None: + bridge = await async_client.bridges.retrieve_capabilities( + "local-whatsapp", + ) + assert_matches_type(ProvisioningCapabilities, bridge, path=["response"]) + + @parametrize + async def test_raw_response_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None: + response = await async_client.bridges.with_raw_response.retrieve_capabilities( + "local-whatsapp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bridge = await response.parse() + assert_matches_type(ProvisioningCapabilities, bridge, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None: + async with async_client.bridges.with_streaming_response.retrieve_capabilities( + "local-whatsapp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bridge = await response.parse() + assert_matches_type(ProvisioningCapabilities, bridge, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"): + await async_client.bridges.with_raw_response.retrieve_capabilities( + "", + ) diff --git a/tests/test_models.py b/tests/test_models.py index e71da85..d228ea1 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,8 @@ import json -from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Iterable, Optional, cast from datetime import datetime, timezone -from typing_extensions import Literal, Annotated, TypeAliasType +from collections import deque +from typing_extensions import Literal, Annotated, TypedDict, TypeAliasType import pytest import pydantic @@ -9,7 +10,7 @@ from beeper_desktop_api._utils import PropertyInfo from beeper_desktop_api._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from beeper_desktop_api._models import DISCRIMINATOR_CACHE, BaseModel, construct_type +from beeper_desktop_api._models import DISCRIMINATOR_CACHE, BaseModel, EagerIterable, construct_type class BasicModel(BaseModel): @@ -961,3 +962,56 @@ def __getattr__(self, attr: str) -> Item: ... assert model.a.prop == 1 assert isinstance(model.a, Item) assert model.other == "foo" + + +# NOTE: Workaround for Pydantic Iterable behavior. +# Iterable fields are replaced with a ValidatorIterator and may be consumed +# during serialization, which can cause subsequent dumps to return empty data. +# See: https://github.com/pydantic/pydantic/issues/9541 +@pytest.mark.parametrize( + "data, expected_validated", + [ + ([1, 2, 3], [1, 2, 3]), + ((1, 2, 3), (1, 2, 3)), + (set([1, 2, 3]), set([1, 2, 3])), + (iter([1, 2, 3]), [1, 2, 3]), + ([], []), + ((x for x in [1, 2, 3]), [1, 2, 3]), + (map(lambda x: x, [1, 2, 3]), [1, 2, 3]), + (frozenset([1, 2, 3]), frozenset([1, 2, 3])), + (deque([1, 2, 3]), deque([1, 2, 3])), + ], + ids=["list", "tuple", "set", "iterator", "empty", "generator", "map", "frozenset", "deque"], +) +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction(data: Iterable[int], expected_validated: Iterable[int]) -> None: + class TypeWithIterable(TypedDict): + items: EagerIterable[int] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": data}}) + assert m.data["items"] == expected_validated + + # Verify repeated dumps don't lose data (the original bug) + assert m.model_dump()["data"]["items"] == list(expected_validated) + assert m.model_dump()["data"]["items"] == list(expected_validated) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction_str_falls_back_to_list() -> None: + # str is iterable (over chars), but str(list_of_chars) produces the list's repr + # rather than reconstructing a string from items. We special-case str to fall + # back to list instead of attempting reconstruction. + class TypeWithIterable(TypedDict): + items: EagerIterable[str] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": "hello"}}) + + # falls back to list of chars rather than calling str(["h", "e", "l", "l", "o"]) + assert m.data["items"] == ["h", "e", "l", "l", "o"] + assert m.model_dump()["data"]["items"] == ["h", "e", "l", "l", "o"]