Metadata-Version: 2.4
Name: metricsfirst
Version: 0.6.0
Summary: MetricsFirst SDK for Python - Analytics for Telegram bots and web funnels
Project-URL: Homepage, https://metricsfirst.com
Project-URL: Documentation, https://docs.metricsfirst.com/python
Project-URL: Repository, https://github.com/metricsfirst/metricsfirst-python
Project-URL: Changelog, https://github.com/metricsfirst/metricsfirst-python/blob/main/CHANGELOG.md
Author-email: MetricsFirst <support@metricsfirst.com>
License-Expression: MIT
License-File: LICENSE
Keywords: analytics,bot,metrics,metricsfirst,telegram,tracking
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
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.8
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: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Provides-Extra: async
Requires-Dist: aiohttp>=3.8.0; extra == 'async'
Provides-Extra: dev
Requires-Dist: aiohttp>=3.8.0; extra == 'dev'
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.20.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# MetricsFirst Python SDK

Official Python SDK for [MetricsFirst](https://metricsfirst.com) - Analytics for Telegram bots and web funnels.

> **Note:** Commands and interactions are tracked automatically when you add your bot to MetricsFirst. This SDK is for tracking custom events like services, purchases, errors, and **funnel events**.

## Installation

```bash
# Basic installation (sync only)
pip install metricsfirst

# With async support
pip install metricsfirst[async]
```

## Features

- **Fire-and-forget**: All tracking calls are non-blocking and don't add latency
- **Background sending**: Events are sent in a separate thread (sync) or task (async)
- **Auto cleanup**: No need to call shutdown() - events flush automatically on exit
- **Error resilience**: Errors are logged, never thrown to your code
- **Memory safe**: Queue is limited to 1000 events to prevent memory issues
- **Custom Events**: Track any event with dynamic properties (Mixpanel-style)
- **Funnel Tracking**: Track anonymous visitors before registration with `ad_id`

## Quick Start

### Custom Events (Mixpanel-style)

Track any event with dynamic properties:

```python
from metricsfirst import MetricsFirst

# Initialize once (globally)
mf = MetricsFirst(
    bot_id="your_bot_id",
    api_key="your_api_key",
)

# Track custom events with any properties
mf.track(123456789, 'STORY_RESPONSE', {
    'target': 'username123',
    'url': 'https://example.com/story',
    'response_time_ms': 150,
    'success': True,
})

mf.track(123456789, 'BUTTON_CLICK', {
    'button_name': 'premium_upgrade',
    'screen': 'main_menu',
})

mf.track(123456789, 'VIDEO_DOWNLOADED', {
    'duration_seconds': 45,
    'quality': '1080p',
    'source': 'instagram',
})

# Events are automatically flushed on exit - no shutdown() needed!
```

### Synchronous Client

```python
from metricsfirst import MetricsFirst, ServiceEventData

# Initialize once (globally)
mf = MetricsFirst(
    bot_id="your_bot_id",
    api_key="your_api_key",
)

# Track a service (fire-and-forget, non-blocking)
mf.track_service(ServiceEventData(
    user_id=123456789,
    service_name="image_generation",
    is_free=False,
    price=10,
    currency="USD",
))

# Events are automatically flushed on exit
```

### Asynchronous Client

```python
import asyncio
from metricsfirst import AsyncMetricsFirst, ServiceEventData

# Initialize once (globally)
mf = AsyncMetricsFirst(
    bot_id="your_bot_id",
    api_key="your_api_key",
)

async def main():
    # Just use it - auto-starts on first call
    await mf.track_service(ServiceEventData(
        user_id=123456789,
        service_name="image_generation",
    ))

# Events are automatically flushed on exit
asyncio.run(main())
```

### Context Manager

```python
# Sync
with MetricsFirst(bot_id="...", api_key="...") as mf:
    mf.track_service(...)

# Async
async with AsyncMetricsFirst(bot_id="...", api_key="...") as mf:
    await mf.track_service(...)
```

## Funnel Tracking

Track anonymous visitors before they register. Perfect for landing pages and ad attribution.

### Stage-Based Funnels (v2 - Recommended)

Track funnels with **numeric stages** and **numeric steps**, each with descriptive names.

```python
from metricsfirst import MetricsFirst, UtmParams

mf = MetricsFirst(api_key='your_api_key')
FUNNEL_ID = 'my_landing_funnel'

# Stage and step definitions
STAGE = {'LANDING': {'id': 1, 'name': 'landing'}}
STEPS = {
    'PAGE_VIEW': {'id': 1, 'name': 'page_view'},
    'SCROLL_50': {'id': 2, 'name': 'scroll_50'},
    'BUTTON_CLICK': {'id': 3, 'name': 'button_click'},
    'START_DOWNLOAD': {'id': 4, 'name': 'start_download'},  # conversion
    'PAGE_LEAVE': {'id': 5, 'name': 'page_leave'},          # exit
}

# Track page view (stage 1 "landing", step 1 "page_view")
mf.track_stage_event(
    funnel_id=FUNNEL_ID,
    stage_id=STAGE['LANDING']['id'],
    stage_name=STAGE['LANDING']['name'],
    step_id=STEPS['PAGE_VIEW']['id'],
    step_name=STEPS['PAGE_VIEW']['name'],
    event_type='step',
    ad_id='visitor_abc123',
    properties={'page': '/landing'},
    utm=UtmParams(utm_source='google', utm_campaign='spring_sale'),
)

# Track button click (step 3)
mf.track_stage_event(
    FUNNEL_ID,
    STAGE['LANDING']['id'], STAGE['LANDING']['name'],
    STEPS['BUTTON_CLICK']['id'], STEPS['BUTTON_CLICK']['name'],
    'step', 'visitor_abc123'
)

# Track conversion (step 4)
mf.track_stage_event(
    FUNNEL_ID,
    STAGE['LANDING']['id'], STAGE['LANDING']['name'],
    STEPS['START_DOWNLOAD']['id'], STEPS['START_DOWNLOAD']['name'],
    'conversion', 'visitor_abc123'
)

# Link ad_id to user_id when user registers
mf.link_user_identity('visitor_abc123', update.effective_user.id)
```

#### Event Types

| Type | Description |
|------|-------------|
| `step` | Normal progression within a stage |
| `conversion` | User successfully completed the stage |
| `exit` | User left the stage without converting |

### Async Example (v2)

```python
from metricsfirst import AsyncMetricsFirst, UtmParams

mf = AsyncMetricsFirst(api_key='your_api_key')
FUNNEL_ID = 'my_landing_funnel'

# Track stage event (stage 1 "landing", step 1 "page_view")
await mf.track_stage_event(
    funnel_id=FUNNEL_ID,
    stage_id=1,
    stage_name='landing',
    step_id=1,
    step_name='page_view',
    event_type='step',
    ad_id='visitor_abc123',
    utm=UtmParams(utm_source='google', utm_campaign='spring'),
)

# Track conversion (step 4)
await mf.track_stage_event(FUNNEL_ID, 1, 'landing', 4, 'start_download', 'conversion', 'visitor_abc123')

# Link to user when they register
await mf.link_user_identity('visitor_abc123', user_id=123456789)
```

### Simple Funnel Steps (v1)

For simpler funnels without stages:

```python
mf.track_funnel_step(
    funnel_id='my_funnel',
    step='page_view',
    ad_id='visitor_abc123',
    properties={'page': '/landing'},
    utm=UtmParams(utm_source='google'),
)

mf.track_funnel_step('my_funnel', 'cta_click', 'visitor_abc123')
mf.link_user_identity('visitor_abc123', user_id=123456789)
```

### Methods

```python
# RECOMMENDED: Track stage-based funnel event (v2)
mf.track_stage_event(
    funnel_id: str,              # Funnel identifier
    stage_id: int,               # Stage number (1, 2, 3...)
    stage_name: str,             # Stage name ('landing', 'checkout')
    step_id: int,                # Step number (1, 2, 3...)
    step_name: str,              # Step name ('page_view', 'button_click')
    event_type: Literal['step', 'conversion', 'exit'],  # Event type
    ad_id: str,                  # Anonymous visitor ID
    properties: dict = None,     # Event properties
    utm: UtmParams = None,       # UTM parameters
)

# Track simple funnel step (v1)
mf.track_funnel_step(
    funnel_id: str,              # Funnel identifier
    step: str,                   # Step name
    ad_id: str,                  # Anonymous visitor ID
    properties: dict = None,     # Event properties
    utm: UtmParams = None,       # UTM parameters
)

# Link ad_id to user_id
mf.link_user_identity(
    ad_id: str,                  # Anonymous visitor ID
    user_id: int,                # Telegram user ID
    properties: dict = None,     # Additional context
)

# Legacy methods (manual dashboard configuration required)
mf.track_funnel(ad_id, event_name, properties=None, utm=None)
mf.link_identity(ad_id, user_id, properties=None)
```

## Available Methods

| Method                             | Description                                        |
| ---------------------------------- | -------------------------------------------------- |
| `track()`                          | **Track custom events with any properties**        |
| `track_stage_event()`              | **Track stage-based funnel event (v2)**            |
| `track_funnel_step()`              | Track simple funnel step (v1)                      |
| `link_user_identity()`             | **Link ad_id to user_id**                          |
| `track_funnel()`                   | Track anonymous funnel events (legacy)             |
| `link_identity()`                  | Link ad_id to user_id (legacy)                     |
| `track_service()`                  | Track services provided                            |
| `track_error()`                    | Track errors                                       |
| `track_error_from_exception()`     | Track error from exception                         |
| `track_purchase_initiated()`       | Track purchase start                               |
| `track_purchase_completed()`       | Track successful purchase                          |
| `track_purchase_error()`           | Track failed purchase                              |
| `track_recurring_charge_success()` | Track subscription charge                          |
| `track_recurring_charge_failed()`  | Track failed charge                                |
| `identify()`                       | Identify user with properties                      |

### track() - Custom Events

```python
mf.track(
    user_id: int,                 # Telegram user ID
    event_name: str,              # Event name (e.g., 'STORY_RESPONSE')
    properties: dict = None,      # Any key-value pairs
)
```

## Configuration

```python
mf = MetricsFirst(
    bot_id="your_bot_id",
    api_key="your_api_key",
    api_url="https://api.metricsfirst.com",  # Custom API URL
    batch_events=True,      # Batch events before sending
    batch_size=10,          # Events per batch
    batch_interval=5.0,     # Seconds between flushes
    debug=False,            # Enable debug logging
    timeout=10.0,           # HTTP timeout
)
```

## License

MIT
