Metadata-Version: 2.5
Name: pymsgr
Version: 0.1.0
Summary: A modern, strictly-typed Python wrapper for the Meta Messenger Platform Graph API.
Project-URL: Homepage, https://github.com/ssshiponu/pymsgr
Project-URL: Documentation, https://pymsgr.readthedocs.io
Project-URL: Repository, https://github.com/ssshiponu/pymsgr
Project-URL: Issues, https://github.com/ssshiponu/pymsgr/issues
Author-email: Mohin Uddin Shipon <sshiponuddin22@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: chatbot,facebook,graph-api,messenger,meta,webhook
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
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: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.28.1
Requires-Dist: pydantic>=2.13.5
Provides-Extra: all
Requires-Dist: fastapi>=0.141.1; extra == 'all'
Requires-Dist: flask>=3.1.3; extra == 'all'
Requires-Dist: uvicorn>=0.53.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: fastapi>=0.141.1; extra == 'dev'
Requires-Dist: flask>=3.1.3; extra == 'dev'
Requires-Dist: httpx>=0.28.1; extra == 'dev'
Requires-Dist: mypy>=2.3.1; extra == 'dev'
Requires-Dist: pytest-httpx>=0.36.2; extra == 'dev'
Requires-Dist: pytest>=9.1.1; extra == 'dev'
Requires-Dist: respx>=0.23.1; extra == 'dev'
Requires-Dist: ruff>=0.16.8; extra == 'dev'
Requires-Dist: uvicorn>=0.53.0; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.141.1; extra == 'fastapi'
Requires-Dist: uvicorn>=0.53.0; extra == 'fastapi'
Provides-Extra: flask
Requires-Dist: flask>=3.1.3; extra == 'flask'
Description-Content-Type: text/markdown

A modern, strictly-typed Python wrapper for the Meta Messenger Platform (Graph API).

Most Python libraries for Facebook Messenger are either abandoned, lack type hints, or were written back in the Python 2/3 transition era. `pymsgr` was built to give developers an intuitive, production-grade experience using modern Python (3.10+), Pydantic v2, and full typing support.

> **Note & Credit:** This project is heavily inspired by [pywa](https://github.com/david-lev/pywa) (the awesome WhatsApp Cloud API library). If you've used `pywa` before, you'll feel right at home with the handler syntax, contextual replies, and overall developer experience.

---

## Features

- **Strictly Typed & Autocomplete-Ready**: Built from the ground up with Pydantic v2. Your IDE knows every incoming event field and outbound payload parameter.
- **Contextual Reply Helpers**: Reply directly using `msg.reply_text()`, `msg.reply_buttons()`, or `msg.typing_on()` without juggling PSIDs and page IDs manually.
- **Built-in Webhook Handlers**: Zero-boilerplate webhook setup with first-class support for both **FastAPI** and **Flask**.
- **Webhook Security Out of the Box**: Automatic HMAC-SHA256 signature verification (`X-Hub-Signature-256`) and verification challenge (`hub.challenge`) handshakes.
- **Multi-Tenant / SaaS Friendly**: Pass a single token, a mapping dictionary, or a dynamic callable/database lookup function for multi-page bots.
- **Rich Templates**: Clean models for Buttons, Generic Templates (Carousels), Quick Replies, and sender actions.

---

## Installation

Install with pip:

```bash
pip install pymsgr
```

Or install with webhook integrations:

```bash
# For FastAPI & Uvicorn
pip install "pymsgr[fastapi]"

# For Flask
pip install "pymsgr[flask]"

# Everything included
pip install "pymsgr[all]"
```

---

## Quick Start (FastAPI)

Here is a simple bot that verifies your webhook and echoes back greetings:

```python
from fastapi import FastAPI
import uvicorn

from pymsgr import Messenger
from pymsgr.server import FastAPIIntegration
from pymsgr.types import Message, QuickReply

app = FastAPI()

bot = Messenger(
    page_access_token="YOUR_PAGE_ACCESS_TOKEN",
    verify_token="YOUR_CUSTOM_VERIFY_TOKEN",
    app_secret="YOUR_META_APP_SECRET",
)

# Automatically registers GET and POST routes on /webhook
FastAPIIntegration(bot, app, path="/webhook")


@bot.on_message()
def handle_message(msg: Message):
    # Contextual shortcuts — no need to pass sender_id or page_id
    if msg.text and msg.text.lower() == "hello":
        msg.reply_text(
            text="Hey there! How can I help you today?",
            quick_replies=[
                QuickReply(title="Pricing", payload="ACTION_PRICING"),
                QuickReply(title="Contact Support", payload="ACTION_SUPPORT"),
            ],
        )


@bot.on_postback()
def handle_postback(msg: Message):
    if msg.postback_payload == "GET_STARTED":
        msg.reply_text("Welcome aboard! Let's get started.")


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
```

---

## Using with Flask

Prefer Flask? The setup is just as simple:

```python
from flask import Flask
from pymsgr import Messenger
from pymsgr.server import FlaskIntegration
from pymsgr.types import Message

app = Flask(__name__)

bot = Messenger(
    page_access_token="YOUR_PAGE_ACCESS_TOKEN",
    verify_token="YOUR_CUSTOM_VERIFY_TOKEN",
    app_secret="YOUR_META_APP_SECRET",
)

FlaskIntegration(bot, app, path="/webhook")


@bot.on_message()
def handle_message(msg: Message):
    msg.reply_text(f"You said: {msg.text}")


if __name__ == "__main__":
    app.run(port=8000)
```

---

## Multi-Page & SaaS Architecture

If you're building a multi-tenant bot where different Facebook Pages use the same backend, you don't need multiple client instances. `pymsgr` allows dynamic token resolution:

### 1. Dictionary Mapping
```python
bot = Messenger(
    page_access_token={
        "PAGE_ID_1": "TOKEN_FOR_PAGE_1",
        "PAGE_ID_2": "TOKEN_FOR_PAGE_2",
    },
    verify_token="shared_verify_token",
    app_secret="shared_app_secret",
)
```

### 2. Dynamic Database Lookup (Callable)
```python
def get_page_token(page_id: str) -> str:
    # Fetch token from your database or cache
    page = db.query(Page).filter_by(id=page_id).first()
    return page.access_token


bot = Messenger(
    page_access_token=get_page_token,
    verify_token="shared_verify_token",
    app_secret="shared_app_secret",
)
```

---

## Interactive Message Templates

### Button Template
```python
from pymsgr.types import PostbackButton, UrlButton

bot.send_buttons(
    psid=user_id,
    page_id=page_id,
    text="What would you like to check out?",
    buttons=[
        UrlButton(title="Visit Website", url="https://example.com"),
        PostbackButton(title="View Order", payload="VIEW_ORDER_123"),
    ],
)
```

### Generic Template (Carousel)
```python
from pymsgr.types import GenericElement, PostbackButton, UrlButton

elements = [
    GenericElement(
        title="Product 1",
        subtitle="$29.99 - In Stock",
        image_url="https://example.com/item1.jpg",
        buttons=[PostbackButton(title="Buy Now", payload="BUY_1")],
    ),
    GenericElement(
        title="Product 2",
        subtitle="$49.99 - Limited Supply",
        image_url="https://example.com/item2.jpg",
        buttons=[PostbackButton(title="Buy Now", payload="BUY_2")],
    ),
]

bot.send_generic_template(psid=user_id, page_id=page_id, elements=elements)
```

---

## Event Handlers

You can register listeners for various incoming events:

```python
@bot.on_message()
def on_msg(msg: Message): ...

@bot.on_postback()
def on_postback(msg: Message): ...

@bot.on_read()
def on_read(msg: Message): ...

@bot.on_delivery()
def on_delivery(msg: Message): ...

@bot.on_reaction()
def on_reaction(msg: Message): ...
```

---

## Contributing

Contributions are always welcome! Feel free to open an issue if you discover a bug or want to request a feature, or submit a pull request directly.

1. Fork the repository
2. Create your branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

---

## License

Distributed under the MIT License. See `LICENSE` for more information.