Metadata-Version: 2.3
Name: cloudstile
Version: 2.0.1
Summary: Unofficial Cloudflare Turnstile library for Python with async and sync support.
License: MIT
Keywords: cloudflare,turnstile,captcha
Author: NotAussie
Author-email: notaussie@duck.com
Requires-Python: >=3.9,<4.0
Classifier: Framework :: Pydantic
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: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Dist: httpx (==0.28.1)
Requires-Dist: pydantic (==2.11.7)
Requires-Dist: typing-extensions (==4.14.1)
Project-URL: Repository, https://github.com/notaussie/cloudstile
Description-Content-Type: text/markdown

# `⛅` Cloudstile
An unofficial Cloudflare Turnstile library with both asynchronous and synchronous support out of the box.

[![codecov](https://codecov.io/github/NotAussie/cloudstile/branch/main/graph/badge.svg?token=6VKWB9GXEU)](https://codecov.io/github/NotAussie/cloudstile) [![Tests](https://github.com/NotAussie/cloudstile/actions/workflows/tests.yml/badge.svg)](https://github.com/NotAussie/cloudstile/actions/workflows/tests.yml) [![Pylint](https://github.com/NotAussie/cloudstile/actions/workflows/pylint.yml/badge.svg)](https://github.com/NotAussie/cloudstile/actions/workflows/pylint.yml) [![Mypy](https://github.com/NotAussie/cloudstile/actions/workflows/mypy.yml/badge.svg)](https://github.com/NotAussie/cloudstile/actions/workflows/mypy.yml)

## `📥` Installation
**Cloudstile** is available for download via PyPI. To install it, simply do:
```shell
pip install cloudstile
```

## `🎭` Example

Here are some basic examples of how to validate a user's turnstile token.

> [!WARNING]
> These examples expect the user's IP to be transparent. If you're using something like Cloudflare's proxy service, then you'll need to access the corresponding header for your use case.

### `🍷` Quart *(Asynchronous)*

```python
from quart import Quart, request, jsonify
from cloudstile import AsyncTurnstile

app = Quart(__name__)
turnstile = AsyncTurnstile(token="...")

@app.route("/submit", methods=["POST"])
async def submit():

    body = await request.form

    response = await turnstile.validate(
        body.get("cf-turnstile-response", "..."),
        request.remote_addr,
    )

    return jsonify(response.model_dump()) # <- Response is a pydantic object

```

### `🏃‍♀️` FastAPI *(Asynchronous)*

```python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from cloudstile import AsyncTurnstile

app = FastAPI()
turnstile = AsyncTurnstile(token="...")

@app.post("/submit")
async def submit(request: Request):

    body = await request.form()

    response = await turnstile.validate(
        body.get("cf-turnstile-response", "..."),
        request.client.host,
    )

    return JSONResponse(response.model_dump()) # <- Response is a pydantic object

```


### `🦥` Flask *(Synchronous)*

```python
from flask import Flask, request, jsonify
from cloudstile import SyncTurnstile

app = Flask(__name__)
turnstile = SyncTurnstile(token="...")

@app.route("/submit", methods=["POST"])
def submit():

    body = request.form

    response = turnstile.validate(
        body.get("cf-turnstile-response", "..."),
        request.remote_addr,
    )

    return jsonify(response.model_dump()) # <- Response is a pydantic object

```

