| Name | rustcord JSON |
| Version |
0.2.0
JSON |
| download |
| home_page | None |
| Summary | A high-performance Discord API library with Rust core |
| upload_time | 2025-11-03 01:21:23 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | >=3.9 |
| license | MIT |
| 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"
}