Metadata-Version: 2.4
Name: pychive
Version: 1.0.0
Summary: A Python wrapper for the Pawchive (pawchive.pw) API.
Author: Pychive
License: MIT
Project-URL: Homepage, https://github.com/NoobToolzz/Pychive
Project-URL: Repository, https://github.com/NoobToolzz/Pychive
Keywords: pawchive,api,wrapper,archive,fanbox,patreon
Classifier: Development Status :: 4 - Beta
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.9
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: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: responses>=0.23; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Dynamic: license-file

# Pychive

**Unofficial** Python wrapper for the [Pawchive](https://pawchive.pw) API.
This project is not affiliated with, endorsed by, or associated with Pawchive.

Pychive provides a clean, typed, object-oriented interface to Pawchive's REST
API — covering creators, posts, comments, announcements, fancards, post
flagging, favorites, file-hash search, and app version info.

- **Base URL:** `https://pawchive.pw/api/v1`
- **Auth:** cookie-based (`session` cookie), passed to the client constructor
- **Python:** 3.9+
- **Dependencies:** [`requests`](https://pypi.org/project/requests/)

## Documentation

Full documentation — installation, quick start, authentication, configuration,
API reference, models, exceptions, error handling, pagination, cookbook, and
changelog — lives in [`docs/index.md`](docs/index.md).

## Installation (local, into a virtualenv)

Pychive uses a standard `src/`-layout package described by `pyproject.toml`,
so it installs cleanly with `pip` (which will pull in `requests` automatically).

### 1. Clone the repository

```bash
git clone https://github.com/NoobToolzz/Pychive.git
cd Pychive
```

### 2. Create and activate a virtualenv

```bash
python3 -m venv .venv

# activate it
source .venv/bin/activate      # Linux / macOS
# .venv\Scripts\activate       # Windows
```

### 3. Install the package

From the project root (where `pyproject.toml` lives):

```bash
# editable install (recommended while developing — picks up edits live)
pip install -e .

# OR a regular install (copies the package into the venv)
pip install .
```

> `pip install -e .` installs in "editable" mode so changes to files under
> `src/pychive/` are reflected immediately without reinstalling.
> `pip install .` builds and installs a copy instead.
>
> Both commands automatically pull in the runtime dependency `requests`.

### 4. (Optional) Install dev tooling

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

This adds `pytest`, `responses`, `ruff`, and `mypy` for testing/linting.

### 5. Verify the install

```bash
python -c "import pychive; print(pychive.__version__)"
```

Expected output: `1.0.0`

### Updating / reinstalling

```bash
pip install -e . --upgrade
```

### Uninstall

```bash
pip uninstall pychive
```

---

## Quick start

```python
from pychive import Pawchive, AuthenticationError, NotFoundError

# Pass the session cookie value. The "session=" prefix is optional,
# and surrounding quotes/spaces are stripped automatically.
with Pawchive("your_session_value_here") as client:
    # Public endpoints (no auth needed)
    creators = client.creators.list_all()
    for c in creators:
        print(c.name, c.service, c.url)

    # Recent posts across all creators (paginated)
    posts = client.posts.list_recent(limit=50, offset=0)
    for p in posts:
        print(p.title, p.published_at)

    # Posts from a specific creator
    fanbox_posts = client.posts.list_from_creator("fanbox", "12345", limit=20)

    # A specific post + its revisions and comments
    post = client.posts.get("fanbox", "12345", "67890")
    revisions = client.posts.list_revisions("fanbox", "12345", "67890")
    comments = client.comments.list("fanbox", "12345", "67890")

    # Creator profile / links / tags / announcements / fancards
    profile = client.creators.get_profile("fanbox", "12345")
    links = client.creators.get_links("fanbox", "12345")
    tags = client.creators.get_tags("fanbox", "12345")
    announcements = client.creators.get_announcements("fanbox", "12345")
    fancards = client.creators.get_fancards("fanbox", "12345")  # fanbox only

    # Authorized endpoints (require a valid session)
    try:
        favs = client.favorites.list()
        client.favorites.add_post("fanbox", "12345", "67890")
        client.favorites.add_creator("fanbox", "12345")
        client.flags.flag("fanbox", "12345", "67890")
        flag_status = client.flags.get_status("fanbox", "12345", "67890")
    except AuthenticationError:
        print("Session is invalid or expired.")

    # File search by hash + app version
    result = client.search.lookup_hash("abcdef1234567890")
    version = client.misc.app_version()
```

### Getting your session cookie

1. Log in to <https://pawchive.pw> in your browser.
2. Open DevTools → **Application** (Chrome) / **Storage** (Firefox) → **Cookies**.
3. Copy the value of the `session` cookie.
4. Pass it to `Pawchive(...)` — either as the bare value or as `session=<value>`.

You can also load it from an environment variable:

```python
import os
from pychive import Pawchive

session = os.environ["PAWCHIVE_SESSION"]
with Pawchive(session) as client:
    ...
```

```bash
export PAWCHIVE_SESSION="your_session_value_here"
```

---

## API surface

The `Pawchive` client exposes endpoint groups as properties:

| Property       | Class                 | Single-id methods                                                                              | Multi-id (`*_many`) methods                                                                          | Auth? |
| -------------- | --------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----- |
| `client.creators`  | `CreatorsEndpoint`  | `list_all()`, `get_profile(s,cid)`, `get_links(s,cid)`, `get_tags(s,cid)`, `get_announcements(s,cid)`, `get_fancards(s,cid)` | `get_profile_many(s,cids)`, `get_links_many(s,cids)`, `get_tags_many(s,cids)`, `get_announcements_many(s,cids)`, `get_fancards_many(cids)` | No (fancards: fanbox only) |
| `client.posts`     | `PostsEndpoint`     | `list_recent(l,o)`, `list_from_creator(s,cid,l,o)`, `get(s,cid,pid)`, `list_revisions(s,cid,pid)` | `list_from_creator_many(s,cids,l,o)`, `get_many(s,cid,pids)`, `list_revisions_many(s,cid,pids)`     | No |
| `client.comments`  | `CommentsEndpoint`  | `list(s,cid,pid)`                                                                              | `list_many(s,cid,pids)`                                                                              | No |
| `client.flags`     | `FlagsEndpoint`     | `flag(s,cid,pid)`, `get_status(s,cid,pid)`                                                     | `flag_many(s,cid,pids)`, `get_status_many(s,cid,pids)`                                               | Yes |
| `client.favorites` | `FavoritesEndpoint` | `list()`, `add_post(s,cid,pid)`, `remove_post(s,cid,pid)`, `add_creator(s,cid)`, `remove_creator(s,cid)` | `add_post_many(s,cid,pids)`, `remove_post_many(s,cid,pids)`, `add_creator_many(s,cids)`, `remove_creator_many(s,cids)` | Yes |
| `client.search`    | `SearchEndpoint`    | `lookup_hash(hash)`                                                                            | —                                                                                                    | No |
| `client.misc`      | `MiscEndpoint`      | `app_version()`                                                                                | —                                                                                                    | No |

> **Multi-id methods** accept a string or iterable of ids, return a `dict`
> keyed by id, and accept an optional `delay` keyword to throttle between
> requests. A bare string is treated as a single id (not character-split).

### Constructor options

```python
Pawchive(
    session,            # str: session cookie value (with or without "session=")
    base_url="https://pawchive.pw/api/v1",
    timeout=30,         # per-request timeout (seconds)
    max_retries=3,      # retries on 429 / 5xx
    backoff_base=0.5,   # exponential backoff base (seconds)
)
```

---

## Error handling

All errors inherit from `pychive.PawchiveError`. Catch the base class or a
specific subclass:

| Exception              | When raised                                    |
| ---------------------- | ---------------------------------------------- |
| `PawchiveError`        | Base class for all wrapper errors              |
| `AuthenticationError`  | HTTP 401 (invalid/missing session)             |
| `NotFoundError`        | HTTP 404                                       |
| `RateLimitError`       | HTTP 429 after retries exhausted               |
| `ClientError`          | Other HTTP 4xx                                 |
| `ServerError`          | HTTP 5xx after retries exhausted               |
| `ValidationError`      | Invalid arguments passed to a method           |
| `ConnectionError`      | Network/timeout failures                       |

Each HTTP exception carries `.status_code` and `.response` (the original
`requests.Response`). `RateLimitError` also exposes `.retry_after`.

```python
from pychive import Pawchive, RateLimitError, AuthenticationError, NotFoundError

with Pawchive(session) as client:
    try:
        post = client.posts.get("fanbox", "12345", "67890")
    except NotFoundError:
        print("Post not found.")
    except AuthenticationError:
        print("Session expired.")
    except RateLimitError as e:
        print(f"Rate limited; server suggested waiting {e.retry_after}s")
```

Retry behavior: the client automatically retries `429`, `502`, `503`, and
`504` responses up to `max_retries` times with exponential backoff. It honors
the `Retry-After` header when present (capped at 60s).

---

## Project layout

```
Pychive/
├── pyproject.toml          # packaging + tool config
├── README.md
├── AGENTS.md               # compact guide for AI coding sessions
├── docs/                   # full documentation (Markdown)
│   ├── index.md            # entry point + table of contents
│   ├── installation.md
│   ├── quickstart.md
│   ├── authentication.md
│   ├── configuration.md
│   ├── api-reference.md    # every endpoint, method, return type
│   ├── models.md           # every dataclass and its fields
│   ├── exceptions.md
│   ├── error-handling.md
│   ├── pagination.md
│   ├── cookbook.md         # complete examples
│   └── changelog.md
├── tests/                  # pytest test suite (responses for HTTP mocking)
│   ├── conftest.py         # shared fixtures (client with max_retries=0)
│   ├── test_session_parsing.py
│   ├── test_models.py
│   ├── test_validation.py
│   ├── test_endpoints.py
│   ├── test_error_handling.py
│   └── test_client.py
└── src/
    └── pychive/
        ├── __init__.py     # public exports (Pawchive, models, exceptions)
        ├── client.py       # Pawchive — main client class
        ├── http.py         # HttpClient — transport, auth, retry/backoff
        ├── exceptions.py   # exception hierarchy
        ├── models.py       # frozen dataclasses for API responses
        ├── py.typed        # PEP 561 marker (typed package)
        └── endpoints/
            ├── __init__.py
            ├── base.py        # BaseEndpoint
            ├── creators.py    # list_all, profile, links, tags, announcements, fancards
            ├── posts.py       # recent, creator posts, specific post, revisions
            ├── comments.py    # post comments
            ├── flags.py       # flag / flag status
            ├── favorites.py   # list / add / remove posts & creators
            ├── search.py      # hash lookup
            └── misc.py        # app version
```

---

## Development

```bash
source .venv/bin/activate
pip install -e ".[dev]"

# lint
ruff check src
# type-check
mypy src
# tests
pytest
```

## License

MIT
