Discord RPC client written in Python for integrating applications with Discord's Rich Presence system
—
Comprehensive exception hierarchy for handling Discord integration errors. Pypresence provides specific exceptions for different failure scenarios, enabling precise error handling and debugging.
class PyPresenceException(Exception):
def __init__(self, message: str = None):
"""
Base exception for all pypresence errors.
Parameters:
- message (str): Optional custom error message
"""class DiscordNotFound(PyPresenceException):
def __init__(self):
"""
Raised when Discord is not installed or not running.
This exception indicates that the Discord client could not be found
on the system or is not currently running.
"""class InvalidPipe(PyPresenceException):
def __init__(self):
"""
Raised when the Discord IPC pipe cannot be found.
This typically occurs when Discord is not running or the IPC
communication channels are not available.
"""class ConnectionTimeout(PyPresenceException):
def __init__(self):
"""
Raised when unable to create a connection to the pipe in time.
This occurs when the connection attempt exceeds the specified
connection_timeout value.
"""class PipeClosed(PyPresenceException):
def __init__(self):
"""
Raised when the Discord IPC pipe is closed unexpectedly.
This can occur when Discord is closed while maintaining an active
connection. Applications should catch this and attempt to reconnect.
"""class ResponseTimeout(PyPresenceException):
def __init__(self):
"""
Raised when no response is received from Discord RPC in time.
This occurs when Discord doesn't respond within the specified
response_timeout value.
"""class ServerError(PyPresenceException):
def __init__(self, message: str):
"""
Raised when Discord server returns an error.
Parameters:
- message (str): Error message from Discord server
"""class DiscordError(PyPresenceException):
def __init__(self, code: int, message: str, override: bool = False):
"""
Raised when Discord API returns an error response.
Parameters:
- code (int): Discord error code
- message (str): Error message from Discord
- override (bool): Whether to use message as-is
Attributes:
- code (int): Discord error code
- message (str): Original error message
"""class InvalidID(DiscordError):
def __init__(self):
"""
Raised when the Discord application client ID is invalid.
This occurs when the provided client_id doesn't match any
registered Discord application.
"""class InvalidArgument(PyPresenceException):
def __init__(self, expected, received, description: str = None):
"""
Raised when invalid arguments are passed to functions.
Parameters:
- expected: Expected argument type or value
- received: Actual argument type or value received
- description (str): Optional additional description
"""class ArgumentError(PyPresenceException):
def __init__(self):
"""
Raised when function arguments are incorrect.
Typically occurs when event handler functions don't have
the correct number of parameters.
"""class EventNotFound(PyPresenceException):
def __init__(self, event: str):
"""
Raised when trying to unregister a non-existent event.
Parameters:
- event (str): Name of the event that wasn't found
"""PyPresenceException (base)
├── DiscordNotFound
├── InvalidPipe
├── ConnectionTimeout
├── PipeClosed
├── ResponseTimeout
├── ServerError
├── InvalidArgument
├── ArgumentError
├── EventNotFound
└── DiscordError
└── InvalidIDfrom pypresence import Presence
from pypresence import (
DiscordNotFound, InvalidPipe, PipeClosed,
ResponseTimeout, InvalidID
)
def connect_with_retry(client_id, max_retries=3):
for attempt in range(max_retries):
try:
RPC = Presence(client_id)
RPC.connect()
return RPC
except DiscordNotFound:
print("Discord is not running. Please start Discord.")
return None
except InvalidID:
print("Invalid client ID. Check your Discord application settings.")
return None
except (InvalidPipe, ConnectionTimeout) as e:
print(f"Connection failed (attempt {attempt + 1}): {e}")
if attempt == max_retries - 1:
print("Max retries reached. Unable to connect.")
return None
time.sleep(2) # Wait before retry
return Nonefrom pypresence import Presence, PyPresenceException
import time
import logging
def robust_presence_client(client_id):
RPC = None
try:
# Initialize client
RPC = Presence(client_id, connection_timeout=10, response_timeout=5)
RPC.connect()
while True:
try:
# Update presence
RPC.update(
state="Online",
details="Available for chat",
large_image="status_online"
)
time.sleep(15)
except PipeClosed:
logging.warning("Discord connection lost. Attempting to reconnect...")
try:
RPC.connect()
logging.info("Reconnected successfully.")
except PyPresenceException as e:
logging.error(f"Reconnection failed: {e}")
break
except ResponseTimeout:
logging.warning("Discord not responding. Continuing...")
continue
except Exception as e:
logging.error(f"Unexpected error: {e}")
break
except DiscordNotFound:
logging.error("Discord is not running. Please start Discord and try again.")
except InvalidID:
logging.error("Invalid client ID. Check your Discord application configuration.")
except ConnectionTimeout:
logging.error("Connection timeout. Discord may be unresponsive.")
except PyPresenceException as e:
logging.error(f"PyPresence error: {e}")
except Exception as e:
logging.error(f"Unexpected error: {e}")
finally:
if RPC:
try:
RPC.close()
except:
pass # Ignore errors during cleanup
# Usage
robust_presence_client("your_client_id")from pypresence import Client, ArgumentError, EventNotFound
def safe_event_handler(data):
"""Event handler that won't crash the application."""
try:
print(f"Received event data: {data}")
# Process event data here
except Exception as e:
print(f"Error in event handler: {e}")
def setup_events(client):
try:
# Register event with proper signature
client.register_event("ACTIVITY_JOIN", safe_event_handler)
# This would raise ArgumentError - wrong number of parameters
# client.register_event("ACTIVITY_JOIN", lambda: print("Invalid"))
except ArgumentError:
print("Event handler must accept exactly one parameter.")
except Exception as e:
print(f"Failed to register event: {e}")
def cleanup_events(client):
try:
client.unregister_event("ACTIVITY_JOIN")
except EventNotFound:
print("Event was not registered.")
except Exception as e:
print(f"Error unregistering event: {e}")import asyncio
from pypresence import AioPresence, PyPresenceException
async def async_presence_with_error_handling(client_id):
RPC = None
try:
RPC = AioPresence(client_id)
await RPC.connect()
# Set initial presence
await RPC.update(
state="Starting up",
details="Initializing application"
)
# Simulate long-running application
for i in range(100):
try:
await RPC.update(
state=f"Running - Step {i+1}",
details="Processing data"
)
await asyncio.sleep(10)
except PyPresenceException as e:
print(f"Presence update failed: {e}")
# Continue running even if presence update fails
continue
except PyPresenceException as e:
print(f"Failed to initialize Discord presence: {e}")
finally:
if RPC:
RPC.close()
# Run with error handling
asyncio.run(async_presence_with_error_handling("your_client_id"))from pypresence import Presence, PyPresenceException
class PresenceManager:
def __init__(self, client_id, auto_retry=True):
self.client_id = client_id
self.auto_retry = auto_retry
self.RPC = None
self._connected = False
def connect(self):
"""Connect with custom error handling."""
try:
self.RPC = Presence(self.client_id)
self.RPC.connect()
self._connected = True
return True
except PyPresenceException as e:
self._handle_error(e)
return False
def update_safe(self, **kwargs):
"""Update presence with automatic error recovery."""
if not self._connected and self.auto_retry:
if not self.connect():
return False
try:
self.RPC.update(**kwargs)
return True
except PyPresenceException as e:
self._handle_error(e)
if self.auto_retry and isinstance(e, (PipeClosed, ResponseTimeout)):
self._connected = False
return self.update_safe(**kwargs) # Retry once
return False
def _handle_error(self, error):
"""Custom error handling logic."""
error_actions = {
DiscordNotFound: "Please start Discord and try again.",
InvalidID: "Check your Discord application client ID.",
PipeClosed: "Discord connection lost. Will attempt to reconnect.",
ResponseTimeout: "Discord is not responding. Retrying...",
}
action = error_actions.get(type(error), f"Unknown error: {error}")
print(f"Discord error: {action}")
def close(self):
"""Clean shutdown."""
if self.RPC:
self.RPC.close()
self._connected = False
# Usage
manager = PresenceManager("your_client_id", auto_retry=True)
if manager.connect():
manager.update_safe(state="Connected", details="Ready to go!")Install with Tessl CLI
npx tessl i tessl/pypi-pypresence