Metadata-Version: 2.4
Name: signal-cli-rest-api-client
Version: 0.1.0
Summary: Async Python client for the signal-cli-rest-api (json-rpc mode).
Author: whogben
License: MIT License
        
        Copyright (c) 2026 whogben
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/whogben/signal-cli-rest-api-client
Project-URL: Repository, https://github.com/whogben/signal-cli-rest-api-client
Project-URL: Issues, https://github.com/whogben/signal-cli-rest-api-client/issues
Keywords: signal,signal-cli,async,aiohttp,messaging
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Chat
Classifier: Typing :: Typed
Classifier: Framework :: AsyncIO
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.9
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: python-dotenv>=1.0; extra == "dev"
Dynamic: license-file

# signal-cli-rest-api-client

Async Python client for [bbernhard's signal-cli-rest-api](https://github.com/bbernhard/signal-cli-rest-api).

**Requires `signal-cli-rest-api` to be running in [json-rpc mode](https://github.com/bbernhard/signal-cli-rest-api#json-rpc-mode)** (the default `normal` mode will not work).

Covers the endpoints I currently need — sending messages, listening for events, reactions, typing indicators, attachments, and a few others. The upstream API has many more endpoints (groups, contacts, profiles, registration, etc.) that are **not yet wrapped** by this client. See the [coverage table](#api-coverage) below so you know if this meets your needs.

## Install

```bash
pip install signal-cli-rest-api-client
```

Or from source:

```bash
pip install .
```

Requires Python 3.10+ and `aiohttp`.

## Quick start

```python
import asyncio
from signal_cli_rest_api_client.client import SignalClient, encode_attachment

client = SignalClient(
    url="https://signal-api.example.com",
    phone="+1234567890",
    user="admin",        # optional, for basic auth
    password="secret",   # optional, for basic auth
)

async def main():
    # Send a message
    resp = await client.send("+0987654321", "Hello from Python!")

    # Send with an attachment
    attachment = encode_attachment("/path/to/photo.jpg")
    await client.send("+0987654321", "Check this out", base64_attachments=[attachment])

    # Listen for incoming events
    async for event in client.stream_events():
        print(event)

asyncio.run(main())
```

## What's included

### `SignalClient`

| Method | Description |
|---|---|
| `stream_events()` | Open a WebSocket and yield incoming events as dicts |
| `send(recipients, message, ...)` | Send a message via the v2 API (supports attachments, quotes, edits, stickers, styled text, view-once) |
| `submit_rate_limit_challenge(challenge_token, captcha)` | Solve a CAPTCHA to lift rate-limit restrictions |
| `show_typing_indicator(recipient)` | Show "typing..." to a recipient |
| `hide_typing_indicator(recipient)` | Stop showing "typing..." |
| `delete_message(recipient, timestamp)` | Remote-delete a message for everyone |
| `set_reaction(recipient, target_author, timestamp, reaction)` | React to a message with an emoji |
| `clear_reaction(recipient, target_author, timestamp)` | Remove your reaction from a message |
| `send_read_receipt(recipient, timestamp)` | Send a read receipt |
| `list_attachments()` | List all attachment IDs on the server |
| `get_attachment(attachment_id)` | Download attachment bytes by ID |
| `delete_attachment(attachment_id)` | Delete an attachment from the server |

### Helpers

| Symbol | Description |
|---|---|
| `encode_attachment(file_or_bytes, ...)` | Encode a file path or raw bytes into a data-URI string for `send(base64_attachments=...)` |
| `SendError` | Exception raised on 400 responses; carries `challenge_tokens` for rate-limit recovery |

## API coverage

The upstream [signal-cli-rest-api](https://github.com/bbernhard/signal-cli-rest-api) exposes ~40 endpoints. This client wraps the ones listed below. Everything else is **not implemented**.

| Upstream endpoint | Client method | Status |
|---|---|---|
| `POST /v2/send` | `send()` | Implemented |
| `WS /v1/receive/{number}` | `stream_events()` | Implemented |
| `POST /v1/reactions/{number}` | `set_reaction()` | Implemented |
| `DELETE /v1/reactions/{number}` | `clear_reaction()` | Implemented |
| `POST /v1/receipts/{number}` | `send_read_receipt()` | Implemented |
| `DELETE /v1/remote-delete/{number}` | `delete_message()` | Implemented |
| `PUT /v1/typing-indicator/{number}` | `show_typing_indicator()` | Implemented |
| `DELETE /v1/typing-indicator/{number}` | `hide_typing_indicator()` | Implemented |
| `GET /v1/attachments` | `list_attachments()` | Implemented |
| `GET /v1/attachments/{id}` | `get_attachment()` | Implemented |
| `DELETE /v1/attachments/{id}` | `delete_attachment()` | Implemented |
| `POST /v1/accounts/{number}/rate-limit-challenge` | `submit_rate_limit_challenge()` | Implemented |
| Groups (create, list, update, members, admins, join, quit, block, avatar) | — | Not implemented |
| Contacts (list, update, sync, avatar) | — | Not implemented |
| Profiles (get, update) | — | Not implemented |
| Devices (list, link, remove) | — | Not implemented |
| Identities (list, trust) | — | Not implemented |
| Registration & verification | — | Not implemented |
| Account settings, PIN, username | — | Not implemented |
| Polls (create, vote) | — | Not implemented |
| Sticker packs | — | Not implemented |
| Search | — | Not implemented |
| QR code linking | — | Not implemented |
| `/v1/about`, `/v1/health` | — | Not implemented |
| `POST /v1/send` (v1 send) | — | Not implemented (use v2) |
| `GET /v1/receive/{number}` (REST poll) | — | Not implemented (use WebSocket) |

PRs welcome if you need something from the "not implemented" list.

## Configuration

The client expects `signal-cli-rest-api` to be running in **json-rpc mode** (`--mode=json-rpc`). This is different from the default `normal` mode — json-rpc mode is required for the v2 send API and WebSocket event streaming to work. See the [signal-cli-rest-api docs](https://github.com/bbernhard/signal-cli-rest-api#json-rpc-mode) for how to enable it.

Pass the base URL and your registered phone number:

```python
client = SignalClient(url="https://signal.example.com", phone="+1234567890")
```

If your API is behind basic auth, pass `user` and `password` as well.

## Running tests

Tests hit a real Signal API instance and send messages to yourself. Set these in a `.env` file at the project root:

```
SIGNAL_URL=https://signal-api.example.com
SIGNAL_NUMBER=+1234567890
SIGNAL_USER=admin
SIGNAL_PASSWORD=secret
```

Then:

```bash
pip install -e ".[dev]"
pytest
```

## License

MIT
