Skip to main content

Python SDK for Listify API

Project description

ListifyPy - Python SDK for Listify API

PyPI version

Python versions

License

Tests

Coverage

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

Using pip

pip install listifypy

Using poetry

poetry add listifypy

Development Installation

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

cd listify-pythonsdk

pip install -e .[dev]

🏃 Quick Start

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

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:

# 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:

# 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:

# 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:

# 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:

# 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:

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:

# 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:

# 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:

# 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:

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

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

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

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:

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:

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

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:

# 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:

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:

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

# 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:

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

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

# Set environment variables

export LISTIFY_BOT_ID="your-bot-id"

export LISTIFY_API_TOKEN="your-api-token"

export LISTIFY_WEBHOOK_SECRET="your-webhook-secret"
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

# 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

# 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:

# 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:

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

# 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

# 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 file for details.

🙏 Support

📊 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

listifypy-0.1.0.tar.gz (41.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

listifypy-0.1.0-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file listifypy-0.1.0.tar.gz.

File metadata

  • Download URL: listifypy-0.1.0.tar.gz
  • Upload date:
  • Size: 41.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for listifypy-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5375b6fd70ba777ae075e6595a99305976784b96e9f9d51d56b0e39eb43d0436
MD5 6c4a3655e5e2b8fc77aa3973da1145e8
BLAKE2b-256 41a8963b65f3e85e9db20b8b097435421cdf91b65a043752267044432c7c133c

See more details on using hashes here.

File details

Details for the file listifypy-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: listifypy-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 27.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for listifypy-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c23de0cae2303ff4085681c40640faad7afd370423018cf12fb256713c0a7d1f
MD5 9ea7fe058085a3ccbd7d5117c83b1fe4
BLAKE2b-256 674f5010e99695cad35422c0d35acbfc0bf8e143071114358f4ace6ea343a769

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page