From 9626c9ed1bb4dcb5a60902c1c464fc4d8ddcd358 Mon Sep 17 00:00:00 2001 From: Nick Ninov Date: Tue, 14 Jul 2026 20:40:07 +0300 Subject: [PATCH] PyPI: PIP setup - Rename repository from `bot` to `status_sdk` - Add `pyproject.toml` file - Update code documentation - Move `docker-compose.yaml` in folder `status_sdk` - Update `Account` docker volume paths - Update Agent example - Add `volume_folder` to `Account` --- README.md | 75 ++++++++---- bot/utils.py | 49 -------- docs/account.md | 109 +++++++++-------- docs/images/mac-docker.png | Bin 0 -> 105341 bytes docs/utils.md | 70 ++++++++++- examples/agents/README.md | 111 ++++++++++++++++++ examples/agents/main.py | 10 +- examples/agents/requirements.txt | 5 - examples/agents/tools.py | 2 +- pyproject.toml | 51 ++++++++ requirements.txt | 6 - {bot => status_sdk}/__init__.py | 2 + {bot => status_sdk}/account.py | 14 ++- {assets => status_sdk/assets}/profile.png | Bin {bot => status_sdk}/constants.py | 0 .../docker-compose.yaml | 2 +- {bot => status_sdk}/exceptions.py | 0 {bot => status_sdk}/logger.py | 0 {bot => status_sdk}/signal.py | 0 status_sdk/utils.py | 64 ++++++++++ 20 files changed, 420 insertions(+), 150 deletions(-) delete mode 100644 bot/utils.py create mode 100644 docs/images/mac-docker.png create mode 100644 examples/agents/README.md delete mode 100644 examples/agents/requirements.txt create mode 100644 pyproject.toml delete mode 100644 requirements.txt rename {bot => status_sdk}/__init__.py (60%) rename {bot => status_sdk}/account.py (98%) rename {assets => status_sdk/assets}/profile.png (100%) rename {bot => status_sdk}/constants.py (100%) rename docker-compose.yaml => status_sdk/docker-compose.yaml (93%) rename {bot => status_sdk}/exceptions.py (100%) rename {bot => status_sdk}/logger.py (100%) rename {bot => status_sdk}/signal.py (100%) create mode 100644 status_sdk/utils.py diff --git a/README.md b/README.md index 3350653..0c4ac8f 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ ![Status Python SDK header image](./docs/images/overview-header.png) +[Status](http://status.app/) is a decentralized, open-source super app combining a crypto wallet, messenger, and community spaces. It uses peer-to-peer technology so no central server can censor your messages or access your data. + The initial Python Status Backend was built with testing in mind, instead of easy developer access. The objective of this repository is to make a SDK that is: - **light** - as less external packages when it comes to working with Status App - **fast** - quick to get started with Status Python - **documented** - clear explanations of what was done and **why it was done in a specific way**. -Currently this repository is not on [PyPi](https://pypi.org/) but will be added when core functionality has been devleoped and tested. - ## How it works ```mermaid @@ -25,7 +25,7 @@ graph TB end - subgraph bot[Python SDK] + subgraph bot[status-im/status-python-sdk] REQUIREMENTS[requirements.txt] SDK[class Account] SIGNAL[class Signal] @@ -56,7 +56,7 @@ To access Python funcitonality you will have to set up [Status Backend](https:// sequenceDiagram actor User participant Docker - participant Python@{"alias": "status-im/status-bot"} + participant Python@{"alias": "status-im/status-python-sdk"} participant Github@{"alias": "status-im/status-go" } User ->> Docker: docker-compose up @@ -64,37 +64,72 @@ sequenceDiagram Docker ->> Docker: Build User ->> Docker: Run container User ->> Python: initialize module - Note over User,Python: from bot import Account
account = Account() + Note over User,Python: from status_sdk import Account
account = Account() ``` ### Python -1. Setup environment. [Conda](https://www.anaconda.com/) example: -```bash -conda create -n status-sdk python=3.12 +#### Install + +Clone the repository and move into it: + +``` +git clone https://github.com/status-im/status-python-sdk.git +cd status-python-sdk ``` -**Note**: Code has been tested with **Python 3.12**. +Install the base library: -2. Install requirements - -```bash -pip install -r ./requirements.txt ``` +pip install . +``` + +If you want to modify the library itself without having to reinstall: + +``` +pip install -e . +``` + +#### Uninstall + +``` +pip uninstall status-sdk +``` + ### Docker -Setup [`status-im/status-go`](https://github.com/status-im/status-go/) with the provided `docker-compose.yaml` file. +[`status-im/status-go`](https://github.com/status-im/status-go/) runs from the provided [`docker-compose.yaml`](./status_sdk/docker-compose.yaml) file. It does not use a pre-built image - it builds the backend from source, pulling directly from GitHub. -``` -docker compose up -d -``` -If you would like to initialize and start the container with Python: +To run on Windows, please make sure you have set up [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). It is **required** for the `build: context` above. The SDK invokes Docker through WSL so it can build the Linux image and clone the repository during the build. If the `build` is changed to point to a local repository, then WSL is not required. + +You can set it up in **two** ways. + +#### With Python + +Use [`launch_docker_container`](./docs/utils.md#launch_docker_containercommitnone-wait_seconds5-platformlinuxamd64), which builds and starts the container for you. This is the recommended option, as it handles platform selection and (on Windows) recovers from stale Docker mounts: ```python -from bot import launch_docker_container +from status_sdk import launch_docker_container launch_docker_container() ``` -**Note**: To run on Windows, please make sure you have set up [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). +#### Manually + +Run the compose file yourself. It lives inside the installed package, so point Docker at it: + +``` +docker compose -f status_sdk/docker-compose.yaml up -d +``` + +The compose file reads two variables from the environment. Both have a default, so the command above works as-is, but they can be overridden: + +| Variable | Default | Description | +|-----|-----|-------------| +| `STATUS_GO_REF` | `develop` | The [`status-im/status-go`](https://github.com/status-im/status-go/) git ref (commit SHA, branch or tag) to build from. | +| `STATUS_GO_PLATFORM` | `linux/amd64` | The platform the image is built for. | + +``` +STATUS_GO_REF=2bee8b6a38cdc8f92d74e2dbb8c4e77fbbeea149 STATUS_GO_PLATFORM=linux/amd64 docker compose -f status_sdk/docker-compose.yaml up -d +``` diff --git a/bot/utils.py b/bot/utils.py deleted file mode 100644 index 3cd5a3b..0000000 --- a/bot/utils.py +++ /dev/null @@ -1,49 +0,0 @@ -import shutil, os, subprocess, sys, time -from pathlib import Path -from typing import Optional -from .logger import Logger -from . import exceptions - -def launch_docker_container(commit: Optional[str] = None, wait_seconds: int = 5): - """ - Launch the Status Backend Docker container using `docker-compose.yaml` - - Parameters: - - `commit` - the commit SHA. If no commit is provided, the latest version is pulled - - `wait_seconds` - nunmber of seconds to wait before the code resumes. Sleep prevents calling `class Account` faster than launching the docker container. This only happens when the container already exists and it is must be turned on. - """ - logger = Logger() - platform = sys.platform - is_windows = platform == "win32" - if not shutil.which("docker"): - raise exceptions.DockerError("Please install Docker.") - - logger.info(f"Running Docker on {platform}") - ref = commit if commit else "develop" - DOCKER_COMPOSE_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "docker-compose.yaml") - docker_path = DOCKER_COMPOSE_PATH - if is_windows: - p = Path(DOCKER_COMPOSE_PATH) - drive = p.drive.rstrip(":").lower() - docker_path = f"/mnt/{drive}/" + "/".join(p.parts[1:]) - - cmd = ["env", f"STATUS_GO_REF={ref}", "docker", "compose", "-f", docker_path, "up", "-d", "--build"] - - if is_windows: - if not shutil.which("wsl"): - raise exceptions.DockerError("Please install wsl - https://learn.microsoft.com/en-us/windows/wsl/install.") - cmd.insert(0, "wsl") - - logger.info(f"Running:\n{' '.join(cmd)}") - result = subprocess.run( - cmd, - cwd=os.path.dirname(DOCKER_COMPOSE_PATH), - stderr=subprocess.PIPE, - text=True, - ) - - if result.returncode != 0: - raise exceptions.DockerError(result.stderr.strip()) - - logger.info(f"Sleeping for {wait_seconds}s") - time.sleep(wait_seconds) diff --git a/docs/account.md b/docs/account.md index ec6656f..05ea879 100644 --- a/docs/account.md +++ b/docs/account.md @@ -84,7 +84,7 @@ Wallet features are optional and can be omitted if not required for your use cas ![Status App Wallet](./images/wallet.png) -## `Account(domain="localhost", backend_port=8080, media_port=9000, is_secure=False, backup_folder=None)` +## `Account(domain="localhost", backend_port=8080, media_port=9000, is_secure=False, backup_folder=None, volume_folder=None)` Create a new `Account` instance ready to be logged in. The constructor wires the SDK to a running [Status Backend](https://github.com/status-im/status-go) at the given `domain` and `backend_port`, prepares the local `assets/` folder (used for image uploads, such as the [profile picture](./account.md#profile_picture)) and `backups/` folder (used for [backup uploads](./account.md#backups) and recovery). @@ -94,12 +94,13 @@ Create a new `Account` instance ready to be logged in. The constructor wires the | `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`. | -| `backup_folder` | `str` | No | Absolute path on the host machine where `.bkp` files will be stored and loaded from. If not provided, the SDK's own `backups/` folder is used. See [Backups](./account.md#backups). | +| `backup_folder` | `str` | No | Absolute path on the host machine where `.bkp` files will be created and loaded from. If not provided, the SDK's own `backups/` folder is used. See [Backups](./account.md#backups). | +| `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. | 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. ```python -from bot import Account +from status_sdk import Account account = Account() ``` @@ -107,7 +108,7 @@ account = Account() Use a custom backup folder: ```python -from bot import Account +from status_sdk import Account account = Account(backup_folder=r"C:\\Users\\me\\status-backups") ``` @@ -115,7 +116,7 @@ account = Account(backup_folder=r"C:\\Users\\me\\status-backups") Connect to a Status Backend running on a different host or port: ```python -from bot import Account +from status_sdk import Account account = Account( domain="status-backend.internal", @@ -124,10 +125,20 @@ account = Account( ) ``` +Run against a local clone of the repository instead of with [`launch_docker_container`](./utils.md#launch_docker_container): + +```python +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**: 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. +**Note**: `volume_folder` must match the directory containing the `docker-compose.yaml` actually used to launch Status Backend, since that is what determines where Docker mounts `backups/` and `assets/` on the host. If `volume_folder` points elsewhere, `Account` will create and use folders that are never seen by the running container. + ## Methods ### `login(password, key_uid=None, display_name=None, mnemonic=None, infura_token=None, alchemy_token=None, coingecko_api_key=None)` @@ -156,7 +167,7 @@ Returns the current `Account` instance, allowing method chaining. #### Login with Display name ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -175,7 +186,7 @@ The code above is equivalent to the following screen on Status App: #### Login with ENS ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -194,7 +205,7 @@ You can purchase a **universal username** on Status App: #### Login with `key_uid` ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -207,7 +218,7 @@ account.login(**params) #### Recover account ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -227,7 +238,7 @@ The code above is equivalent to the following screen on Status App: #### Wallet setup ```python -from bot import Account +from status_sdk import Account account = Account() @@ -248,7 +259,7 @@ account.login(**params) Logout from the currently logged-in Status account. This method also clears the internal account state and stops the active messenger session. This function is also supported in `del` and when the script automatically finishes. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -274,7 +285,7 @@ Returns `str` representing the **Docker path** of the generated backup file. The The filename is generated by the Status Backend and follows the pattern `_user_data.bkp`, where `` is the **last 6 characters of the account's compressed public key**. For example, an account whose compressed key ends in `abc123` produces `abc123_user_data.bkp`. Because the suffix is derived deterministically from the account's key, the same account always maps to the same filename, which is how a backup is uniquely associated with its account. ```python -from bot import Account +from status_sdk import Account account = Account(backup_folder=r"C:\\Users\\me\\status-backups") params = { @@ -299,7 +310,7 @@ Send a text message to a specific chat. This method currently supports **text me | `message` | `str` | Yes | The text message to send. | ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -330,7 +341,7 @@ Messages can be fetched from: Returns `list[dict]` containing message objects. Timestamp fields returned by the backend are automatically converted into `datetime.datetime` objects. ```python -from bot import Account +from status_sdk import Account import datetime account = Account() @@ -358,7 +369,7 @@ for message in 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 ```python -from bot import Account +from status_sdk import Account import datetime # For terminal readability only from rich import print as rprint @@ -400,7 +411,7 @@ Modes: Returns the current `Account` instance, allowing method chaining. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -443,7 +454,7 @@ Returns `bool`. | `False` | The contact does not exist or was already removed. | ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -473,7 +484,7 @@ Send a request to join a community using its invitation URL. The method parses t Returns `datetime.datetime` representing when the join request was submitted. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -506,7 +517,7 @@ Returns `pd.DataFrame`. | `source_id` | `str` | Source list from which the token was fetched. | ```python -from bot import Account +from status_sdk import Account account = Account() @@ -539,7 +550,7 @@ Returns `pd.DataFrame`. | `price` | `float` | Token price **for 1 `token_symbol`** in the given fiat currency (only present if `ccy` is provided). If you want to get the amount in the wallet, you must `amount * price`. | ```python -from bot import Account +from status_sdk import Account account = Account() @@ -566,7 +577,7 @@ data = account.get_balance(token_addresses) Access multuple chains: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -594,7 +605,7 @@ data = account.get_balance(token_addresses, chain_ids) Access multiple wallets: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -626,7 +637,7 @@ data = account.get_balance(token_addresses, chain_ids, wallets) Get token prices: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -705,7 +716,7 @@ Returns `pd.DataFrame`, sorted by `timestamp` in descending order (newest first) | `trx_fee` | `float` | Gas fee paid in the chain's native token, computed as `gas_price * gas_used / 10**18`. Populated only for `sent` rows; `0` for `received` rows since the receiver does not pay gas. | ```python -from bot import Account +from status_sdk import Account account = Account() @@ -744,7 +755,7 @@ Returns `str` representing the **transaction hash**. The hash can be appended to Send ETH: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -770,7 +781,7 @@ print(f"Transaction: https://etherscan.io/tx/{tx_hash}") Send an ERC-20 token by symbol: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -795,7 +806,7 @@ tx_hash = account.send_transaction( Send an ERC-20 token by contract address: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -821,7 +832,7 @@ tx_hash = account.send_transaction( Send on a different chain: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -873,7 +884,7 @@ Returns `str` representing the **transaction hash**. The hash can be appended to Swap **ETH** for an **ERC-20** token: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -897,7 +908,7 @@ print(f"Swap: https://etherscan.io/tx/{tx_hash}") Swap **ERC-20** for an **ETH** token: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -921,7 +932,7 @@ print(f"Swap: https://etherscan.io/tx/{tx_hash}") Swap **ERC-20** for an **ERC-20** token: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -965,7 +976,7 @@ Returns `list[dict]`, one entry per locally available account. | `created_at` | `datetime.datetime` | Timestamp when the account was created locally. | ```python -from bot import Account +from status_sdk import Account # For terminal readability only from rich import print as rprint from rich.pretty import Pretty @@ -994,7 +1005,7 @@ Provides information about the currently logged-in account. If `login()` has not | `logged_in_timestamp` | `datetime.datetime` | Timestamp when the account successfully logged in. | ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1013,7 +1024,7 @@ Get or update the current display name of the logged‑in account. Returns `str` when reading the property. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1029,7 +1040,7 @@ print(account.display_name) You can update the display name by assigning a new value: ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1052,7 +1063,7 @@ Get or update the **bio** of the currently logged‑in account. The length of th Returns `str` when reading the property. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1068,7 +1079,7 @@ print(account.bio) The value assigned to `bio` will automatically be converted to a string before being sent to the backend. You can update the bio by assigning a new value: ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1085,7 +1096,7 @@ print(account.bio) You can also **clear the bio** by deleting the property: ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1107,7 +1118,7 @@ Get or update the **profile picture** of the currently logged‑in account. The Returns `PIL.Image.Image` when reading the property, or `None` if no profile picture has been set. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1125,7 +1136,7 @@ if image: The file path assigned to `profile_picture` will be automatically set as the latest profile picture in Status App. If the given file does not exist or the extension is not supported, an **exception will be raised**. Supported image formats are `.jpg`, `.jpeg` and `.png`. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1154,7 +1165,7 @@ The property exposes the following methods: - `signal.expect()` - return a context manager that waits for one or more matching signals to arrive **after** you perform an action. This is the recommended way to make **async message calls**, since it removes the race conditions and infinite-loop risk of `get()`. ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1191,7 +1202,7 @@ Default logger configuration: Example: ```python -from bot import Account +from status_sdk import Account account = Account() @@ -1236,7 +1247,7 @@ Returns `dict[str, dict]` where the key is the contact's **public key**. This ma | `last_updated` | `datetime.datetime` | Timestamp when the contact information was last updated. | ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1304,7 +1315,7 @@ Channel permissions: | `token_gated` | `bool` | Whether the channel requires a token to participate. | ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1341,7 +1352,7 @@ Returns `pd.DataFrame`. | `status_alias` | `str` | Initial display name of the member when the account was created. | ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1370,7 +1381,7 @@ Returns `list[dict]` where each `dict` represents a chat that can be used with [ | `name` | `str` | Either the display name of the user or the community channel name. | ```python -from bot import Account +from status_sdk import Account account = Account() params = { @@ -1400,7 +1411,7 @@ Returns `dict[int, str]`. ```python -from bot import Account +from status_sdk import Account account = Account() @@ -1430,7 +1441,7 @@ Returns `pd.DataFrame`. | `symbol` | `str` | Token symbol (e.g. `ETH`, `USDT`). | ```python -from bot import Account +from status_sdk import Account account = Account() @@ -1448,7 +1459,7 @@ print(account.balance) You can convert the current balance into fiat currency by using a [ISO 4217 currency code](https://www.iso.org/iso-4217-currency-codes.html) in the `[]` accessor: ```python -from bot import Account +from status_sdk import Account account = Account() diff --git a/docs/images/mac-docker.png b/docs/images/mac-docker.png new file mode 100644 index 0000000000000000000000000000000000000000..61413698162634cb214f27211dc7b46ba5ca6cf9 GIT binary patch literal 105341 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%0DC+i&qs0G;zmPJ|9k@u9q7uBJ-yTe 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 literal 0 HcmV?d00001 diff --git a/docs/utils.md b/docs/utils.md index 6e5f75a..dabb72b 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -6,7 +6,7 @@ Helper functions for setting up the Status Backend environment. ## Methods -### `launch_docker_container(commit=None, wait_seconds=5)` +### `launch_docker_container(commit=None, wait_seconds=5, platform="linux/amd64")` Launch Status Backend Docker container in the background using `docker-compose.yaml`. If `docker` is not installed, or if the container fails to start, an **exception will be raised** with the error message from Docker. The container is built from [`status-im/status-go`](https://github.com/status-im/status-go) at the git ref you choose: @@ -16,22 +16,82 @@ context: https://github.com/status-im/status-go.git#${STATUS_GO_REF:-develop} The image is always rebuilt (`docker compose up --build`) so a newly chosen `commit` is picked up instead of reusing a previously built image. +**Note**: The container mounts the SDK's `backups/` and `assets/` folders as Docker volumes. Make sure the repository has **read and write permissions**, otherwise the container will fail to start or [backups](./account.md#backups) and [profile pictures](./account.md#profile_picture) will not be saved. On Docker Desktop the repository must also be a **shared path** (see [Windows](./utils.md#windows) and [Mac](./utils.md#mac)). + | 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. | -| `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-port8080-is_securefalse-backup_foldernone) too quickly will fail to connect. | +| `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-port8080-is_securefalse-backup_foldernone) 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. | +Wait time after container has launched: ```python -from bot import launch_docker_container +from status_sdk import launch_docker_container # Build from the latest develop branch launch_docker_container(wait_seconds=10) +``` +Building from a specific commit: + +```python +from status_sdk import launch_docker_container # Pin a specific status-go commit # https://github.com/status-im/status-go/commit/2bee8b6a38cdc8f92d74e2dbb8c4e77fbbeea149 launch_docker_container(commit="2bee8b6a38cdc8f92d74e2dbb8c4e77fbbeea149") ``` -**Windows Note**: In Docker go to `Settings > Resources > WSL integration` and make sure `Enable integration with my default WSL distro` and `Ubuntu` are **turned on**. +Building for a specific platform: + +```bash +docker buildx ls +``` + +```python +from status_sdk import launch_docker_container + +# Build for Raspberry PI 5 - arm64 +launch_docker_container(platform="linux/arm64") +``` + +#### Windows + +In Docker go to `Settings > Resources > WSL integration` and make sure `Enable integration with my default WSL distro` and `Ubuntu` are **turned on**. + +![Image of Windows Note](./images/wsl-docker.png) + +Docker Desktop creates internal intermediary mounts inside its WSL 2 environment when bind-mounting paths from a WSL distribution into a container. In some cases, these mounts can become **stale**, and the container may fail to start with: + +``` +error while creating mount source path '/run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu/...': file exists +``` + +The only reliable way to clear the cache is to restart WSL. Method `launch_docker_container` will do the following when the container fails to start: + +1. `wsl --shutdown` is run to clear the stale mounts. Keep in mind that this will shut down **every** WSL distribution, not just the one Docker uses. Any other WSL session running at the same time will be terminated. +2. The container is launched again, sleeping `wait_seconds` between each attempt, until it starts. + +WSL boots back up on demand, so no manual step is needed. Docker Desktop does need a moment to bring its backend back up, which is why the retries are spaced out - **increase `wait_seconds` if the container takes a long time to come back**. + +#### Mac + +In Docker go to `Settings > Resources > File Sharing` and make sure the SDK repository is added to **Virtual file shares**. + +![Image of Mac Note](./images/mac-docker.png) + +#### Linux + +Make sure the installed `status_sdk` folder has **read and write permissions**, otherwise the container will fail to start or [backups](./account.md#backups) and [profile pictures](./account.md#profile_picture) will not be saved. + +If the package was installed with `sudo` (for example `sudo pip install`), the folder is owned by `root` and your user cannot write to it. Take ownership of the package so your existing permissions apply (adjust the path to where `status_sdk` is installed): + +```bash +sudo chown -R $USER:$USER /path/to/status_sdk +``` + +`$USER` expands to your current login user, so both the owner and group of every file under `status_sdk` are set to you. If the container writes as a different user than the one that installed the package, `chown` alone is not enough - grant read and write permissions to everyone instead: + +```bash +sudo chmod -R a+rw /path/to/status_sdk +``` -![Image of Note](./images/wsl-docker.png) diff --git a/examples/agents/README.md b/examples/agents/README.md new file mode 100644 index 0000000..11cc596 --- /dev/null +++ b/examples/agents/README.md @@ -0,0 +1,111 @@ +# Onchain Agent + +A **personal crypto assistant** that lives inside Status App. The script logs into a Status account, listens for incoming messages **in real time**, and answers them with a [Groq](https://groq.com/) that has been given tools to read and act on the account. + +The agent can look up balances, tokens, contacts and transaction history - and it can also **send messages, send crypto and swap tokens** on your behalf. + +## How it works + +```mermaid +flowchart LR + contact[Your Status Account] + subgraph bot[Status AI Agent] + listen[listen_messages] + agent[LangChain Agent] + tools[Status Tools] + end + llm[Groq LLM] + backend[status-im/status-go] + + contact -->|message| listen + listen -->|prompt| agent + agent <-->|reasoning| llm + agent -->|tool call| tools + tools <--> backend + agent -->|reply| contact +``` + +## Tools + +Each tool is a thin wrapper around the [Python SDK](../../README.md). They are defined in [`tools.py`](./tools.py), and their arguments are validated by the [pydantic](https://docs.pydantic.dev/) models in [`models.py`](./models.py). + +| Tool | SDK | What the agent can do | +|-----|-----|-------------| +| `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. | +| `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. | +| `search_transactions` | [`get_transactions`](../../docs/account.md#get_transactionsrefreshfalse) | Read historical wallet transactions. | +| `send_message` | [`send_message`](../../docs/account.md#send_messagechat_id-message) | **Send a message** to any chat. | +| `send_transaction` | [`send_transaction`](../../docs/account.md#send_transactionaddress-symbol-amount-chain_id1) | **Send crypto** to any address. | +| `swap_tokens` | [`swap_tokens`](../../docs/account.md#swap_tokensfrom_token-to_token-amount-chain_id1) | **Swap tokens** in the wallet. | + +**Note**: The last three tools move real funds and send real messages. See [Security](./README.md#security). + +## Setup + +### 1. Install + +From the **repository root**, install the SDK with the `agent` dependencies: + +``` +pip install ".[agent]" +``` + +### 2. Configure + +Copy [`env.example`](./env.example) to `.env` in this folder and fill it in: + +``` +cp env.example .env +``` + +| Variable | What it is | +|-----|-------------| +| `PASSWORD` | The password of your Status account. | +| `NAME` | The [display name](../../docs/account.md#display-name) or ENS name of the account. If you have previously logged in with the SDK you can provide an ENS. For first time log ins, it is best to provide a [display name](../../docs/account.md#display-name). | +| `MNEMONIC` | The [recovery phrase](https://status.app/help/profile/understand-your-status-keys-and-recovery-phrase) of the account. Used to recover it into the container. | +| `ALCHEMY_TOKEN` | [Alchemy](https://www.alchemy.com/) token - needed for transaction history. | +| `COINGECKO_API_KEY` | [CoinGecko](https://www.coingecko.com/) key - needed for token prices. | +| `INFURA_TOKEN` | [Infura](https://www.infura.io/) token - needed for Ethereum RPC. | +| `GROQ_API_KEY` | [Groq API key](https://console.groq.com/) for the LLM. | +| `GROQ_MODEL` | The Groq model name, e.g. `llama-3.3-70b-versatile`. | +| `FROM_PUBLIC_KEY` | The public key of the account the bot will **listen and reply to**. This is the account you message the bot *from*. | + +All three wallet keys (`ALCHEMY_TOKEN`, `COINGECKO_API_KEY`, `INFURA_TOKEN`) are required - without all of them the wallet tools raise a custom exception. + +### 3. Run + +The script imports `tools` and `models` as **top-level modules**, so it must be run from inside this folder: + +``` +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: + +``` +[INFO] Running Docker on +[INFO] Successfully logged in! +[INFO] Starting messaging +[INFO] Messaging launched +``` + +Now message the bot from the account matching `FROM_PUBLIC_KEY`. It runs until you stop it with `Ctrl+C`. + +## Security + +**This agent has full control of the Status account and its wallet.** It can send messages as you, transfer crypto out of your wallet, and swap your tokens - and it decides to do so based on the output of an LLM. + +The safeguards in this example are deliberately simple: + +- **One sender only.** Messages are ignored unless `latest_message["from"] == FROM_PUBLIC_KEY`. Anyone else messaging the bot is not processed. +- **Secrets are withheld from prompts.** `get_account_info` strips `password` and `mnemonic` before the LLM ever sees the account details. + +That is the whole boundary. There is **no** spending limit, no confirmation step and no allowlist of receiver addresses. Anyone who can send messages from `FROM_PUBLIC_KEY` - or anyone who can convince the LLM through a [prompt injection](https://en.wikipedia.org/wiki/Prompt_injection) in the chat content - can move funds. + +Use a **dedicated account with a small balance**. Do not point this at a wallet you care about. diff --git a/examples/agents/main.py b/examples/agents/main.py index 03b9253..91b13a7 100644 --- a/examples/agents/main.py +++ b/examples/agents/main.py @@ -2,15 +2,9 @@ from langchain_groq import ChatGroq from langchain.agents import create_agent from dotenv import load_dotenv from typing import Optional -import os, sys -import pandas as pd - -# Temp solution until repo it turned into a PyPI library -# Add the repo root to sys.path so `bot` is importable when running this -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) - +import os import tools -from bot import Account, launch_docker_container +from status_sdk import Account, launch_docker_container class StatusToolKit: diff --git a/examples/agents/requirements.txt b/examples/agents/requirements.txt deleted file mode 100644 index 832104d..0000000 --- a/examples/agents/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -tabulate -langchain -langchain-core -langchain-groq -python-dotenv diff --git a/examples/agents/tools.py b/examples/agents/tools.py index fff2e06..493fc09 100644 --- a/examples/agents/tools.py +++ b/examples/agents/tools.py @@ -5,7 +5,7 @@ import pandas as pd import datetime import models -from bot import Account, exceptions +from status_sdk import Account, exceptions class StatusBaseTool(BaseTool): diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..361afc2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,51 @@ +[build-system] +requires = ["setuptools>=77"] +build-backend = "setuptools.build_meta" + +[project] +name = "status-sdk" +version = "1.0.0" +description = "Private chat. Communities. Multi-chain wallet. Browser. dApps all in one app, powered by SNT." +readme = "README.md" +requires-python = ">=3.12" +license = "MIT" +license-files = ["LICENSE.txt"] +authors = [{ name = "Status Research & Development GmbH" }] +keywords = ["privacy", "decentralized", "messenger", "ethereum", "blockchain", "cryptocurrency"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dependencies = [ + "requests", + "websocket-client", + "websockets", + "pandas", + "pillow", + "eth-abi", +] + +[project.optional-dependencies] +agents = [ + "tabulate", + "langchain", + "langchain-core", + "langchain-groq", + "python-dotenv", +] + +[project.urls] +Source = "https://github.com/status-im/status-python-sdk" +Issues = "https://github.com/status-im/status-python-sdk/issues" +"Status App" = "https://status.app/" +"Status Backend" = "https://github.com/status-im/status-go" + +[tool.setuptools] +packages = ["status_sdk"] + +[tool.setuptools.package-data] +status_sdk = ["docker-compose.yaml"] + diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 43cb841..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -requests -websocket-client -websockets -pandas -pillow -eth-abi diff --git a/bot/__init__.py b/status_sdk/__init__.py similarity index 60% rename from bot/__init__.py rename to status_sdk/__init__.py index 8ae96f6..ac8d1b0 100644 --- a/bot/__init__.py +++ b/status_sdk/__init__.py @@ -1,3 +1,5 @@ from .account import Account from .utils import launch_docker_container from . import exceptions + +__all__ = ["Account", "launch_docker_container", "exceptions"] diff --git a/bot/account.py b/status_sdk/account.py similarity index 98% rename from bot/account.py rename to status_sdk/account.py index 340e229..5537fc5 100644 --- a/bot/account.py +++ b/status_sdk/account.py @@ -35,7 +35,7 @@ class Account: } __ETH_ADDRESS = "0x0000000000000000000000000000000000000000" - def __init__(self, domain: str = "localhost", backend_port: int = 8080, media_port: int = 9000, is_secure: bool = False, backup_folder: Optional[str] = None): + def __init__(self, domain: str = "localhost", backend_port: int = 8080, media_port: int = 9000, is_secure: bool = False, backup_folder: Optional[str] = None, volume_folder: Optional[str] = None): """ Work with your own Status App account @@ -44,7 +44,8 @@ class Account: - `backend_port` - the port to connect to Status Backend. If this is changed, the published port for `backend_port` must be updated to match in `docker-compose.yaml` as well. - `media_port` - the port to connect to Status localhost images. If this is changed, the published port for `media_port` must be updated to match in `docker-compose.yaml` as well. - `is_secure` - if `http` or `https` should be used - - `backup_folder` - where backup files will be created and stored + - `backup_folder` - where backup files will be created and loaded + - `volume_folder` - directory containing the `backups` and `assets` folders mounted into the Status Backend Docker container (folder holding `docker-compose.yaml`). Defaults to this package's own installation folder. Set this when Status Backend is launched from a different `docker-compose.yaml` location, such as a local clone of the repo. """ # Wallet transactions self.__alchemy_token = None @@ -54,16 +55,17 @@ class Account: # Path of the backups in the Docker container for Status Backend self.__docker_backup_folder = "./root/.config/Status/backups" self.__backup_folder = backup_folder + # PyPI installation folder + sdk_folder = volume_folder if volume_folder else os.path.dirname(__file__) # As the docker-compose.yaml folder is at the moment - # NOTE: This might change for initial release - self.__backup_sdk_folder = os.path.join(os.path.dirname(os.path.dirname(__file__)), "backups") + self.__backup_sdk_folder = os.path.join(sdk_folder, "backups") os.makedirs(self.__backup_sdk_folder, exist_ok=True) # Path of where images will be uploaded to Status Backend self.__docker_asset_folder = "./assets" # As the docker-compose.yaml folder is at the moment # NOTE: This might change for initial release - self.__assets_local_folder = os.path.join(os.path.dirname(os.path.dirname(__file__)), "assets") + self.__assets_local_folder = os.path.join(sdk_folder, "assets") os.makedirs(self.__assets_local_folder, exist_ok=True) self.__logger = Logger() @@ -1379,7 +1381,7 @@ class Account: folder = self.__backup_folder if self.__backup_folder else self.__backup_sdk_folder file_name = self.info["compressed_key"][-6:] + "_user_data.bkp" - file_path = os.path.join(folder, self.info["compressed_key"][-6:] + "_user_data.bkp") + file_path = os.path.join(folder, file_name) if not os.path.exists(file_path): self.logger.warning(f"Backup file was not found in {folder}") return diff --git a/assets/profile.png b/status_sdk/assets/profile.png similarity index 100% rename from assets/profile.png rename to status_sdk/assets/profile.png diff --git a/bot/constants.py b/status_sdk/constants.py similarity index 100% rename from bot/constants.py rename to status_sdk/constants.py diff --git a/docker-compose.yaml b/status_sdk/docker-compose.yaml similarity index 93% rename from docker-compose.yaml rename to status_sdk/docker-compose.yaml index d05b6d4..449f9fe 100644 --- a/docker-compose.yaml +++ b/status_sdk/docker-compose.yaml @@ -2,7 +2,7 @@ services: backend: build: context: https://github.com/status-im/status-go.git#${STATUS_GO_REF:-develop} - platform: linux/amd64 + platform: ${STATUS_GO_PLATFORM:-linux/amd64} container_name: status-backend ports: - 8080:8080 # backend_port diff --git a/bot/exceptions.py b/status_sdk/exceptions.py similarity index 100% rename from bot/exceptions.py rename to status_sdk/exceptions.py diff --git a/bot/logger.py b/status_sdk/logger.py similarity index 100% rename from bot/logger.py rename to status_sdk/logger.py diff --git a/bot/signal.py b/status_sdk/signal.py similarity index 100% rename from bot/signal.py rename to status_sdk/signal.py diff --git a/status_sdk/utils.py b/status_sdk/utils.py new file mode 100644 index 0000000..040e9a7 --- /dev/null +++ b/status_sdk/utils.py @@ -0,0 +1,64 @@ +import shutil, os, subprocess, sys, time +from pathlib import Path +from typing import Optional +from .logger import Logger +from . import exceptions + +def launch_docker_container(commit: Optional[str] = None, wait_seconds: int = 5, platform: str = "linux/amd64"): + """ + Launch the Status Backend Docker container using `docker-compose.yaml` + + NOTE: On Windows, Docker Desktop caches the Docker volume bind mounts in the WSL + virtual machine. When the mounts go stale the container cannot start. WSL is + restarted to clear the cache and the container is launched again until it is up. + + Parameters: + - `commit` - the commit SHA. If no commit is provided, the latest version is pulled + - `wait_seconds` - number of seconds to wait before the code resumes. Sleep prevents calling `class Account` faster than launching the docker container. This only happens when the container already exists and it is must be turned on. On Windows the same value is used to wait between retries after WSL has been restarted. + - `platform` - the platform the image is built for. Defaults to `linux/amd64`. Run `docker buildx ls` to see the platforms your Docker installation supports. + """ + logger = Logger() + system = sys.platform + is_windows = system == "win32" + if not shutil.which("docker"): + raise exceptions.DockerError("Please install Docker.") + + logger.info(f"Running Docker on {system}") + ref = commit if commit else "develop" + DOCKER_COMPOSE_PATH = os.path.join(os.path.dirname(__file__), "docker-compose.yaml") + docker_path = DOCKER_COMPOSE_PATH + if is_windows: + p = Path(DOCKER_COMPOSE_PATH) + drive = p.drive.rstrip(":").lower() + docker_path = f"/mnt/{drive}/" + "/".join(p.parts[1:]) + + cmd = ["env", f"STATUS_GO_REF={ref}", f"STATUS_GO_PLATFORM={platform}", "docker", "compose", "-f", docker_path, "up", "-d", "--build"] + + if is_windows: + if not shutil.which("wsl"): + raise exceptions.DockerError("Please install wsl - https://learn.microsoft.com/en-us/windows/wsl/install.") + cmd.insert(0, "wsl") + + logger.info(f"Running:\n{' '.join(cmd)}") + docker_compose_up = lambda: subprocess.run(cmd, cwd=os.path.dirname(DOCKER_COMPOSE_PATH), stderr=subprocess.PIPE, text=True) + result = docker_compose_up() + + if result.returncode != 0 and is_windows: + logger.warning("Command failed! Restarting wsl...") + subprocess.run(["wsl", "--shutdown"]) + attempt = 1 + while result.returncode != 0: + result = docker_compose_up() + if result.returncode == 0: + logger.info(f"Container started on attempt {attempt}!") + break + + logger.warning(f"Attempt {attempt} failed... Sleeping for {wait_seconds}s") + time.sleep(wait_seconds) + attempt += 1 + + if result.returncode != 0: + raise exceptions.DockerError(result.stderr.strip()) + + logger.info(f"Docker Container successfully launched! Sleeping for {wait_seconds}s") + time.sleep(wait_seconds)