rustcord


Namerustcord JSON
Version 0.2.0 PyPI version JSON
download
home_pageNone
SummaryA high-performance Discord API library with Rust core
upload_time2025-11-03 01:21:23
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT
keywords discord bot api rust async websocket gateway
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # RustCord

RustCord is a high-performance asynchronous Python library for interacting with the Discord API, with core functionality implemented in Rust.

## Features

- Asynchronous Python interface using `asyncio`
- High-performance Rust core for Discord API interactions
- Support for Discord REST API and Gateway (WebSocket) connections
- Resilient WebSocket connection with:
  - Automatic reconnection with exponential backoff
  - Session resuming to prevent missed events
  - Heartbeat jitter to prevent thundering herd
  - Proper connection state management
  - Comprehensive error handling and recovery
- Event-based architecture for handling Discord events
- Slash command support with command registration and interaction responses
- Support for ephemeral responses, embeds, and deferred responses
- Support for autocomplete responses
- Support for permission based interaction commands
- Minimal memory footprint and CPU usage
- Clean and intuitive Python API

## Current Status

This project is in active development. Currently:

- The Python interface has been fully designed and implemented
- A complete implementation of the Discord API client in Python is available
- Bot functionality is working with the actual Discord API
- Enhanced WebSocket connection with robust error handling and reconnection logic
- Support for Discord slash commands and interactions
- Proper command registration and interaction responses with deferred responses
- Rich embeds system for creating visually appealing messages with fields, images, and formatting
- Fully functional event dispatching system with coroutine support
- Connection state tracking and smart reconnection strategies
- Voice channel support for joining/leaving voice channels and audio playback
- UI Components (buttons, select menus, etc.) for interactive bot experiences
- Rust backend implementation is ready, providing optimized performance for core functionality

## Installation

### From PyPI

```bash
pip install rustcord
```

### For Development

Clone the repository and install in development mode:

```bash
git clone https://github.com/ghulq/rustcord.git
cd rustcord
pip install -e .
```

### Dependencies

RustCord requires the following Python packages:

```bash
pip install aiohttp websockets
```

For bot functionality, you'll need to set the `DISCORD_TOKEN` environment variable:

```bash
export DISCORD_TOKEN=your_bot_token_here
```

## Usage Examples

### Basic Bot Example

```python
import asyncio
import os
from rustcord import Client, Intents

# Create Discord client with default intents
client = Client(intents=Intents.DEFAULT)

@client.event("ready")
async def on_ready(data):
    """Called when the bot is ready and connected to Discord"""
    me = await client.get_current_user()
    print(f"Logged in as {me.username}#{me.discriminator}")
    
    # Print list of connected guilds
    guilds = await client.get_current_guilds()
    print(f"Connected to {len(guilds)} guilds:")
    for guild in guilds:
        print(f"  - {guild.name} (ID: {guild.id})")

@client.event("message")
async def on_message(data):
    """Called when a message is received"""
    content = data.get("content", "")
    author = data.get("author", {})
    author_username = author.get("username", "")
    channel_id = data.get("channel_id", "")
    
    # Don't respond to our own messages
    if author.get("id") == (await client.get_current_user()).id:
        return
    
    print(f"Received message from {author_username}: {content}")
    
    # Simple command handler
    if content.startswith("!ping"):
        await client.send_message(channel_id, "Pong!")
    elif content.startswith("!hello"):
        await client.send_message(channel_id, f"Hello, {author_username}!")

async def main():
    # Connect to Discord
    try:
        await client.start()
        
        # Keep the bot running
        while True:
            await asyncio.sleep(1)
    except KeyboardInterrupt:
        print("Bot is shutting down...")
        await client.disconnect()

if __name__ == "__main__":
    # Get token from environment variable
    token = os.environ.get("DISCORD_TOKEN")
    if not token:
        print("Please set the DISCORD_TOKEN environment variable")
        exit(1)
    
    # Run the bot
    asyncio.run(main())
```

### Rich Embed Bot Example

```python
import asyncio
import os
from typing import Any
from rustcord import Client, Intents
from rustcord.embeds import Embed, Color

# Create Discord client
client = Client(intents=Intents.DEFAULT)

@client.event("ready")
async def on_ready(data: dict[str, Any]):
    """Called when the bot is ready"""
    me = await client.get_current_user()
    print(f"Logged in as {me.username}")
    print("Ready to send rich embeds!")

@client.event("message")
async def on_message(data: dict[str, Any]):
    """Called when a message is received"""
    content = data.get("content", "")
    author = data.get("author", {})
    author_id = author.get("id", "")
    channel_id = data.get("channel_id", "")
    
    # Don't respond to our own messages
    if author_id == (await client.get_current_user()).id:
        return
    
    # Send info embed
    if content == "!info":
        # Create a rich embed with various features
        embed = Embed(
            title="RustCord Information",
            description="A high-performance Discord API library with Rust core",
            color=Color.BLURPLE
        )
        
        # Add author information
        embed.set_author(
            name=author.get("username", "User"),
            icon_url=f"https://cdn.discordapp.com/avatars/{author_id}/{author.get('avatar')}.png"
        )
        
        # Add fields - some inline, some not
        embed.add_field("Version", "1.0.0", inline=True)
        embed.add_field("Library", "RustCord", inline=True)
        embed.add_field("Language", "Python + Rust", inline=True)
        embed.add_field("Features", "• Fast WebSocket connections\n• Rich embeds\n• Slash commands", inline=False)
        
        # Add footer
        embed.set_footer(text="Powered by RustCord", icon_url="https://example.com/icon.png")
        
        # Add timestamp
        embed.set_timestamp()
        
        # Send the embed
        await client.send_message(channel_id, embed=embed)
    
    # Send multiple embeds
    elif content == "!colors":
        # Create multiple embeds to demonstrate colors
        embeds = [
            Embed(title="Red Embed", description="This embed is red", color=Color.RED),
            Embed(title="Green Embed", description="This embed is green", color=Color.GREEN),
            Embed(title="Blue Embed", description="This embed is blue", color=Color.BLUE),
            Embed(title="Custom Color", description="This embed has a custom purple color", 
                  color=Color.from_rgb(128, 0, 255))
        ]
        
        # Send multiple embeds in one message
        await client.send_message(channel_id, content="Here are some colored embeds:", embeds=embeds)

async def main():
    """Main function"""
    try:
        await client.start()
        
        # Keep the bot running
        while True:
            await asyncio.sleep(1)
    except KeyboardInterrupt:
        print("Bot is shutting down...")
        await client.disconnect()

if __name__ == "__main__":
    # Run the bot
    asyncio.run(main())
```

### Slash Commands Example

```python
import asyncio
import os
from rustcord import Client
from rustcord.models import CommandOption, CommandOptionType, Interaction

# Create Discord client
client = Client()

@client.event("ready")
async def on_ready(data):
    """Called when the bot is ready"""
    me = await client.get_current_user()
    print(f"Logged in as {me.tag}")
    print("Bot is ready!")

# Define a simple ping command
@client.command(
    name="ping",
    description="Checks if the bot is responding"
)
async def ping_command(interaction: Interaction):
    """Handles the /ping command"""
    await interaction.respond("Pong! 🏓")

# Define a command with options
@client.command(
    name="echo",
    description="Echoes your message back to you",
    options=[
        CommandOption(
            type=CommandOptionType.STRING,
            name="message",
            description="The message to echo",
            required=True
        ),
        CommandOption(
            type=CommandOptionType.BOOLEAN,
            name="ephemeral",
            description="Whether the response should be ephemeral (only visible to you)",
            required=False
        )
    ]
)
async def echo_command(interaction: Interaction):
    """Handles the /echo command"""
    # Get the command options
    message = interaction.get_option_value("message", "")
    ephemeral = interaction.get_option_value("ephemeral", False)
    
    # Respond to the interaction
    await interaction.respond(
        content=f"You said: {message}",
        ephemeral=ephemeral
    )

async def main():
    """Main function"""
    try:
        # Connect to Discord
        await client.start()
        
        # Keep the bot running
        while True:
            await asyncio.sleep(1)
    except KeyboardInterrupt:
        print("Bot is shutting down...")
        await client.disconnect()

if __name__ == "__main__":
    # Run the bot
    asyncio.run(main())
```

## Advanced Features

The library currently supports:

- Slash Commands & Discord Interactions API
- Command Registration (both global and guild-specific)
- Command Options (arguments for slash commands)
- Subcommands and option choices
- Ephemeral Responses (responses only visible to the command user)
- Rich Embeds with fields, images, colors, and formatting
- Deferred Responses (for commands that take longer to process)
- Autocompletion for Interaction Options
- Permission based Interaction Commands
- Voice Channels support:
  - Joining and leaving voice channels
  - Audio playback from files
  - Volume control
- Sharding support for scaling bots to large guild counts

Coming soon:

- Type-safe API interfaces with Rust validation
- Modal Forms
- Voice recording and streaming

## Documentation

### Connection Architecture

RustCord's WebSocket connection to the Discord Gateway implements several important reliability features:

1. **Connection State Management**
   - Tracks the WebSocket connection through multiple states (Disconnected, Connecting, Connected, Reconnecting, Resuming, Identifying)
   - Safe state transitions to prevent race conditions with mutex locks

2. **Automatic Reconnection**
   - Exponential backoff strategy for reconnection attempts
   - Jitter added to prevent thundering herd problems
   - Configurable max reconnection attempts and backoff limits

3. **Session Resuming**
   - Maintains session information to resume instead of creating new sessions
   - Properly tracks sequence numbers to prevent missed events
   - Handles Discord's INVALID_SESSION events appropriately

4. **Heartbeat Management**
   - Immediate first heartbeat to quickly establish connection
   - Automatic heartbeat acknowledgement tracking with timeout detection
   - Jitter added to heartbeat timing to distribute load

5. **Error Recovery**
   - Comprehensive error handling for all Gateway events
   - Automatic recovery from common errors
   - Special handling for fatal connection errors (authentication failures, etc.)
   - Connection timeouts to detect stalled connections

### Main Components

#### Client

The `Client` class is the main entry point for interacting with Discord:

```python
from rustcord import Client, Intents

# Create a client with specific intents
client = Client(intents=Intents.DEFAULT)  

# Register event handlers
@client.event("ready")
async def on_ready(data):
    print("Bot is ready!")

# Start the client
await client.start()
```

#### Intents

Discord requires specifying which events your bot wants to receive through Gateway Intents:

```python
from rustcord import Intents

# Use predefined intent groups
client = Client(intents=Intents.DEFAULT)  # Common intents for most bots
client = Client(intents=Intents.ALL)      # All intents (privileged intents require approval)
client = Client(intents=Intents.NONE)     # No intents

# Or combine specific intents
intents = Intents.GUILDS | Intents.GUILD_MESSAGES | Intents.DIRECT_MESSAGES
client = Client(intents=intents)
```

#### Events

Register handlers for Discord Gateway events:

```python
@client.event("ready")
async def on_ready(data):
    print("Bot is ready!")

@client.event("message")
async def on_message(data):
    print(f"Received message: {data.get('content')}")
```

#### Slash Commands

Register and handle slash commands:

```python
@client.command(
    name="ping",
    description="Checks if the bot is responding"
)
async def ping_command(interaction):
    await interaction.respond("Pong!")
```

Commands with options:

```python
@client.command(
    name="echo",
    description="Echoes your message",
    options=[
        CommandOption(
            type=CommandOptionType.STRING,
            name="message",
            description="The message to echo",
            required=True
        )
    ]
)
async def echo_command(interaction):
    message = interaction.get_option_value("message")
    await interaction.respond(f"You said: {message}")
```

Guild-specific commands:

```python
@client.command(
    name="guild_only",
    description="This command only works in a specific guild",
    guild_id="123456789012345678"  # Your guild ID here
)
async def guild_command(interaction):
    await interaction.respond("This command only works in this server!")
```

#### Rich Embeds

Create rich embed messages with various formatting options:

```python
from rustcord import Client
from rustcord.embeds import Embed, Color

# Create a simple embed
embed = Embed(
    title="Hello, world!",
    description="This is a rich embed message",
    color=Color.BLUE
)

# Add fields
embed.add_field("Regular Field", "This is a regular field", inline=False)
embed.add_field("Inline Field 1", "This is inline", inline=True)
embed.add_field("Inline Field 2", "This is also inline", inline=True)

# Add author information
embed.set_author(
    name="RustCord Bot",
    icon_url="https://example.com/icon.png"
)

# Add footer
embed.set_footer(
    text="Powered by RustCord",
    icon_url="https://example.com/footer_icon.png"
)

# Add images
embed.set_thumbnail("https://example.com/thumbnail.png")
embed.set_image("https://example.com/image.png")

# Send the embed
await client.send_message(channel_id, embed=embed)

# Send multiple embeds
embeds = [
    Embed(title="First Embed", color=Color.RED),
    Embed(title="Second Embed", color=Color.GREEN)
]
await client.send_message(channel_id, embeds=embeds)

# Color utilities
custom_color = Color.from_rgb(255, 0, 255)  # Creates purple color
```

#### Interaction Responses

Respond to interactions in various ways:

```python
# Basic text response
await interaction.respond("Hello!")

# Ephemeral response (only visible to the command user)
await interaction.respond("This is private", ephemeral=True)

# Response with embeds using the Embed class
from rustcord.embeds import Embed, Color

embed = Embed(
    title="Embed Title",
    description="This is an embed",
    color=Color.BLURPLE
)
embed.add_field("Field 1", "Value 1", inline=True)
embed.add_field("Field 2", "Value 2", inline=True)

await interaction.respond(embed=embed)

# Deferred response (for commands that take time to process)
await interaction.defer_response()
# ... do some work ...
await client.edit_interaction_response(
    interaction.token,
    {"content": "Here's the result after processing!"}
)
```

#### Voice Features

Connect to voice channels and play audio:

```python
from rustcord import Client, Intents
from rustcord.models import VoiceConnection, AudioPlayer

# Create Discord client with voice intents
intents = Intents.DEFAULT | Intents.GUILD_VOICE_STATES
client = Client(intents=intents)

# Store active voice connections and players
voice_connections = {}
audio_players = {}

@client.command("join", "Join a voice channel")
async def join_command(interaction):
    guild_id = interaction.guild_id
    channel_id = interaction.get_option_value("channel")
    
    # Join the voice channel
    connection = await client.join_voice_channel(guild_id, channel_id)
    if connection:
        voice_connections[guild_id] = connection
        
        # Create an audio player for this connection
        player = await client.create_audio_player()
        audio_players[guild_id] = player
        
        # Attach the player to the connection
        await player.attach(connection)
        
        await interaction.respond("Joined voice channel successfully!")

@client.command("play", "Play audio in the voice channel")
async def play_command(interaction):
    guild_id = interaction.guild_id
    file_path = interaction.get_option_value("file")
    
    # Check if we're in a voice channel
    if guild_id not in audio_players:
        await interaction.respond("I need to join a voice channel first!")
        return
    
    # Play audio
    player = audio_players[guild_id]
    success = await player.play_file(file_path)
    
    if success:
        await interaction.respond(f"Now playing: {file_path}")
    else:
        await interaction.respond("Failed to play the file.")

@client.command("leave", "Leave the voice channel")
async def leave_command(interaction):
    guild_id = interaction.guild_id
    
    # Leave the voice channel
    if guild_id in voice_connections:
        # Stop any audio playback
        if guild_id in audio_players:
            await audio_players[guild_id].stop()
        
        # Leave the channel
        success = await client.leave_voice_channel(guild_id)
        if success:
            await interaction.respond("Left voice channel!")
```

#### Sharding

For bots in large numbers of guilds (approaching Discord's limit of 2500 guilds per connection), sharding is necessary:

```python
from rustcord import Client, Intents

# Create a sharded client
client = Client(
    intents=Intents.DEFAULT,
    shard_id=0,     # Current shard ID (0-based)
    shard_count=2   # Total number of shards
)

# The client will only receive events for guilds that match:
# guild_id % shard_count = shard_id

# Multiple shards can be run in separate processes
# For example, to run shard 1 of 2:
client_shard1 = Client(
    intents=Intents.DEFAULT,
    shard_id=1,
    shard_count=2
)

# Rest of the code is the same as a non-sharded bot
@client.event("ready")
async def on_ready(data):
    print(f"Shard 0 is ready!")

@client_shard1.event("ready")
async def on_ready_shard1(data):
    print(f"Shard 1 is ready!")
```


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "rustcord",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "discord, bot, api, rust, async, websocket, gateway",
    "author": null,
    "author_email": "ghulq <ghulq@mcbehub.de>",
    "download_url": "https://files.pythonhosted.org/packages/85/cd/bec477388c3d695af5d9b2495407d5733c112edcf93d4442351f7313b820/rustcord-0.2.0.tar.gz",
    "platform": null,
    "description": "# RustCord\r\n\r\nRustCord is a high-performance asynchronous Python library for interacting with the Discord API, with core functionality implemented in Rust.\r\n\r\n## Features\r\n\r\n- Asynchronous Python interface using `asyncio`\r\n- High-performance Rust core for Discord API interactions\r\n- Support for Discord REST API and Gateway (WebSocket) connections\r\n- Resilient WebSocket connection with:\r\n  - Automatic reconnection with exponential backoff\r\n  - Session resuming to prevent missed events\r\n  - Heartbeat jitter to prevent thundering herd\r\n  - Proper connection state management\r\n  - Comprehensive error handling and recovery\r\n- Event-based architecture for handling Discord events\r\n- Slash command support with command registration and interaction responses\r\n- Support for ephemeral responses, embeds, and deferred responses\r\n- Support for autocomplete responses\r\n- Support for permission based interaction commands\r\n- Minimal memory footprint and CPU usage\r\n- Clean and intuitive Python API\r\n\r\n## Current Status\r\n\r\nThis project is in active development. Currently:\r\n\r\n- The Python interface has been fully designed and implemented\r\n- A complete implementation of the Discord API client in Python is available\r\n- Bot functionality is working with the actual Discord API\r\n- Enhanced WebSocket connection with robust error handling and reconnection logic\r\n- Support for Discord slash commands and interactions\r\n- Proper command registration and interaction responses with deferred responses\r\n- Rich embeds system for creating visually appealing messages with fields, images, and formatting\r\n- Fully functional event dispatching system with coroutine support\r\n- Connection state tracking and smart reconnection strategies\r\n- Voice channel support for joining/leaving voice channels and audio playback\r\n- UI Components (buttons, select menus, etc.) for interactive bot experiences\r\n- Rust backend implementation is ready, providing optimized performance for core functionality\r\n\r\n## Installation\r\n\r\n### From PyPI\r\n\r\n```bash\r\npip install rustcord\r\n```\r\n\r\n### For Development\r\n\r\nClone the repository and install in development mode:\r\n\r\n```bash\r\ngit clone https://github.com/ghulq/rustcord.git\r\ncd rustcord\r\npip install -e .\r\n```\r\n\r\n### Dependencies\r\n\r\nRustCord requires the following Python packages:\r\n\r\n```bash\r\npip install aiohttp websockets\r\n```\r\n\r\nFor bot functionality, you'll need to set the `DISCORD_TOKEN` environment variable:\r\n\r\n```bash\r\nexport DISCORD_TOKEN=your_bot_token_here\r\n```\r\n\r\n## Usage Examples\r\n\r\n### Basic Bot Example\r\n\r\n```python\r\nimport asyncio\r\nimport os\r\nfrom rustcord import Client, Intents\r\n\r\n# Create Discord client with default intents\r\nclient = Client(intents=Intents.DEFAULT)\r\n\r\n@client.event(\"ready\")\r\nasync def on_ready(data):\r\n    \"\"\"Called when the bot is ready and connected to Discord\"\"\"\r\n    me = await client.get_current_user()\r\n    print(f\"Logged in as {me.username}#{me.discriminator}\")\r\n    \r\n    # Print list of connected guilds\r\n    guilds = await client.get_current_guilds()\r\n    print(f\"Connected to {len(guilds)} guilds:\")\r\n    for guild in guilds:\r\n        print(f\"  - {guild.name} (ID: {guild.id})\")\r\n\r\n@client.event(\"message\")\r\nasync def on_message(data):\r\n    \"\"\"Called when a message is received\"\"\"\r\n    content = data.get(\"content\", \"\")\r\n    author = data.get(\"author\", {})\r\n    author_username = author.get(\"username\", \"\")\r\n    channel_id = data.get(\"channel_id\", \"\")\r\n    \r\n    # Don't respond to our own messages\r\n    if author.get(\"id\") == (await client.get_current_user()).id:\r\n        return\r\n    \r\n    print(f\"Received message from {author_username}: {content}\")\r\n    \r\n    # Simple command handler\r\n    if content.startswith(\"!ping\"):\r\n        await client.send_message(channel_id, \"Pong!\")\r\n    elif content.startswith(\"!hello\"):\r\n        await client.send_message(channel_id, f\"Hello, {author_username}!\")\r\n\r\nasync def main():\r\n    # Connect to Discord\r\n    try:\r\n        await client.start()\r\n        \r\n        # Keep the bot running\r\n        while True:\r\n            await asyncio.sleep(1)\r\n    except KeyboardInterrupt:\r\n        print(\"Bot is shutting down...\")\r\n        await client.disconnect()\r\n\r\nif __name__ == \"__main__\":\r\n    # Get token from environment variable\r\n    token = os.environ.get(\"DISCORD_TOKEN\")\r\n    if not token:\r\n        print(\"Please set the DISCORD_TOKEN environment variable\")\r\n        exit(1)\r\n    \r\n    # Run the bot\r\n    asyncio.run(main())\r\n```\r\n\r\n### Rich Embed Bot Example\r\n\r\n```python\r\nimport asyncio\r\nimport os\r\nfrom typing import Any\r\nfrom rustcord import Client, Intents\r\nfrom rustcord.embeds import Embed, Color\r\n\r\n# Create Discord client\r\nclient = Client(intents=Intents.DEFAULT)\r\n\r\n@client.event(\"ready\")\r\nasync def on_ready(data: dict[str, Any]):\r\n    \"\"\"Called when the bot is ready\"\"\"\r\n    me = await client.get_current_user()\r\n    print(f\"Logged in as {me.username}\")\r\n    print(\"Ready to send rich embeds!\")\r\n\r\n@client.event(\"message\")\r\nasync def on_message(data: dict[str, Any]):\r\n    \"\"\"Called when a message is received\"\"\"\r\n    content = data.get(\"content\", \"\")\r\n    author = data.get(\"author\", {})\r\n    author_id = author.get(\"id\", \"\")\r\n    channel_id = data.get(\"channel_id\", \"\")\r\n    \r\n    # Don't respond to our own messages\r\n    if author_id == (await client.get_current_user()).id:\r\n        return\r\n    \r\n    # Send info embed\r\n    if content == \"!info\":\r\n        # Create a rich embed with various features\r\n        embed = Embed(\r\n            title=\"RustCord Information\",\r\n            description=\"A high-performance Discord API library with Rust core\",\r\n            color=Color.BLURPLE\r\n        )\r\n        \r\n        # Add author information\r\n        embed.set_author(\r\n            name=author.get(\"username\", \"User\"),\r\n            icon_url=f\"https://cdn.discordapp.com/avatars/{author_id}/{author.get('avatar')}.png\"\r\n        )\r\n        \r\n        # Add fields - some inline, some not\r\n        embed.add_field(\"Version\", \"1.0.0\", inline=True)\r\n        embed.add_field(\"Library\", \"RustCord\", inline=True)\r\n        embed.add_field(\"Language\", \"Python + Rust\", inline=True)\r\n        embed.add_field(\"Features\", \"\u2022 Fast WebSocket connections\\n\u2022 Rich embeds\\n\u2022 Slash commands\", inline=False)\r\n        \r\n        # Add footer\r\n        embed.set_footer(text=\"Powered by RustCord\", icon_url=\"https://example.com/icon.png\")\r\n        \r\n        # Add timestamp\r\n        embed.set_timestamp()\r\n        \r\n        # Send the embed\r\n        await client.send_message(channel_id, embed=embed)\r\n    \r\n    # Send multiple embeds\r\n    elif content == \"!colors\":\r\n        # Create multiple embeds to demonstrate colors\r\n        embeds = [\r\n            Embed(title=\"Red Embed\", description=\"This embed is red\", color=Color.RED),\r\n            Embed(title=\"Green Embed\", description=\"This embed is green\", color=Color.GREEN),\r\n            Embed(title=\"Blue Embed\", description=\"This embed is blue\", color=Color.BLUE),\r\n            Embed(title=\"Custom Color\", description=\"This embed has a custom purple color\", \r\n                  color=Color.from_rgb(128, 0, 255))\r\n        ]\r\n        \r\n        # Send multiple embeds in one message\r\n        await client.send_message(channel_id, content=\"Here are some colored embeds:\", embeds=embeds)\r\n\r\nasync def main():\r\n    \"\"\"Main function\"\"\"\r\n    try:\r\n        await client.start()\r\n        \r\n        # Keep the bot running\r\n        while True:\r\n            await asyncio.sleep(1)\r\n    except KeyboardInterrupt:\r\n        print(\"Bot is shutting down...\")\r\n        await client.disconnect()\r\n\r\nif __name__ == \"__main__\":\r\n    # Run the bot\r\n    asyncio.run(main())\r\n```\r\n\r\n### Slash Commands Example\r\n\r\n```python\r\nimport asyncio\r\nimport os\r\nfrom rustcord import Client\r\nfrom rustcord.models import CommandOption, CommandOptionType, Interaction\r\n\r\n# Create Discord client\r\nclient = Client()\r\n\r\n@client.event(\"ready\")\r\nasync def on_ready(data):\r\n    \"\"\"Called when the bot is ready\"\"\"\r\n    me = await client.get_current_user()\r\n    print(f\"Logged in as {me.tag}\")\r\n    print(\"Bot is ready!\")\r\n\r\n# Define a simple ping command\r\n@client.command(\r\n    name=\"ping\",\r\n    description=\"Checks if the bot is responding\"\r\n)\r\nasync def ping_command(interaction: Interaction):\r\n    \"\"\"Handles the /ping command\"\"\"\r\n    await interaction.respond(\"Pong! \ud83c\udfd3\")\r\n\r\n# Define a command with options\r\n@client.command(\r\n    name=\"echo\",\r\n    description=\"Echoes your message back to you\",\r\n    options=[\r\n        CommandOption(\r\n            type=CommandOptionType.STRING,\r\n            name=\"message\",\r\n            description=\"The message to echo\",\r\n            required=True\r\n        ),\r\n        CommandOption(\r\n            type=CommandOptionType.BOOLEAN,\r\n            name=\"ephemeral\",\r\n            description=\"Whether the response should be ephemeral (only visible to you)\",\r\n            required=False\r\n        )\r\n    ]\r\n)\r\nasync def echo_command(interaction: Interaction):\r\n    \"\"\"Handles the /echo command\"\"\"\r\n    # Get the command options\r\n    message = interaction.get_option_value(\"message\", \"\")\r\n    ephemeral = interaction.get_option_value(\"ephemeral\", False)\r\n    \r\n    # Respond to the interaction\r\n    await interaction.respond(\r\n        content=f\"You said: {message}\",\r\n        ephemeral=ephemeral\r\n    )\r\n\r\nasync def main():\r\n    \"\"\"Main function\"\"\"\r\n    try:\r\n        # Connect to Discord\r\n        await client.start()\r\n        \r\n        # Keep the bot running\r\n        while True:\r\n            await asyncio.sleep(1)\r\n    except KeyboardInterrupt:\r\n        print(\"Bot is shutting down...\")\r\n        await client.disconnect()\r\n\r\nif __name__ == \"__main__\":\r\n    # Run the bot\r\n    asyncio.run(main())\r\n```\r\n\r\n## Advanced Features\r\n\r\nThe library currently supports:\r\n\r\n- Slash Commands & Discord Interactions API\r\n- Command Registration (both global and guild-specific)\r\n- Command Options (arguments for slash commands)\r\n- Subcommands and option choices\r\n- Ephemeral Responses (responses only visible to the command user)\r\n- Rich Embeds with fields, images, colors, and formatting\r\n- Deferred Responses (for commands that take longer to process)\r\n- Autocompletion for Interaction Options\r\n- Permission based Interaction Commands\r\n- Voice Channels support:\r\n  - Joining and leaving voice channels\r\n  - Audio playback from files\r\n  - Volume control\r\n- Sharding support for scaling bots to large guild counts\r\n\r\nComing soon:\r\n\r\n- Type-safe API interfaces with Rust validation\r\n- Modal Forms\r\n- Voice recording and streaming\r\n\r\n## Documentation\r\n\r\n### Connection Architecture\r\n\r\nRustCord's WebSocket connection to the Discord Gateway implements several important reliability features:\r\n\r\n1. **Connection State Management**\r\n   - Tracks the WebSocket connection through multiple states (Disconnected, Connecting, Connected, Reconnecting, Resuming, Identifying)\r\n   - Safe state transitions to prevent race conditions with mutex locks\r\n\r\n2. **Automatic Reconnection**\r\n   - Exponential backoff strategy for reconnection attempts\r\n   - Jitter added to prevent thundering herd problems\r\n   - Configurable max reconnection attempts and backoff limits\r\n\r\n3. **Session Resuming**\r\n   - Maintains session information to resume instead of creating new sessions\r\n   - Properly tracks sequence numbers to prevent missed events\r\n   - Handles Discord's INVALID_SESSION events appropriately\r\n\r\n4. **Heartbeat Management**\r\n   - Immediate first heartbeat to quickly establish connection\r\n   - Automatic heartbeat acknowledgement tracking with timeout detection\r\n   - Jitter added to heartbeat timing to distribute load\r\n\r\n5. **Error Recovery**\r\n   - Comprehensive error handling for all Gateway events\r\n   - Automatic recovery from common errors\r\n   - Special handling for fatal connection errors (authentication failures, etc.)\r\n   - Connection timeouts to detect stalled connections\r\n\r\n### Main Components\r\n\r\n#### Client\r\n\r\nThe `Client` class is the main entry point for interacting with Discord:\r\n\r\n```python\r\nfrom rustcord import Client, Intents\r\n\r\n# Create a client with specific intents\r\nclient = Client(intents=Intents.DEFAULT)  \r\n\r\n# Register event handlers\r\n@client.event(\"ready\")\r\nasync def on_ready(data):\r\n    print(\"Bot is ready!\")\r\n\r\n# Start the client\r\nawait client.start()\r\n```\r\n\r\n#### Intents\r\n\r\nDiscord requires specifying which events your bot wants to receive through Gateway Intents:\r\n\r\n```python\r\nfrom rustcord import Intents\r\n\r\n# Use predefined intent groups\r\nclient = Client(intents=Intents.DEFAULT)  # Common intents for most bots\r\nclient = Client(intents=Intents.ALL)      # All intents (privileged intents require approval)\r\nclient = Client(intents=Intents.NONE)     # No intents\r\n\r\n# Or combine specific intents\r\nintents = Intents.GUILDS | Intents.GUILD_MESSAGES | Intents.DIRECT_MESSAGES\r\nclient = Client(intents=intents)\r\n```\r\n\r\n#### Events\r\n\r\nRegister handlers for Discord Gateway events:\r\n\r\n```python\r\n@client.event(\"ready\")\r\nasync def on_ready(data):\r\n    print(\"Bot is ready!\")\r\n\r\n@client.event(\"message\")\r\nasync def on_message(data):\r\n    print(f\"Received message: {data.get('content')}\")\r\n```\r\n\r\n#### Slash Commands\r\n\r\nRegister and handle slash commands:\r\n\r\n```python\r\n@client.command(\r\n    name=\"ping\",\r\n    description=\"Checks if the bot is responding\"\r\n)\r\nasync def ping_command(interaction):\r\n    await interaction.respond(\"Pong!\")\r\n```\r\n\r\nCommands with options:\r\n\r\n```python\r\n@client.command(\r\n    name=\"echo\",\r\n    description=\"Echoes your message\",\r\n    options=[\r\n        CommandOption(\r\n            type=CommandOptionType.STRING,\r\n            name=\"message\",\r\n            description=\"The message to echo\",\r\n            required=True\r\n        )\r\n    ]\r\n)\r\nasync def echo_command(interaction):\r\n    message = interaction.get_option_value(\"message\")\r\n    await interaction.respond(f\"You said: {message}\")\r\n```\r\n\r\nGuild-specific commands:\r\n\r\n```python\r\n@client.command(\r\n    name=\"guild_only\",\r\n    description=\"This command only works in a specific guild\",\r\n    guild_id=\"123456789012345678\"  # Your guild ID here\r\n)\r\nasync def guild_command(interaction):\r\n    await interaction.respond(\"This command only works in this server!\")\r\n```\r\n\r\n#### Rich Embeds\r\n\r\nCreate rich embed messages with various formatting options:\r\n\r\n```python\r\nfrom rustcord import Client\r\nfrom rustcord.embeds import Embed, Color\r\n\r\n# Create a simple embed\r\nembed = Embed(\r\n    title=\"Hello, world!\",\r\n    description=\"This is a rich embed message\",\r\n    color=Color.BLUE\r\n)\r\n\r\n# Add fields\r\nembed.add_field(\"Regular Field\", \"This is a regular field\", inline=False)\r\nembed.add_field(\"Inline Field 1\", \"This is inline\", inline=True)\r\nembed.add_field(\"Inline Field 2\", \"This is also inline\", inline=True)\r\n\r\n# Add author information\r\nembed.set_author(\r\n    name=\"RustCord Bot\",\r\n    icon_url=\"https://example.com/icon.png\"\r\n)\r\n\r\n# Add footer\r\nembed.set_footer(\r\n    text=\"Powered by RustCord\",\r\n    icon_url=\"https://example.com/footer_icon.png\"\r\n)\r\n\r\n# Add images\r\nembed.set_thumbnail(\"https://example.com/thumbnail.png\")\r\nembed.set_image(\"https://example.com/image.png\")\r\n\r\n# Send the embed\r\nawait client.send_message(channel_id, embed=embed)\r\n\r\n# Send multiple embeds\r\nembeds = [\r\n    Embed(title=\"First Embed\", color=Color.RED),\r\n    Embed(title=\"Second Embed\", color=Color.GREEN)\r\n]\r\nawait client.send_message(channel_id, embeds=embeds)\r\n\r\n# Color utilities\r\ncustom_color = Color.from_rgb(255, 0, 255)  # Creates purple color\r\n```\r\n\r\n#### Interaction Responses\r\n\r\nRespond to interactions in various ways:\r\n\r\n```python\r\n# Basic text response\r\nawait interaction.respond(\"Hello!\")\r\n\r\n# Ephemeral response (only visible to the command user)\r\nawait interaction.respond(\"This is private\", ephemeral=True)\r\n\r\n# Response with embeds using the Embed class\r\nfrom rustcord.embeds import Embed, Color\r\n\r\nembed = Embed(\r\n    title=\"Embed Title\",\r\n    description=\"This is an embed\",\r\n    color=Color.BLURPLE\r\n)\r\nembed.add_field(\"Field 1\", \"Value 1\", inline=True)\r\nembed.add_field(\"Field 2\", \"Value 2\", inline=True)\r\n\r\nawait interaction.respond(embed=embed)\r\n\r\n# Deferred response (for commands that take time to process)\r\nawait interaction.defer_response()\r\n# ... do some work ...\r\nawait client.edit_interaction_response(\r\n    interaction.token,\r\n    {\"content\": \"Here's the result after processing!\"}\r\n)\r\n```\r\n\r\n#### Voice Features\r\n\r\nConnect to voice channels and play audio:\r\n\r\n```python\r\nfrom rustcord import Client, Intents\r\nfrom rustcord.models import VoiceConnection, AudioPlayer\r\n\r\n# Create Discord client with voice intents\r\nintents = Intents.DEFAULT | Intents.GUILD_VOICE_STATES\r\nclient = Client(intents=intents)\r\n\r\n# Store active voice connections and players\r\nvoice_connections = {}\r\naudio_players = {}\r\n\r\n@client.command(\"join\", \"Join a voice channel\")\r\nasync def join_command(interaction):\r\n    guild_id = interaction.guild_id\r\n    channel_id = interaction.get_option_value(\"channel\")\r\n    \r\n    # Join the voice channel\r\n    connection = await client.join_voice_channel(guild_id, channel_id)\r\n    if connection:\r\n        voice_connections[guild_id] = connection\r\n        \r\n        # Create an audio player for this connection\r\n        player = await client.create_audio_player()\r\n        audio_players[guild_id] = player\r\n        \r\n        # Attach the player to the connection\r\n        await player.attach(connection)\r\n        \r\n        await interaction.respond(\"Joined voice channel successfully!\")\r\n\r\n@client.command(\"play\", \"Play audio in the voice channel\")\r\nasync def play_command(interaction):\r\n    guild_id = interaction.guild_id\r\n    file_path = interaction.get_option_value(\"file\")\r\n    \r\n    # Check if we're in a voice channel\r\n    if guild_id not in audio_players:\r\n        await interaction.respond(\"I need to join a voice channel first!\")\r\n        return\r\n    \r\n    # Play audio\r\n    player = audio_players[guild_id]\r\n    success = await player.play_file(file_path)\r\n    \r\n    if success:\r\n        await interaction.respond(f\"Now playing: {file_path}\")\r\n    else:\r\n        await interaction.respond(\"Failed to play the file.\")\r\n\r\n@client.command(\"leave\", \"Leave the voice channel\")\r\nasync def leave_command(interaction):\r\n    guild_id = interaction.guild_id\r\n    \r\n    # Leave the voice channel\r\n    if guild_id in voice_connections:\r\n        # Stop any audio playback\r\n        if guild_id in audio_players:\r\n            await audio_players[guild_id].stop()\r\n        \r\n        # Leave the channel\r\n        success = await client.leave_voice_channel(guild_id)\r\n        if success:\r\n            await interaction.respond(\"Left voice channel!\")\r\n```\r\n\r\n#### Sharding\r\n\r\nFor bots in large numbers of guilds (approaching Discord's limit of 2500 guilds per connection), sharding is necessary:\r\n\r\n```python\r\nfrom rustcord import Client, Intents\r\n\r\n# Create a sharded client\r\nclient = Client(\r\n    intents=Intents.DEFAULT,\r\n    shard_id=0,     # Current shard ID (0-based)\r\n    shard_count=2   # Total number of shards\r\n)\r\n\r\n# The client will only receive events for guilds that match:\r\n# guild_id % shard_count = shard_id\r\n\r\n# Multiple shards can be run in separate processes\r\n# For example, to run shard 1 of 2:\r\nclient_shard1 = Client(\r\n    intents=Intents.DEFAULT,\r\n    shard_id=1,\r\n    shard_count=2\r\n)\r\n\r\n# Rest of the code is the same as a non-sharded bot\r\n@client.event(\"ready\")\r\nasync def on_ready(data):\r\n    print(f\"Shard 0 is ready!\")\r\n\r\n@client_shard1.event(\"ready\")\r\nasync def on_ready_shard1(data):\r\n    print(f\"Shard 1 is ready!\")\r\n```\r\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A high-performance Discord API library with Rust core",
    "version": "0.2.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/ghulq/rustcord/issues",
        "Documentation": "https://github.com/ghulq/rustcord#readme",
        "Homepage": "https://github.com/ghulq/rustcord",
        "Source": "https://github.com/ghulq/rustcord"
    },
    "split_keywords": [
        "discord",
        " bot",
        " api",
        " rust",
        " async",
        " websocket",
        " gateway"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "10691fba5b25984070cafbb2587c2c7b1cd77c801463b9435498aadc5c589b34",
                "md5": "2bf9d05f89650272d98adfa3fe32b5c7",
                "sha256": "aa257259c421ac1a53228b323f0de45b4d636d3d69bba6c59b1082d750e80ffe"
            },
            "downloads": -1,
            "filename": "rustcord-0.2.0-cp39-abi3-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "2bf9d05f89650272d98adfa3fe32b5c7",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 2300334,
            "upload_time": "2025-11-03T01:21:22",
            "upload_time_iso_8601": "2025-11-03T01:21:22.315167Z",
            "url": "https://files.pythonhosted.org/packages/10/69/1fba5b25984070cafbb2587c2c7b1cd77c801463b9435498aadc5c589b34/rustcord-0.2.0-cp39-abi3-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "85cdbec477388c3d695af5d9b2495407d5733c112edcf93d4442351f7313b820",
                "md5": "3d47900dcb86851466f7042cd825aa5c",
                "sha256": "571a61436f9a381129107e942e1d0d10141f784ade4e8f231aceb959a936848b"
            },
            "downloads": -1,
            "filename": "rustcord-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "3d47900dcb86851466f7042cd825aa5c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 76953,
            "upload_time": "2025-11-03T01:21:23",
            "upload_time_iso_8601": "2025-11-03T01:21:23.924950Z",
            "url": "https://files.pythonhosted.org/packages/85/cd/bec477388c3d695af5d9b2495407d5733c112edcf93d4442351f7313b820/rustcord-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-03 01:21:23",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ghulq",
    "github_project": "rustcord",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "rustcord"
}
        
Elapsed time: 2.23969s