# dexgram-bot

Python SDK for building bots on Dark Link / SimpleX Chat with rich GUI messages.

Build Telegram-style bots with inline keyboards, product cards, catalogs, forms, and crypto payment integration.

## Quick Start

### 1. Install

```bash
pip install dexgram-bot
```

Or from source:

```bash
git clone https://github.com/AsadKhanSwati/dexgram-bot.git
cd dexgram-bot
pip install -e .
```

### 2. Start SimpleX Chat CLI

Download the CLI from [simplex.chat](https://simplex.chat) and run it as a WebSocket server:

```bash
simplex-chat -p 5225 --create-bot-display-name "MyBot"
```

### 3. Write Your Bot

```python
from dexgram import Bot

bot = Bot(ws_url="ws://localhost:5225")

@bot.on_contact_connected
async def welcome(contact):
    await bot.send_inline_keyboard(
        contact.id,
        "Welcome! What would you like to do?",
        [
            [{"text": "Browse Products", "callback_data": "catalog"}],
            [{"text": "Help", "callback_data": "help"}],
        ],
    )

@bot.on_command("help")
async def help_cmd(contact, args):
    await bot.send_text(contact.id, "Commands: /help, /catalog")

@bot.on_callback("catalog")
async def show_catalog(contact, callback):
    await bot.send_catalog(contact.id, products=[
        {"id": "item-1", "title": "Widget", "price": "0.01", "currency": "ETH"},
        {"id": "item-2", "title": "Gadget", "price": "0.05", "currency": "ETH"},
    ])

@bot.on_message
async def fallback(contact, text):
    await bot.send_text(contact.id, f"You said: {text}")

bot.run()
```

## Features

### Inline Keyboards

Send messages with interactive buttons:

```python
await bot.send_inline_keyboard(
    contact.id,
    "Choose an option:",
    [
        [{"text": "Option A", "callback_data": "a"}, {"text": "Option B", "callback_data": "b"}],
        [{"text": "Visit Website", "url": "https://example.com"}],
        [{"text": "Pay Now", "pay": True}],
    ],
)
```

### Product Cards

Display product information with images and Buy buttons:

```python
await bot.send_product_card(
    contact.id,
    id="shoe-1",
    title="Running Shoes",
    price="0.05",
    currency="ETH",
    description="Lightweight running shoes",
    network="ethereum",
    wallet_address="0x...",
    keyboard=[
        [{"text": "Buy Now", "pay": True}, {"text": "Add to Cart", "callback_data": "cart:shoe-1"}],
    ],
)
```

### Catalogs

Send a horizontal carousel of products:

```python
await bot.send_catalog(contact.id, products=[
    {"id": "p1", "title": "Item 1", "price": "0.01", "currency": "ETH"},
    {"id": "p2", "title": "Item 2", "price": "0.02", "currency": "ETH"},
    {"id": "p3", "title": "Item 3", "price": "0.03", "currency": "ETH"},
])
```

### Forms

Collect structured input from users:

```python
await bot.send_form(
    contact.id,
    id="shipping",
    title="Shipping Info",
    fields=[
        {"name": "name", "label": "Full Name", "field_type": "text"},
        {"name": "email", "label": "Email", "field_type": "email"},
        {"name": "country", "label": "Country", "field_type": "dropdown",
         "options": ["US", "UK", "Canada", "Germany"]},
    ],
    submit_label="Submit Order",
    callback_data="order:submit",
)
```

### Crypto Payments

Trigger the wallet payment flow:

```python
await bot.send_payment_request(
    contact_id=contact.id,
    amount="0.05",
    currency="ETH",
    network="ethereum",
    wallet_address="0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    memo="Order #1234",
)
```

## Event Handlers

### Decorators

| Decorator | Trigger |
|-----------|---------|
| `@bot.on_start` | Bot connected and ready |
| `@bot.on_contact_connected` | New user connects to bot |
| `@bot.on_contact_disconnected` | User disconnects |
| `@bot.on_command("keyword")` | User sends `/keyword` |
| `@bot.on_callback("pattern")` | User clicks inline button |
| `@bot.on_message` | Any text message (fallback) |

### Callback Patterns

Callbacks support wildcard matching:

```python
@bot.on_callback("buy:*")          # matches "buy:shoe-1", "buy:hat-2"
async def handle_buy(contact, callback):
    product_id = callback.data.split(":")[1]

@bot.on_callback("order:*:*")      # matches "order:123:confirm"
async def handle_order(contact, callback):
    parts = callback.data.split(":")
    order_id, action = parts[1], parts[2]
```

## Low-Level Client

For advanced use, access the `DexgramClient` directly:

```python
from dexgram import DexgramClient, ChatType
import asyncio

async def main():
    client = await DexgramClient.create("ws://localhost:5225")
    user = await client.api_get_active_user()
    print(f"Bot: {user.local_display_name}")

    await client.api_send_text(ChatType.DIRECT, contact_id, "Hello!")
    await client.disconnect()

asyncio.run(main())
```

## Architecture

```
dexgram-bot
├── dexgram/
│   ├── __init__.py          # Public API exports
│   ├── transport.py         # WebSocket JSON protocol
│   ├── types.py             # Data types (Contact, User, ChatItem, ...)
│   ├── client.py            # DexgramClient — low-level API methods
│   ├── rich_messages.py     # BotMessage encode/decode (__BOT_MSG__ protocol)
│   └── bot.py               # Bot — high-level framework with decorators
└── examples/
    ├── echo_bot.py          # Minimal echo bot
    ├── shop_bot.py          # E-commerce bot with cart & payments
    └── faq_bot.py           # FAQ bot with search & nested menus
```

## Message Protocol

Rich bot messages use a prefix-based protocol over standard SimpleX text messages:

- `__BOT_MSG__` + JSON — Bot sends rich content (keyboards, cards, forms)
- `__BOT_CB__` + JSON — User sends callback (button click, form submit)
- `__PAY_INV__` + JSON — Payment invoice (existing wallet protocol)
- `__PAY_CONF__` + JSON — Payment confirmation (existing wallet protocol)

The Dark Link mobile/desktop app detects these prefixes and renders the appropriate
interactive UI components instead of plain text.

## Examples

See the `examples/` directory:

- **echo_bot.py** — Minimal bot that echoes messages back
- **shop_bot.py** — Full e-commerce bot with products, cart, checkout, and crypto payments
- **faq_bot.py** — FAQ bot with categories, search, and nested navigation

## Requirements

- Python 3.10+
- `websockets` library
- SimpleX Chat CLI running as WebSocket server
