Metadata-Version: 2.4
Name: lunartools-py
Version: 0.2.0
Summary: SDK for the LunarTools remote APIs: captcha solving, inbox OTP retrieval, and Discord webhook forwarding.
Project-URL: Homepage, https://github.com/lunaraio/lunartools-py
Project-URL: Repository, https://github.com/lunaraio/lunartools-py
Project-URL: Issues, https://github.com/lunaraio/lunartools-py/issues
License: MIT
License-File: LICENSE
Keywords: captcha,discord,hcaptcha,imap,lunartools,otp,webhook
Requires-Python: >=3.9
Requires-Dist: requests>=2.28
Description-Content-Type: text/markdown

# lunartools-py

Python SDK for the LunarTools remote APIs: captcha solving, inbox OTP retrieval, and Discord webhook forwarding.

Requests are routed to a specific user's running LunarTools toolbox. The toolbox must be open and connected.

```bash
pip install lunartools-py
```

The package installs as `lunartools`:

```python
from lunartools import LunarTools
```

## Quick start

Create a client, then pass an API key per call. The key is created in the toolbox and identifies both the key and the toolbox it belongs to, so no Client ID is needed.

```python
from lunartools import LunarTools

client = LunarTools()

otp = client.otp("lt_ik_...", email="softies_archaic_8x@icloud.com", site="nike")
print(otp.otp_code, "from", otp.imap_email)
```

## Inbox API

`otp()` waits for a one-time code to arrive and returns it. Only unread mail is searched, newest first, and the matched message is marked read.

```python
otp = client.otp(
    api_key,
    email="alias@icloud.com",        # required - indexes the inbox
    imap_email="mailbox@icloud.com", # optional - omit to search every connected account
    site="nike",                     # optional - use a built-in parser
    regex=None,                      # optional - your own pattern, first capture group wins
    from_=None,                      # optional - filter by sender
    timeout_ms=60_000,               # optional - default 60s, max 120s
)
```

Omit `imap_email` and the toolbox searches every connected account, then reports which one served the code in `otp.imap_email`.

With no `site` and no `regex`, the toolbox auto-detects the code (4, 6, or 8 characters), falling back to AI if the heuristic finds nothing.

Built-in sites: `bestbuy`, `crunchyroll`, `disney`, `eql`, `funko`, `goat`, `nike`, `privacy`, `samsclub`, `target`, `topps`, `walmart`, `zumiez`.

`from_` has a trailing underscore because `from` is a Python keyword. It is sent as `from` on the wire.

### Counting by subject

Pass a `subject` to count matching mail instead of extracting a code. Useful for win trackers. Returns immediately and marks nothing read.

```python
result = client.count(api_key, email="alias@icloud.com", subject="WINNER")
print(result.count)
```

## Solving API

```python
result = client.solve(
    api_key,
    captcha_type="hcaptcha",
    page_url="https://example.com/checkout",
    site_key="e94865c2-4231-4c25-9c6e-2b797b2b56cf",
    proxy_url=None,  # optional - omit to use the harvester's own proxy
)
print(result.token, result.solve_ms)
```

## Webhooks

Posts a Discord-shaped payload to your forwarder token, which fans out to every Discord URL configured for it. Embeds without a `timestamp` get one automatically.

```python
from lunartools import WebhookPayload, Embed, EmbedField

result = client.webhook("your-webhook-token", WebhookPayload(
    username="LunarTools",
    embeds=[Embed(
        title="Checkout Success",
        color=0x5865F2,
        fields=[EmbedField(name="Site", value="Nike", inline=True)],
    )],
))

print(f"{result.delivered} of {result.count} delivered")
```

The webhook token comes from the toolbox and is not the same as an API key.

## Errors

Every failure raises a `LunarToolsError` carrying the API's code.

```python
from lunartools import LunarToolsError

try:
    otp = client.otp(api_key, email="alias@icloud.com")
except LunarToolsError as error:
    if error.retryable:
        ...  # client_offline, client_disconnected, auth_unavailable, too_many_inflight
    print(error.code, error.status_code)
```

| Code | Meaning |
|---|---|
| `invalid_key` | Unknown API key for this client |
| `key_disabled` | The key exists but is disabled |
| `client_offline` | The toolbox is not connected |
| `imap_not_connected` | No matching IMAP account is connected |
| `unsupported_site` | `site` is not a built-in parser |
| `invalid_regex` | `regex` failed to compile |
| `otp_timeout` | No code arrived before the deadline |
| `no_harvester` | No accepting harvester of that captcha type is open |
| `solve_timeout` | The solve did not finish before the deadline |
| `too_many_inflight` | Too many concurrent requests for this client |

## Options

```python
client = LunarTools(
    base_url="https://remote.lunaraio.com",
    timeout=150.0,
)
```

Requests are long-polled and can take up to 120s, so keep `timeout` above your per-request `timeout_ms`. The client is also a context manager:

```python
with LunarTools() as client:
    ...
```
