Metadata-Version: 2.4
Name: listifypy
Version: 0.1.0
Summary: Python SDK for Listify API
Author: Listify Team
Author-email: support@listify.com
License: MIT
Project-URL: Homepage, https://github.com/phenuop/listify-pythonsdk
Project-URL: Issues, https://github.com/phenuop/listify-pythonsdk/issues
Project-URL: Documentation, https://rsdash.net/docs
Keywords: listify,discord,bot,api,sdk,listing,votes
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: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24.0
Requires-Dist: typing-extensions>=4.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: isort>=5.12.0; extra == "dev"
Requires-Dist: flake8>=6.1.0; extra == "dev"
Requires-Dist: mypy>=1.4.0; extra == "dev"
Requires-Dist: build>=0.10.0; extra == "dev"
Requires-Dist: twine>=4.0.0; extra == "dev"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# ListifyPy - Python SDK for Listify API



[![PyPI version](https://badge.fury.io/py/listifypy.svg)](https://badge.fury.io/py/listifypy)

[![Python versions](https://img.shields.io/pypi/pyversions/listifypy.svg)](https://pypi.org/project/listifypy/)

[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)

[![Tests](https://github.com/phenuop/listify-pythonsdk/workflows/Tests/badge.svg)](https://github.com/phenuop/listify-pythonsdk/actions)

[![Coverage](https://codecov.io/gh/phenuop/listify-pythonsdk/branch/main/graph/badge.svg)](https://codecov.io/gh/phenuop/listify-pythonsdk)



A comprehensive Python SDK for the Listify API - the easiest way to integrate your bot with Listify's bot listing platform.



## 📦 Features



- ✅ **Full API Client** - All Listify API endpoints with automatic retries and rate limiting

- ✅ **Webhook Handler** - Secure webhook verification with HMAC-SHA256 signatures

- ✅ **Widget Generator** - Beautiful embeddable badges and widgets for your README

- ✅ **Type Safe** - Full type hints for excellent IDE support

- ✅ **Async/Await** - Built with async/await for high performance

- ✅ **Caching** - Intelligent response caching with TTL

- ✅ **Rate Limiting** - Automatic handling of API rate limits

- ✅ **Retry Logic** - Exponential backoff for failed requests

- ✅ **Event System** - Subscribe to API events for monitoring

- ✅ **Zero Dependencies** - Only requires `httpx` and `typing-extensions`



## 📋 Table of Contents



- [Installation](#installation)

- [Quick Start](#quick-start)

- [API Client](#api-client)

  - [Initialization](#initialization)

  - [Push Statistics](#push-statistics)

  - [Update Status](#update-status)

  - [Check Votes](#check-votes)

  - [Sync Commands](#sync-commands)

  - [Get Bot Information](#get-bot-information)

  - [Get Analytics](#get-analytics)

  - [Get Reviews](#get-reviews)

  - [Search Bots](#search-bots)

  - [Get Stats History](#get-stats-history)

  - [Health Check](#health-check)

- [Webhook Handler](#webhook-handler)

  - [FastAPI Example](#fastapi-example)

  - [Flask Example](#flask-example)

  - [Django Example](#django-example)

  - [Event Handling](#event-handling)

  - [Testing Webhooks](#testing-webhooks)

- [Widget Generator](#widget-generator)

  - [Widget Types](#widget-types)

  - [Customization](#customization)

  - [HTML & Markdown](#html--markdown)

  - [Badge Shortcuts](#badge-shortcuts)

- [Error Handling](#error-handling)

- [Configuration](#configuration)

- [Type System](#type-system)

- [Contributing](#contributing)

- [License](#license)



## 🚀 Installation



### Using pip



```bash

pip install listifypy

```



### Using poetry



```bash

poetry add listifypy

```



### Development Installation



```bash

git clone https://github.com/phenuop/listify-pythonsdk.git

cd listify-pythonsdk

pip install -e .[dev]

```



## 🏃 Quick Start



```python

import asyncio

from listifypy import Api



async def main():

    # Initialize the client

    client = Api(

        bot_id="your-bot-id",

        options={

            "token": "your-api-token"

        }

    )

    

    # Push bot statistics

    await client.push_stats({

        "platform": "discord",

        "server_count": 420,

        "user_count": 45000,

        "shard_count": 4,

        "status": "online"

    })

    

    # Check if a user has voted

    result = await client.check_vote({

        "platform": "discord",

        "user_id": "80351110224678912"

    })

    

    if result["voted"]:

        print("User has voted!")

    else:

        print("User hasn't voted yet")



asyncio.run(main())

```



## 📚 API Client



### Initialization



```python

from listifypy import Api



# Basic initialization

client = Api(

    bot_id="your-bot-id",

    options={

        "token": "your-api-token"

    }

)



# Advanced configuration

client = Api(

    bot_id="your-bot-id",

    options={

        "token": "your-api-token",

        "base_url": "https://www.rsdash.net",  # Custom API URL

        "max_retries": 5,                      # Maximum retry attempts

        "backoff_base": 1000,                  # Base delay for exponential backoff (ms)

        "cache": True,                         # Enable response caching

        "cache_ttl": 600,                      # Cache TTL in seconds

        "log_level": "debug",                  # Logging level

        "log_format": "json",                  # Log format (text or json)

        "timeout": 60.0,                       # Request timeout in seconds

        "debug": True,                         # Enable debug mode

        "user_agent": "MyBot/1.0.0"            # Custom User-Agent

    }

)

```



### Push Statistics



Update your bot's statistics on Listify:



```python

# Push basic stats

await client.push_stats({

    "platform": "discord",

    "server_count": 420,

    "user_count": 45000,

    "shard_count": 4,

    "status": "online"

})



# Push with minimal stats

await client.push_stats({

    "platform": "discord",

    "server_count": 420

})



# Push with maintenance status

await client.push_stats({

    "platform": "discord",

    "server_count": 420,

    "status": "maintenance"

})

```



### Update Status



Update your bot's runtime status:



```python

# Update status for a specific platform

await client.update_status({

    "platform": "discord",

    "status": "online"

})



# Update status (if only one platform)

await client.update_status({

    "status": "maintenance"

})



# Available statuses: "online", "offline", "maintenance"

```



### Check Votes



Check if a user has voted in the last 12 hours:



```python

# Check vote for Discord user

result = await client.check_vote({

    "platform": "discord",

    "user_id": "80351110224678912"

})



print(f"Voted: {result['voted']}")

print(f"Voted at: {result['voted_at']}")

print(f"Next vote at: {result['next_vote_at']}")



# Check vote for Fluxer user

result = await client.check_vote({

    "platform": "fluxer",

    "user_id": "user123"

})



# Handle vote status

if result["voted"]:

    await give_rewards(user_id)

else:

    await remind_user_to_vote(user_id)

```



### Sync Commands



Sync slash commands to your listing page:



```python

# Define your commands

commands = [

    {

        "name": "ping",

        "description": "Check bot latency",

        "category": "Utility"

    },

    {

        "name": "play",

        "description": "Play a song",

        "category": "Music",

        "usage": "/play <song name>",

        "options": [

            {

                "name": "query",

                "description": "Song name or URL",

                "type": 3,  # STRING

                "required": True

            }

        ]

    },

    {

        "name": "ban",

        "description": "Ban a user",

        "category": "Moderation",

        "options": [

            {

                "name": "user",

                "description": "User to ban",

                "type": 6,  # USER

                "required": True

            },

            {

                "name": "reason",

                "description": "Ban reason",

                "type": 3,  # STRING

                "required": False

            }

        ]

    }

]



# Sync commands

result = await client.sync_commands({

    "platform": "discord",

    "commands": commands,

    "mode": "replace"  # or "merge"

})



print(f"Created: {result['created']}")

print(f"Updated: {result['updated']}")

print(f"Removed: {result['removed']}")

print(f"Total: {result['total']}")

```



### Get Bot Information



Retrieve detailed information about your bot:



```python

# Get your own bot info

bot = await client.get_bot()

print(f"Name: {bot['name']}")

print(f"Slug: {bot['slug']}")

print(f"Description: {bot['description']}")

print(f"Platforms: {', '.join(bot['platforms'])}")



# Vote statistics

votes = bot['votes']

print(f"Current votes: {votes['current']}")

print(f"Total votes: {votes['total']}")

print(f"Votes today: {votes['today']}")



# Rating information

rating = bot['rating']

print(f"Rating: {rating['score']}/5 ({rating['count']} reviews)")



# Get another bot's info

other_bot = await client.get_bot("other-bot-id")

```



### Get Analytics



Retrieve analytics for your bot:



```python

analytics = await client.get_analytics()

print(f"Views: {analytics['views']}")

print(f"Clicks: {analytics['clicks']}")

print(f"Unique Visitors: {analytics['unique_visitors']}")

print(f"Date: {analytics['date']}")

```



### Get Reviews



Retrieve reviews for your bot:



```python

# Get latest 10 reviews

reviews = await client.get_reviews(limit=10)

for review in reviews:

    print(f"User: {review['username']}")

    print(f"Rating: {'★' * review['rating']}")

    print(f"Comment: {review['comment']}")

    print(f"Date: {review['created_at']}")

    print("-" * 40)



# Get all reviews (no limit)

reviews = await client.get_reviews()

```



### Search Bots



Search for bots on Listify:



```python

# Search by keyword

results = await client.search_bots("music", limit=10)

for bot in results:

    print(f"{bot['name']}: {bot['description']}")

    print(f"Votes: {bot['votes']['current']}")

    print(f"Rating: {bot['rating']['score']}/5")

    print("-" * 40)



# Search with custom limit

results = await client.search_bots("moderation", limit=5)

```



### Get Stats History



Retrieve historical statistics:



```python

# Get last 7 days of stats

history = await client.get_stats_history(7)

for entry in history:

    print(f"{entry['date']}: {entry['server_count']} servers")



# Get last 30 days (default)

history = await client.get_stats_history()

```



### Health Check



Check the API health status:



```python

health = await client.health_check()

print(f"Status: {health['status']}")

print(f"Timestamp: {health['timestamp']}")

```



## 🔔 Webhook Handler



The Webhook handler provides secure, type-safe webhook processing with signature verification.



### FastAPI Example



```python

from fastapi import FastAPI, Request

from listifypy import Webhook, is_vote_created



app = FastAPI()

webhook = Webhook("your-webhook-secret", {"debug": True})



@app.post("/webhook")

async def webhook_endpoint(request: Request):

    # Get raw body

    raw_body = await request.body()

    

    # Verify and parse the webhook

    payload = webhook.verify_and_parse_request(

        request.headers.get("x-listify-timestamp"),

        request.headers.get("x-listify-signature"),

        raw_body.decode()

    )

    

    if not payload:

        return {"error": "Invalid signature"}, 403

    

    # Handle different event types

    if is_vote_created(payload):

        user_id = payload["data"]["identities"]["discord"]["id"]

        if user_id:

            # Grant rewards to the user

            await grant_rewards(user_id)

            print(f"✅ Rewards granted to {user_id}")

    

    return {"success": True}

```



### Flask Example



```python

from flask import Flask, request, jsonify

from listifypy import Webhook



app = Flask(__name__)

webhook = Webhook("your-webhook-secret")



@app.route("/webhook", methods=["POST"])

def webhook_endpoint():

    raw_body = request.get_data(as_text=True)

    

    payload = webhook.verify_and_parse_request(

        request.headers.get("x-listify-timestamp"),

        request.headers.get("x-listify-signature"),

        raw_body

    )

    

    if not payload:

        return jsonify({"error": "Invalid signature"}), 403

    

    # Process the payload

    if payload["event"] == "vote.created":

        user_id = payload["data"]["identities"]["discord"]["id"]

        if user_id:

            grant_rewards(user_id)

    

    return jsonify({"success": True})

```



### Django Example



```python

from django.http import JsonResponse

from django.views.decorators.csrf import csrf_exempt

from listifypy import Webhook



webhook = Webhook("your-webhook-secret")



@csrf_exempt

def webhook_endpoint(request):

    raw_body = request.body.decode('utf-8')

    

    payload = webhook.verify_and_parse_request(

        request.headers.get("x-listify-timestamp"),

        request.headers.get("x-listify-signature"),

        raw_body

    )

    

    if not payload:

        return JsonResponse({"error": "Invalid signature"}, status=403)

    

    if payload["event"] == "vote.created":

        user_id = payload["data"]["identities"]["discord"]["id"]

        if user_id:

            grant_rewards(user_id)

    

    return JsonResponse({"success": True})

```



### Event Handling



Listen to specific webhook events:



```python

from listifypy import Webhook



webhook = Webhook("your-webhook-secret")



# Register event handlers

@webhook.on("vote.created")

def handle_vote_created(payload):

    user_id = payload["data"]["identities"]["discord"]["id"]

    print(f"🎉 User {user_id} voted!")

    grant_rewards(user_id)



@webhook.on("review.created")

def handle_review_created(payload):

    print(f"⭐ New review: {payload['data']['rating']}/5")

    print(f"Comment: {payload['data']['comment']}")



@webhook.on("listing.verified")

def handle_listing_verified(payload):

    print(f"✅ Bot verified! Badge: {payload['data']['badge']}")



@webhook.on("webhook")  # Catch-all event

def handle_any_event(payload):

    print(f"📨 Received event: {payload['event']}")

```



### Testing Webhooks



Test your webhook handlers locally:



```python

from listifypy import Webhook

import json

import time



webhook = Webhook("test-secret")



# Create a test payload

payload = webhook.create_test_payload(

    bot_id="test-bot-id",

    bot_slug="my-bot",

    bot_name="My Bot",

    event_type="vote.created"

)



# Generate signature

timestamp = str(int(time.time() * 1000))

raw_body = json.dumps(payload)

signature = webhook.generate_signature(timestamp, raw_body)



# Verify the signature

is_valid = webhook.validate_signature(timestamp, signature, raw_body)

print(f"Signature valid: {is_valid}")

```



## 🎨 Widget Generator



The Widget generator creates beautiful embeddable badges for your README and website.



### Widget Types



```python

from listifypy import Widget, create_widget



# Available widget types

widgets = {

    "large": "Full bot information with name, avatar, vote count, and rating",

    "votes": "Show only the vote count (perfect for README badges)",

    "owner": "Show bot owner avatar and name",

    "social": "Show server count, user count, and online status",

    "rating": "Show star rating and review count",

    "status": "Show online/offline/maintenance status",

    "combined": "Show votes, servers, and rating in one compact widget"

}



# Generate widget URLs

votes_url = Widget.votes("discord", "bot", "your-bot-id")

large_url = Widget.large("discord", "bot", "your-bot-id")

status_url = Widget.status("discord", "bot", "your-bot-id")

rating_url = Widget.rating("discord", "bot", "your-bot-id")

combined_url = Widget.combined("discord", "bot", "your-bot-id")



# Using the helper function

votes_url = create_widget("votes", "discord", "bot", "your-bot-id")

```



### Customization



Customize widget appearance:



```python

# Dark theme with custom colors

url = Widget.combined("discord", "bot", "your-bot-id", {

    "theme": "dark",

    "bg": "#2C2F33",

    "color": "#FFFFFF",

    "border": "#7289DA",

    "minimal": False

})



# Light theme with minimal design

url = Widget.votes("discord", "bot", "your-bot-id", {

    "theme": "light",

    "bg": "#FFFFFF",

    "color": "#000000",

    "minimal": True

})



# Custom colors only

url = Widget.large("discord", "bot", "your-bot-id", {

    "bg": "#5865F2",

    "color": "#FFFFFF"

})

```



### HTML & Markdown



Generate HTML or Markdown for embedding:



```python

from listifypy import Widget



url = Widget.votes("discord", "bot", "your-bot-id")



# Generate HTML

html = Widget.to_html(

    url,

    alt="Vote on Listify",

    options={

        "class_name": "badge",

        "style": {"margin": "10px", "border": "1px solid #ccc"},

        "width": 200,

        "height": 50

    }

)

print(html)

# <img src="..." alt="Vote on Listify" class="badge" style="margin:10px;border:1px solid #ccc" width="200" height="50" />



# Generate Markdown

markdown = Widget.to_markdown(url, "Vote on Listify")

print(markdown)

# ![Vote on Listify](https://www.rsdash.net/api/v1/widgets/votes/discord/bot/your-bot-id)

```



### Badge Shortcuts



Quick methods for common badge types:



```python

from listifypy import Widget



# Vote badge

vote_badge = Widget.badge_votes("discord", "bot", "your-bot-id")



# Status badge

status_badge = Widget.badge_status("discord", "bot", "your-bot-id")



# Rating badge

rating_badge = Widget.badge_rating("discord", "bot", "your-bot-id")

```



### README Example



```markdown

# My Discord Bot



[![Votes](https://www.rsdash.net/api/v1/widgets/votes/discord/bot/your-bot-id)](https://www.rsdash.net/bots/your-bot-id)

[![Status](https://www.rsdash.net/api/v1/widgets/status/discord/bot/your-bot-id)](https://www.rsdash.net/bots/your-bot-id)

[![Rating](https://www.rsdash.net/api/v1/widgets/rating/discord/bot/your-bot-id)](https://www.rsdash.net/bots/your-bot-id)



## Features



- Feature 1

- Feature 2

- Feature 3

```



## 🛡️ Error Handling



The SDK provides comprehensive error handling with `ListifyAPIError`:



```python

from listifypy import Api, ListifyAPIError, is_network_error



client = Api("bot-id", {"token": "token"})



try:

    result = await client.push_stats({

        "platform": "discord",

        "server_count": 420

    })

except ListifyAPIError as error:

    print(f"API Error: {error.message}")

    print(f"Status: {error.status}")

    print(f"Path: {error.path}")

    print(f"Method: {error.method}")

    

    # Check error type

    if error.is_rate_limit_error():

        print("Rate limited! Waiting before retry...")

        await asyncio.sleep(error.get_retry_delay() / 1000)

    

    if error.is_auth_error():

        print("Authentication failed! Check your token.")

    

    if error.is_server_error():

        print("Server error! Retrying...")

        if error.should_retry():

            await asyncio.sleep(error.get_retry_delay() / 1000)

    

    # Get user-friendly message

    print(f"Friendly message: {error.get_friendly_message()}")

    

except Exception as error:

    if is_network_error(error):

        print("Network error occurred!")

    else:

        print(f"Unexpected error: {error}")

```



### Error Type Guards



```python

from listifypy import (

    is_listify_api_error,

    is_network_error,

    is_timeout_error

)



try:

    # API call

    pass

except Exception as error:

    if is_listify_api_error(error):

        print(f"API Error: {error.message}")

    elif is_network_error(error):

        print("Network error - retrying...")

    elif is_timeout_error(error):

        print("Request timed out - increasing timeout...")

```



## ⚙️ Configuration



### Environment Variables



```bash

# Set environment variables

export LISTIFY_BOT_ID="your-bot-id"

export LISTIFY_API_TOKEN="your-api-token"

export LISTIFY_WEBHOOK_SECRET="your-webhook-secret"

```



```python

import os

from listifypy import Api



client = Api(

    bot_id=os.getenv("LISTIFY_BOT_ID"),

    options={

        "token": os.getenv("LISTIFY_API_TOKEN"),

        "log_level": os.getenv("LISTIFY_LOG_LEVEL", "info")

    }

)

```



### Logging Configuration



```python

# Text logging (default)

client = Api("bot-id", {

    "token": "token",

    "log_level": "debug",

    "log_format": "text"

})



# JSON logging (for structured logging)

client = Api("bot-id", {

    "token": "token",

    "log_level": "debug",

    "log_format": "json"

})



# Silent mode (no logging)

client = Api("bot-id", {

    "token": "token",

    "log_level": "silent"

})

```



### Cache Configuration



```python

# Enable cache with custom TTL

client = Api("bot-id", {

    "token": "token",

    "cache": True,

    "cache_ttl": 600  # 10 minutes

})



# Disable cache

client = Api("bot-id", {

    "token": "token",

    "cache": False

})



# Clear cache manually

client.clear_cache()



# Get cache status

status = client.get_rate_limit_status()

print(f"Rate limit: {status['remaining']}/{status['limit']}")

```



### Rate Limiting



The SDK handles rate limiting automatically:



```python

# Get current rate limit status

status = client.get_rate_limit_status()

print(f"Remaining: {status['remaining']}")

print(f"Reset at: {status['reset_at']}")



# Get pending requests count

pending = client.get_pending_requests()

print(f"Pending requests: {pending}")

```



## 🔬 Type System



The SDK is fully typed with type hints:



```python

from listifypy import (

    Api,

    Webhook,

    Widget,

    PlatformKey,

    RuntimeStatus,

    StatsUpdateInput,

    StatusUpdateInput,

    VoteCheckInput,

    CommandSyncInput,

    WebhookPayload,

    WidgetOptions

)



# Type-safe usage

platform: PlatformKey = "discord"

status: RuntimeStatus = "online"



stats: StatsUpdateInput = {

    "platform": platform,

    "server_count": 420,

    "status": status

}



# Type guards

from listifypy import is_platform_key, is_runtime_status



if is_platform_key(platform):

    print(f"Valid platform: {platform}")



if is_runtime_status(status):

    print(f"Valid status: {status}")

```



## 👥 Contributing



Contributions are welcome! Here's how to contribute:



1. Fork the repository

2. Create a feature branch

3. Make your changes

4. Run tests

5. Submit a pull request



### Development Setup



```bash

# Clone the repository

git clone https://github.com/phenuop/listify-pythonsdk.git

cd listify-pythonsdk



# Install development dependencies

pip install -e .[dev]



# Run tests

pytest tests/ -v



# Run tests with coverage

pytest tests/ -v --cov=src --cov-report=html



# Format code

black src/ tests/



# Run linting

flake8 src/ tests/



# Type checking

mypy src/

```



### Running Tests



```bash

# Run all tests

pytest



# Run specific test file

pytest tests/test_api.py -v



# Run with coverage

pytest --cov=src --cov-report=term-missing



# Run specific test class

pytest tests/test_all.py::TestApiClient -v

```



## 📄 License



This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.



## 🙏 Support



- **Documentation**: [https://docs.rsdash.net](https://rsdash.net/dash)

- **Issues**: [GitHub Issues](https://github.com/phenuop/listify-pythonsdk/issues)

- **Discord**: [Join our Discord](https://discord.gg/z3wrubzZE7)



## 📊 Version History



- **0.1.0** - Initial release

  - Full API client

  - Webhook handler with signature verification

  - Widget generator with 7 widget types

  - Complete type system

  - Comprehensive test suite



---



Made with ❤️ by the Listify Team
