diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ef1e8..05e5529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to `status-python-sdk` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.3] - 2026-10-10 + +### Fixed + +- Updated outdated documentation + +### Changed + +- Split `models.py` into separate files + ## [1.2.2] - 2026-10-08 ### Added @@ -219,6 +229,7 @@ will try to log in without a hashed password - Launch [status-go] Docker container with Python instead of manual `docker compose up -d` setup. - Custom library errors +[1.2.3]: https://github.com/status-im/status-python-sdk/releases/tag/1.2.3 [1.2.2]: https://github.com/status-im/status-python-sdk/releases/tag/1.2.2 [1.2.1]: https://github.com/status-im/status-python-sdk/releases/tag/1.2.1 [1.2.0]: https://github.com/status-im/status-python-sdk/releases/tag/1.2.0 diff --git a/docs/account.md b/docs/account.md index 8f2ad89..5af6270 100644 --- a/docs/account.md +++ b/docs/account.md @@ -6,7 +6,7 @@ The account class allows you to easily work with a Status account. ## Display name -The **display name** is the human‑readable identifier for a Status account. It is used when creating an account, resolving an existing account during [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone), and when updating the account name through the [`display_name`](./account.md#display_name) property. +The **display name** is the human‑readable identifier for a Status account. It is used when creating an account, resolving an existing account during [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone), and when updating the account name through the [`display_name`](./account.md#display_name) property. Display names must follow strict validation rules enforced by the library and expected by the Status application. A valid display name must satisfy all of the following conditions: @@ -50,7 +50,7 @@ Backup files (`.bkp`) can be both created in [Status App](https://our.status.im/ [Status Backend](https://github.com/status-im/status-go) backup folder is exposed in a Docker volume so users can: -- **Upload backup** - by dropping `.bkp` files in the `backups` folder locally (linked to Status Backend Docker container). Backups are automatically uploaded if a [`mnemonic` is provided during `login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone). +- **Upload backup** - by dropping `.bkp` files in the `backups` folder locally (linked to Status Backend Docker container). Backups are automatically uploaded if a [`mnemonic` is provided during `login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone). - **Create backup** - by using [`backup()`](./account.md#backup) or creating one in [Status App](https://our.status.im/status-desktop-v2-35-local-backups-new-home-page-performance-boosts-and-more/). **Note**: Status App will not automatically backup messages. This has to be manually overridden on the app (above screenshot). When using the Python SDK, the messages are automatically stored in the `.bkp` files. @@ -120,7 +120,7 @@ Create a new `Account` instance ready to be logged in. The constructor wires the | Name | Type | Required | Description | |-----|-----|-----|-------------| -| `domain` | `str` | No | Domain where Status Backend is reachable. Defaults to `localhost` when running through [`launch_docker_container`](./utils.md#launch_docker_container) on the same machine. **Use the container name when the SDK runs inside the same Docker network as Status Backend.** | +| `domain` | `str` | No | Domain where Status Backend is reachable. Defaults to `localhost` when running through [`launch_docker_container`](./utils.md#launch_docker_containercommitnone-wait_seconds5-platformlinuxamd64-data_foldernone) on the same machine. **Use the container name when the SDK runs inside the same Docker network as Status Backend.** | | `backend_port` | `int` | No | Port exposed by Status Backend. Defaults to `8080`. If this is changed, the published port for `backend_port` must be updated to match in `docker-compose.yaml` as well. | | `media_port` | `int` | No | Port exposed by the Status media server, used to fetch localhost images such as the [profile picture](./account.md#profile_picture). Defaults to `9000`. If this is changed, the published port for `media_port` must be updated to match in `docker-compose.yaml` as well. | | `is_secure` | `bool` | No | When `True`, the SDK communicates over `https`; otherwise `http` is used. Defaults to `False`. | @@ -128,7 +128,7 @@ Create a new `Account` instance ready to be logged in. The constructor wires the | `volume_folder` | `str` | No | Directory containing the `docker-compose.yaml` whose `backups/` and `assets/` folders are mounted into the Status Backend container. Defaults to this package's own installation folder (e.g. the `status_sdk` folder under `site-packages` when installed via `pip`). Set this when Status Backend is launched from a different `docker-compose.yaml`, such as a local clone of the repository. | | `show_logs` | `bool` | No | Whether `status-go` logs are shown in Docker / Terminal. Defaults to `True`. | -The constructor does not log into any account on its own - call [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) afterwards. To discover what accounts already exist in the configured data directory, use the [`available_accounts`](./account.md#available_accounts) property, which is also populated automatically during initialization. +The constructor does not log into any account on its own - call [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) afterwards. To discover what accounts already exist in the configured data directory, use the [`available_accounts`](./account.md#available_accounts) property, which is also populated automatically during initialization. ```python from status_sdk import Account @@ -156,7 +156,7 @@ account = Account( ) ``` -Run against a local clone of the repository instead of with [`launch_docker_container`](./utils.md#launch_docker_container): +Run against a local clone of the repository instead of with [`launch_docker_container`](./utils.md#launch_docker_containercommitnone-wait_seconds5-platformlinuxamd64-data_foldernone): ```python from status_sdk import Account @@ -164,7 +164,7 @@ from status_sdk import Account account = Account(volume_folder="/path/to/status-python-sdk/status_sdk") ``` -**Note**: Status Backend must be running before initializing `Account`. You can launch the backend container with [`launch_docker_container`](./utils.md#launch_docker_container). If the backend is not reachable on `domain:port`, calls to [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) will fail. +**Note**: Status Backend must be running before initializing `Account`. You can launch the backend container with [`launch_docker_container`](./utils.md#launch_docker_containercommitnone-wait_seconds5-platformlinuxamd64-data_foldernone). If the backend is not reachable on `domain:port`, calls to [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) will fail. **Note**: When `backup_folder` is set, [`backup`](./account.md#backup) moves the generated `.bkp` file out of the SDK's internal `backups/` folder into the provided path, and recovery via `mnemonic` will look in this same folder for `.bkp` files to auto-load. Make sure the folder exists and is writable. @@ -172,7 +172,7 @@ account = Account(volume_folder="/path/to/status-python-sdk/status_sdk") ## Methods -### `login(password, key_uid=None, display_name=None, mnemonic=None, infura_token=None, alchemy_token=None, coingecko_api_key=None)` +### `login(password, key_uid=None, name=None, mnemonic=None, infura_token=None, alchemy_token=None, coingecko_api_key=None)` Login to an existing Status account. If the account does not exist in the initialized data directory, a new account will be created and automatically logged in. @@ -356,7 +356,7 @@ account.login(**params) account.sync("6a2f9c1e-...", "raspberry-pi") ``` -**Note**: [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) **deletes** every installation that is not enabled. A device that was never synced, or that was [unsynced](./account.md#unsyncinstallation_id), is therefore removed on the next login and has to be re-registered by logging in from that device again. +**Note**: [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) **deletes** every installation that is not enabled. A device that was never synced, or that was [unsynced](./account.md#unsyncinstallation_id), is therefore removed on the next login and has to be re-registered by logging in from that device again. ### `unsync(installation_id)` @@ -381,7 +381,7 @@ account.login(**params) account.unsync("6a2f9c1e-...") ``` -**Note**: unsyncing only **disables** the installation, so it can be paired again with [`sync`](./account.md#syncinstallation_id-namenone) within the same session. It does not survive a restart though - the next [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) deletes disabled installations, and the other device has to log in again before it can be synced. +**Note**: unsyncing only **disables** the installation, so it can be paired again with [`sync`](./account.md#syncinstallation_id-namenone) within the same session. It does not survive a restart though - the next [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) deletes disabled installations, and the other device has to log in again before it can be synced. ### Chat @@ -726,7 +726,7 @@ print(f"Deleted: {deleted}") #### `listen_messages()` -Listen for new incoming messages **in real time**. This method yields raw message events as they are received from the Status Backend [signal](./account.md#signallisten) `messages.new`. This method is ideal for developing real time chat applications +Listen for new incoming messages **in real time**. This method yields raw message events as they are received from the Status Backend [signal](./account.md#signal) `messages.new`. This method is ideal for developing real time chat applications ```python from status_sdk import Account @@ -750,7 +750,7 @@ Listen for contact requests **in real time**. . Every yielded event carries a `r | `request_type` | Meaning | |-----|-----| -| `incoming` | Another user sent a contact request to the account. Approve it with [`add_contact`](./account.md#add_contactpublic_key-display_namenone). Use `id` and `public_key` properties from `ContactRequest` | +| `incoming` | Another user sent a contact request to the account. Approve it with [`add_contact`](./account.md#add_contactpublic_key-request_idnone-display_namenone). Use `id` and `public_key` properties from `ContactRequest` | | `accepted` | Another user accepted a contact request that the account had sent. The contact is now mutual. | | `removed` | When a user has removed the account from their contacts. | @@ -904,7 +904,7 @@ Modes: - **Remove** - `has_added_us` is `True` and `added` is `True` - **Reject mode** - `has_added_us` is `True` -Just like [`add_contact`](./account.md#add_contactpublic_key-display_namenone), the contact can be identified in three different ways: +Just like [`add_contact`](./account.md#add_contactpublic_key-request_idnone-display_namenone), the contact can be identified in three different ways: | Format | Example | Key in [`contacts`](./account.md#contacts) | |-------|--------|-----------------| @@ -947,7 +947,7 @@ print(f"Removed: {removed}") Block a user, the same as **Block user** in Status App. Once blocked, the Status Backend stops surfacing that user's messages and contact requests to the account. -Just like [`add_contact`](./account.md#add_contactpublic_key-display_namenone), the contact can be identified in three different ways: +Just like [`add_contact`](./account.md#add_contactpublic_key-request_idnone-display_namenone), the contact can be identified in three different ways: | Format | Example | Key in [`contacts`](./account.md#contacts) | |-------|--------|-----------------| @@ -1265,7 +1265,7 @@ Returns `pd.DataFrame`. #### `get_transactions(refresh=False)` -Retrieve the historical transactions for the **logged-in account wallet** across all chains in [`chains`](./account.md#chains). Data is fetched from the [Alchemy REST API](https://www.alchemy.com/) using the `alchemy_token` provided during [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) and combines three transaction types into a single `DataFrame`: regular transactions (`transaction`), internal transactions (`internal`) and ERC-20 token transfers (`ERC-20`). +Retrieve the historical transactions for the **logged-in account wallet** across all chains in [`chains`](./account.md#chains). Data is fetched from the [Alchemy REST API](https://www.alchemy.com/) using the `alchemy_token` provided during [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) and combines three transaction types into a single `DataFrame`: regular transactions (`transaction`), internal transactions (`internal`) and ERC-20 token transfers (`ERC-20`). | Name | Type | Required | Description | |-----|-----|-----|-------------| @@ -1429,7 +1429,7 @@ tx_hash = account.send_transaction( ) ``` -**Note**: This is a wallet method, so it requires `infura_token`, `alchemy_token` and `coingecko_api_key` to all be provided in [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone). If any is missing, a `WalletNotConfiguredError` is raised when this method is called. +**Note**: This is a wallet method, so it requires `infura_token`, `alchemy_token` and `coingecko_api_key` to all be provided in [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone). If any is missing, a `WalletNotConfiguredError` is raised when this method is called. **Note**: The sender and receiver must be on the **same chain**. Cross-chain transfers are not supported by this method - set `chain_id` to the chain where the funds currently exist. @@ -1546,7 +1546,7 @@ Returns `list[dict]`, one entry per locally available account. |----|----|-------------| | `name` | `str` | The account's name. For accounts using an [ENS](https://status.app/help/profile/transfer-your-ens-name-to-status) name, this is the ENS name (e.g. `malte.stateofus.eth`). For accounts that do not have an ENS name, this will be their display name. | | `is_ens` | `bool` | `True` when `display_name` is an ENS name (ends with `.eth`), otherwise `False`. Useful for telling apart plain display names from universal usernames. | -| `key_uid` | `str` | Internal Status key identifier for the account. Can be passed to [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) as `key_uid`. | +| `key_uid` | `str` | Internal Status key identifier for the account. Can be passed to [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) as `key_uid`. | | `created_at` | `datetime.datetime` | Timestamp when the account was created locally. | ```python @@ -1730,7 +1730,7 @@ When a new profile picture is set, any previous image in the **assets** folder i Get or update the **presence status** of the currently logged‑in account. This is the same presence indicator shown next to the account in Status App, and it controls how the account appears to other users. -Returns `str` when reading the property - one of the options below. After a successful [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone), the status is automatically set to `on`. +Returns `str` when reading the property - one of the options below. After a successful [`login`](./account.md#loginpassword-key_uidnone-namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone), the status is automatically set to `on`. The value is **case‑insensitive** and must be one of the following options: @@ -1811,26 +1811,31 @@ account.signal.disconnect() ### `logger` -Provides access to the internal **Python logger** for monitoring the lifecycle of the account and backend operations such as login, account creation, messenger startup, and recovery. +Provides access to the **Python logger** used for the lifecycle of the account and backend operations such as login, account creation, messenger startup, and recovery. -Returns `logging.Logger`. +Returns `logging.Logger` - a standard logger obtained with `logging.getLogger(__name__)`. -Default logger configuration: - -- **Name**: `status-bot` -- **Level**: `INFO` -- **Output**: standard output (terminal) - -Example: +**The SDK attaches no handler and sets no level.** That is the usual contract for a library: logging is configured by the application, not by the package it imports. The practical consequence is that **nothing is printed until you configure logging yourself** - `info` messages are dropped entirely, and `warning` and above fall through to Python's handler of last resort, which prints the bare message with no timestamp or level. The quickest way is `basicConfig`, which configures the root logger and therefore every library in the process: ```python +import logging from status_sdk import Account +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s" +) + account = Account() +params = { + "name": "status-app-bot", + "password": "SNTPUMP" +} +account.login(**params) +``` -print("Starting Status bot") -account.logger.warning("This is a warning") -account.logger.error("Something went wrong") +``` +2026-10-09 13:39:48 [INFO] status_sdk.account: Successfully logged in! ``` ### `status_go_commit_sha` diff --git a/docs/community.md b/docs/community.md index effa855..cb6ae48 100644 --- a/docs/community.md +++ b/docs/community.md @@ -18,7 +18,7 @@ As of now `Community` works with already created Status App communities. To get - **By invite URL** - pass a shared community `url`. -If the account is already a member, the community is ready to use. Otherwise a **join request is sent** and the instance is left unusable until an administrator accepts it (see [Joining a community](./community.md#joining-a-community)). +If the account is already a member, the community is ready to use. Otherwise a **join request is sent** and the instance is left unusable until an administrator accepts it. Only members can read a community's state, and only privileged members (owner / admin / token master) can [ban](./community.md#banpublic_keys-delete_messagesfalse), [accept](./community.md#acceptpending_request_id) or manage channels. @@ -55,7 +55,7 @@ Create a `Community` instance bound to a **logged-in** [`Account`](./account.md) |-----|-----|-----|-------------| | `account` | `Account` | Yes | A **logged-in** [`Account`](./account.md). If the account is not logged in, a custom exception is raised. | | `community_id` | `str` | No* | The id of a community the account is **already a member of**. Community ids can be obtained from [`communities`](./account.md#communities) on `Account`. | -| `url` | `str` | No* | A shared community invite URL. Used to join the community if the account is not already a member. See [Joining a community](./community.md#joining-a-community). | +| `url` | `str` | No* | A shared community invite URL. Used to join the community if the account is not already a member. See [Membership](./community.md#membership). | | `data_folder` | `str` | No | The folder on **your machine** that [`launch_docker_container`](./utils.md#launch_docker_containercommitnone-wait_seconds5-platformlinuxamd64-data_foldernone) mounts into Status Backend. That is the only place the account data written by Status Backend lives, so a different folder cannot be reached. The path is resolved to its `data` subfolder, so `"status-backend-data"` and `"status-backend-data/data"` are equivalent. This property is only needed when the **same account is logged into Status App**, created a community there, and you want [`status-im/status-go`](https://github.com/status-im/status-go) (Status Backend) to take over as its [control node](./community.md#control-node) - it is where [`upload_control_node`](./community.md#upload_control_nodefolder) writes the uploaded account data. Leave it unset for every other use. | @@ -534,24 +534,20 @@ community.delete_channel("announcements") Listen for join requests to the community **in real time**. -Returns a `Generator` that yields one `models.CommunityRequest` **dataclass** per request event, so the fields are reached as attributes (`request.state`) rather than dictionary keys: +Returns a `Generator` that yields one `models.CommunityRequest` **dataclass** per request event, so the fields are reached as attributes (`request.pending`) rather than dictionary keys: | Attribute | Type | Description | |----|----|-------------| | `id` | `str` | The join request id. Pass this to [`accept`](./community.md#acceptpending_request_id) or [`decline`](./community.md#declinepending_request_id). | -| `state` | `str` | The state the request moved into - see the table below. | | `public_key` | `str` | Public key of the requesting member. | +| `pending` | `bool` | The request is waiting to be [accepted](./community.md#acceptpending_request_id) or [declined](./community.md#declinepending_request_id). | +| `reject` | `bool` | The request was declined. | +| `accept` | `bool` | The request was accepted and the member joined. | +| `cancel` | `bool` | The request was cancelled. | -**Request states** +The state is carried by **four booleans** rather than one string, and **exactly one of them is `True`** on any yielded request - the one matching the state the request just moved into. The rest stay `False`. -| Code | State | Description | -|-----|-----|-------------| -| `1` | `pending` | The request is waiting to be [accepted](./community.md#acceptpending_request_id) or [declined](./community.md#declinepending_request_id). | -| `2` | `reject` | The request was declined. | -| `3` | `accept` | The request was accepted and the member joined. | -| `4` | `cancel` | The request was cancelled. | - -Events belonging to **other communities**, and requests whose state is not one of the four above, are skipped - so everything yielded is a request for this community. +Events belonging to **other communities**, and requests whose state code is not one of the four above, are skipped - so everything yielded is a request for this community. ```python from status_sdk import Account, Community @@ -568,9 +564,9 @@ community = Community(account, url=url) # Auto-accept everyone who asks to join for request in community.listen_requests(): - print(f"{request.public_key}\t{request.state}") + print(f"{request.public_key}\tpending={request.pending}") - if request.state != "pending": + if not request.pending: continue community.accept(request.id) @@ -1309,7 +1305,7 @@ Relay a message that came from **another messaging platform** - Discord, Telegra | `reply_to_message_id` | `str` | No | The **other platform's** id of the message being replied to - *not* a Status message id, unlike in [`send_message`](./community.md#send_messagemessage-reply_to_message_idnone) and [`send_image`](./community.md#send_imagefile_paths-messagenone-reply_to_message_idnone). It threads correctly only when the message it points at was itself relayed with that same value as its `message_id`. | | `image_url` | `str` | No | URL of the author's avatar on the other platform. Defaults to no avatar. | -Returns `str` - the `id` of the message **in Status App**, delegated from [`send_bridged_message`](./account.md#send_bridged_messagechat_id-message-namenone-usernamenone-user_idnone-message_idnone-reply_to_message_idnone-image_urlnone) on `Account`. This is a different value from the `message_id` you passed in, and it can be used with [`delete_message`](./community.md#delete_messageid) like any other sent message. Returns `None` when [`can_post`](./community.md#can_post) is `False` - nothing is relayed and no error is raised. +Returns `str` - the `id` of the message **in Status App**, delegated from [`send_bridged_message`](./account.md#send_bridged_messagechat_id-message-namenone-usernamenone-user_idnone-message_idnone-reply_to_message_idnone-image_urlnone) on `Account`. This is a different value from the `message_id` you passed in, and it can be used with [`delete_message`](./community.md#delete_messageid) like any other sent message. When the account is not allowed to post in the channel, a custom exception is raised carrying the backend's message - the call is **not** silently skipped. ```python from status_sdk import Account, Community diff --git a/docs/group-chat.md b/docs/group-chat.md index 2e49e48..4809d6e 100644 --- a/docs/group-chat.md +++ b/docs/group-chat.md @@ -411,7 +411,7 @@ for message in messages: print(f"{message['timestamp']}\t{message['text']}") ``` -**Note**: This is the group chat equivalent of [`delete_message`](./account.md#delete_messagemessage_id) on `Account`. The only difference is that it first verifies the group chat exists - a custom exception is raised if the chat has not been created or joined. +**Note**: This is the group chat equivalent of [`delete_message`](./account.md#delete_messageid) on `Account`. The only difference is that it first verifies the group chat exists - a custom exception is raised if the chat has not been created or joined. ### `add(public_keys)` diff --git a/docs/utils.md b/docs/utils.md index 0ff3d35..8c1467e 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -20,16 +20,29 @@ The image is always rebuilt (`docker compose up --build`) so a newly chosen `com | Name | Type | Required | Description | |-----|-----|-----|-------------| -| `commit` | `str` | No | The `status-im/status-go` git ref to build from - a commit SHA, branch, or tag. When omitted, the latest `develop` branch is built. | +| `commit` | `str` | No | The `status-im/status-go` git ref to build from - a commit SHA, branch, or tag. When omitted, the newest commit is **resolved to a full SHA** before the build starts - see [Resolving the commit](./utils.md#resolving-the-commit). | | `wait_seconds` | `int` | No | Number of seconds to pause after the `docker compose up` command returns, giving Status Backend enough time to finish booting before subsequent code runs. Defaults to `5`. This matters mainly when the container already exists and is being restarted, because `docker compose up` returns immediately while the backend is still warming up - instantiating [`Account`](./account.md#accountdomainlocalhost-backend_port8080-media_port9000-is_securefalse-backup_foldernone-volume_foldernone-show_logstrue) too quickly will fail to connect. On [Windows](./utils.md#windows) the same value is used to wait between retries after WSL has been restarted. | | `platform` | `str` | No | The platform the image is built for. Defaults to `linux/amd64`. Run `docker buildx ls` to see the platforms your Docker installation supports, and pass the matching value if the default does not build on your machine. | | `data_folder` | `str` | No | The folder on **your machine** where Status Backend keeps the accounts it creates. If you are a **[Community Control Node](./community.md#control-node)** you will need to create a Docker container with a volume folder, and pass that **same** folder to [`Community`](./community.md#communityaccount-community_idnone-urlnone-data_foldernone) so [`upload_control_node`](./community.md#upload_control_nodefolder) can reach it. | +#### Resolving the commit + +When no `commit` is given, the repository is read out of the `context` in `docker-compose.yaml` and its newest commit is looked up through the **GitHub API**, so `STATUS_GO_COMMIT` is always a concrete 40-character SHA rather than a branch name. The resolved value is logged: + +``` +2026-10-09 13:39:48 [INFO] status_sdk.utils.external: status-im/status-go SHA: f89efa62503f4969b8b78a19f7ccf98ef08d4bf5 +``` + +This way: + +- The build is reproducible. +- The version is stamped into the image. + Wait time after container has launched: ```python from status_sdk import launch_docker_container -# Build from the latest develop branch +# Resolves the newest commit, then builds it launch_docker_container(wait_seconds=10) ``` @@ -104,24 +117,26 @@ Build `status-backend` **natively** from a local clone of [`status-im/status-go` |---|---|---| | Needs | Docker (and WSL on Windows) | Nix and `git` | | Windows | Supported | **Not supported** - use Docker, or run from inside WSL | -| Backend runs as | A container | A process on your machine | -| Stopping it | `docker compose down` | Terminate the returned process | +| Backend runs as | A container | A child process of your script | +| Stopping it | `docker compose down` | `Ctrl+C`, or `pkill -f status-backend` | The build runs inside the repository's **Nix dev shell**, so the Go toolchain and every build dependency come from Nix rather than your system - nothing has to be installed by hand beyond Nix itself. | Name | Type | Required | Description | |-----|-----|-----|-------------| -| `commit` | `str` | No | The `status-im/status-go` git commit SHA,. When omitted, the latest `develop` branch is built. | +| `commit` | `str` | No | The `status-im/status-go` git ref to build - a commit SHA, branch, or tag. When omitted, the latest `develop` branch is built. | | `repo_dir` | `str` | No | Local folder to clone `status-go` into, and reuse on later calls. Defaults to a `status-go` folder next to this package's installation. When the folder does not already hold a clone, the repository is fetched from GitHub. | | `address` | `str` | No | The `host:port` to run `status-backend` on. Defaults to `localhost:8080`, which matches the defaults of [`Account`](./account.md#accountdomainlocalhost-backend_port8080-media_port9000-is_securefalse-backup_foldernone-volume_foldernone-show_logstrue). If you change it, pass the matching `domain` and `backend_port` when creating the `Account`. | | `wait_seconds` | `int` | No | How long to poll the backend's `/health` endpoint before giving up, in seconds. Defaults to `30`. **The build itself is not subject to this timeout** - only the startup that follows it. | | `install_deps` | `bool` | No | Whether to run `make status-go-deps` before building. This also runs `go clean -cache` and `go clean -modcache`, which throws away every cached Go build on your machine and makes the next build much slower. Only needed on a first build or after a Go toolchain upgrade. | +Returns `None` - the function waits until the backend answers on `/health` and then hands control back, leaving it running in the background. A custom exception is raised when Nix or `git` are missing, when you are on Windows, when any git or build step fails, when the binary is missing after the build, or when the backend does not become reachable within `wait_seconds`. + ```python from status_sdk import build_and_launch, Account # First run clones status-go and builds it - this takes a while -process = build_and_launch() +build_and_launch() account = Account() params = { @@ -131,9 +146,6 @@ params = { account.login(**params) print(account.info["public_key"]) - -# Stop the backend when you are done -process.terminate() ``` Build a specific ref into a folder of your choosing: @@ -156,6 +168,8 @@ build_and_launch(address="localhost:9500", wait_seconds=60) account = Account(backend_port=9500) ``` +A backend left over from an earlier run is **reused rather than replaced**: the next call sees `/health` already answering, logs `status-backend is already running on
. Skipping launch.` and returns without building anything. That is convenient during development, but it also means you can be talking to an older build than the `commit` you just asked for - stop the old process first when the build matters. + ### `download_build_and_launch(launcher=None, repo_name="status-im/status-go", tag=None, token=None, address="localhost:8080", wait_seconds=30)` Download a prebuilt `status-backend` bundle from a GitHub release, extract it and launch it. This is an **alternative to [`build_and_launch`](./utils.md#build_and_launchcommitnone-repo_dirnone-addresslocalhost8080-wait_seconds30-install_depstrue)** for setups that do not want to build [`status-im/status-go`](https://github.com/status-im/status-go) from source - no Nix or Go toolchain is needed. diff --git a/examples/agents/README.md b/examples/agents/README.md index 64101bb..e0ed42c 100644 --- a/examples/agents/README.md +++ b/examples/agents/README.md @@ -34,7 +34,7 @@ Each tool is a thin wrapper around the [Python SDK](../../README.md). They are d | `get_balance` | [`balance`](../../docs/account.md#balance) | Read the account's wallet balance, optionally enriched with market data. | | `get_account_info` | [`info`](../../docs/account.md#info) | Read public account details. `password` and `mnemonic` are **excluded**. | | `get_account_contacts` | [`contacts`](../../docs/account.md#contacts) | List contacts, contact requests and group chats. | -| `manage_contact` | [`add_contact`](../../docs/account.md#add_contactpublic_key-display_namenone) / `remove_contact` | Accept, send, decline and remove contact requests. | +| `manage_contact` | [`add_contact`](../../docs/account.md#add_contactpublic_key-request_idnone-display_namenone) / `remove_contact` | Accept, send, decline and remove contact requests. | | `get_token_info` | [`get_tokens`](../../docs/account.md#get_tokens) | Look up chains, token symbols and token addresses. | | `search_external_balance` | [`get_balance`](../../docs/account.md#get_balancetoken_addresses-chain_ids1-walletsnone-ccynone) | Read the balance of **any** wallet address, not just the account's. | | `search_messages` | [`get_messages`](../../docs/account.md#get_messageschat_id-start_timestampnone-end_timestampnone) | Read chat history for a date range, including payment requests. | @@ -92,7 +92,7 @@ cd examples/agents python main.py ``` -On the first run, [`launch_docker_container`](../../docs/utils.md#launch_docker_container) builds the Status Backend image, which takes a few minutes. The account is then recovered from `MNEMONIC` and the bot starts listening: +On the first run, [`launch_docker_container`](../../docs/utils.md#launch_docker_containercommitnone-wait_seconds5-platformlinuxamd64-data_foldernone) builds the Status Backend image, which takes a few minutes. The account is then recovered from `MNEMONIC` and the bot starts listening: ``` [INFO] Running Docker on