2 releases
| 0.1.0 | Oct 24, 2025 |
|---|---|
| 0.1.0-dev | Oct 19, 2025 |
#82 in Games
185KB
3.5K
SLoC
PresenceForge
A Rust library for Discord Rich Presence that actually works without the headaches. No more fighting with the Discord SDK or dealing with complex C bindings.
Note: This is currently in development (v0.1.0). Things might break. This is a learning/hobby project. Features and APIs may change in future versions.
Documentation
- Getting Started Guide - Installation and first steps
- API Reference - Complete API documentation
- Activity Builder Reference - Detailed guide to all fields
- Async Runtimes Guide - Using async/await with Tokio, async-std, or smol
- Error Handling Guide - Proper error handling patterns
- FAQ & Troubleshooting - Common questions and solutions
Want to build your own RPC client?
- Discord RPC from Scratch - this is What I found while building this library. I could be wrong about some things, so feel free to correct me!
What Works
- Linux and macOS (Unix domain sockets)
- Windows support (named pipes)
- Flatpak Discord support (automatic detection)
- Basic Rich Presence activities
- Activity builder pattern
- Images, buttons, and timestamps
- Async support with runtime-agnostic design
- Support for tokio, async-std, and smol
- Flexible pipe/socket selection
Quick Start
Add PresenceForge to your Cargo.toml:
[dependencies]
presenceforge = "0.1.0"
For async support, add one of the runtime features:
[dependencies]
presenceforge = { version = "0.1.0", features = ["tokio-runtime"] }
# OR
presenceforge = { version = "0.1.0", features = ["async-std-runtime"] }
# OR
presenceforge = { version = "0.1.0", features = ["smol-runtime"] }
Basic Usage (Synchronous)
use presenceforge::ActivityBuilder;
use presenceforge::sync::DiscordIpcClient;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = DiscordIpcClient::new("your_client_id")?;
client.connect()?;
let activity = ActivityBuilder::new()
.state("Playing a game")
.details("In the menu")
.start_timestamp_now()
.large_image("game_logo")
.large_text("My Awesome Game")
.build();
client.set_activity(&activity)?;
// Keep the activity active
std::thread::sleep(std::time::Duration::from_secs(10));
client.clear_activity()?;
Ok(())
}
Async Usage with Tokio
use presenceforge::{AsyncDiscordIpcClient, ActivityBuilder, Result};
#[tokio::main]
async fn main() -> Result {
let mut client = AsyncDiscordIpcClient::new("your_client_id").await?;
client.connect().await?;
let activity = ActivityBuilder::new()
.state("Playing a game")
.details("In the menu")
.start_timestamp_now()
.large_image("game_logo")
.large_text("My Awesome Game")
.build();
client.set_activity(&activity).await?;
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
client.clear_activity().await?;
Ok(())
}
Async Usage with async-std
use presenceforge::{AsyncDiscordIpcClient, ActivityBuilder, Result};
use async_std::task;
use std::time::Duration;
#[async_std::main]
async fn main() -> Result {
let mut client = AsyncDiscordIpcClient::new("your_client_id").await?;
client.connect().await?;
let activity = ActivityBuilder::new()
.state("Playing a game")
.details("In the menu")
.start_timestamp_now()
.large_image("game_logo")
.large_text("My Awesome Game")
.build();
client.set_activity(&activity).await?;
task::sleep(Duration::from_secs(10)).await;
client.clear_activity().await?;
Ok(())
}
Async Usage with smol
use presenceforge::{AsyncDiscordIpcClient, ActivityBuilder, Result};
use std::time::Duration;
fn main() -> Result {
smol::block_on(async {
let mut client = AsyncDiscordIpcClient::new("your_client_id").await?;
client.connect().await?;
let activity = ActivityBuilder::new()
.state("Playing a game")
.details("In the menu")
.start_timestamp_now()
.large_image("game_logo")
.large_text("My Awesome Game")
.build();
client.set_activity(&activity).await?;
smol::Timer::after(Duration::from_secs(10)).await;
client.clear_activity().await?;
Ok(())
})
}
Getting Your Discord Application ID
- Go to the Discord Developer Portal
- Create a new application or select an existing one
- Copy the Application ID from the General Information page
- (Optional) Upload images in the Rich Presence Art Assets section
Configuration Methods
All examples support three ways to provide your Discord Client ID:
1. Command-line Argument (Recommended for testing)
cargo run --example basic -- --client-id YOUR_CLIENT_ID
2. Environment Variable
DISCORD_CLIENT_ID=YOUR_CLIENT_ID cargo run --example basic
3. .env File (Recommended for development)
# Copy the example file
cp .env.example .env
# Edit .env and add your client ID
# DISCORD_CLIENT_ID=your_client_id_here
# Then run any example
cargo run --example basic
Priority Order: Command-line argument → Environment variable → .env file
Platform Support
| Platform | IPC Method | Status |
|---|---|---|
| Linux | Unix Domain Sockets | [x] |
| macOS | Unix Domain Sockets | [x] |
| Windows | Named Pipes | [x] |
API Reference
Client
DiscordIpcClient::new(client_id)- Create a new clientclient.connect()- Connect to Discordclient.set_activity(activity)- Set Rich Presence activityclient.clear_activity()- Clear current activity
Activity Builder
The ActivityBuilder provides a fluent interface for creating activities:
ActivityBuilder::new()
.state("Custom state") // What the player is doing
.details("Custom details") // Additional context
.start_timestamp_now() // Start time (current)
.start_timestamp(timestamp) // Start time (custom)
.end_timestamp(timestamp) // End time
.large_image("image_key") // Large image asset
.large_text("Hover text") // Large image hover text
.small_image("image_key") // Small image asset
.small_text("Hover text") // Small image hover text
.button("Label", "https://url") // Clickable button (max 2)
.party("id",1, 4) // Party size (current, max)
.build()
Running Examples
For detailed information about all available examples and configuration options, see the Examples README.
Clone the repository and run the included examples:
git clone https://github.com/Sreehari425/presenceforge.git
cd presenceforge
# Basic example (synchronous)
cargo run --example basic -- --client-id YOUR_CLIENT_ID
# Game demo with dynamic status
cargo run --example game_demo -- --client-id YOUR_CLIENT_ID
# Developer coding status
cargo run --example coding_status -- --client-id YOUR_CLIENT_ID
# Custom activity without builder
cargo run --example custom_activity -- --client-id YOUR_CLIENT_ID
# Async example with Tokio
cargo run --example async_tokio --features tokio-runtime -- --client-id YOUR_CLIENT_ID
# Async example with async-std
cargo run --example async_std --features async-std-runtime -- --client-id YOUR_CLIENT_ID
# Async example with smol
cargo run --example async_smol --features smol-runtime -- --client-id YOUR_CLIENT_ID
# Complete builder reference - Shows ALL ActivityBuilder options
cargo run --example builder_all -- --client-id YOUR_CLIENT_ID
# Connection retry and error handling
cargo run --example connection_retry -- --client-id YOUR_CLIENT_ID
# Pipe selection and discovery
cargo run --example pipe_selection -- --client-id YOUR_CLIENT_ID
Or use the .env file method (recommended for development):
# Set up .env file once
cp .env.example .env
# Edit .env and add: DISCORD_CLIENT_ID=your_client_id_here
# Then run examples without specifying client ID
cargo run --example basic
cargo run --example game_demo
cargo run --example async_tokio --features tokio-runtime
Note: Replace YOUR_CLIENT_ID with your actual Discord application ID, or use the .env file method for convenience.
Error Handling
PresenceForge uses the Result type for error handling:
use presenceforge::DiscordIpcError;
use presenceforge::sync::DiscordIpcClient;
match client.connect() {
Ok(_) => println!("Connected successfully!"),
Err(DiscordIpcError::ConnectionFailed) => {
eprintln!("Failed to connect - is Discord running?");
}
Err(e) => eprintln!("Error: {}", e),
}
TODO
- Better error messages
- Party/lobby functionality (partial implementation)
- Async support (tokio, async-std, and smol)
- More comprehensive examples
- Publish to crates.io
- CI/CD pipeline
- Proper documentation
- Connection retry logic with exponential backoff
- Activity validation
License
This project is licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Dependencies
~1–15MB
~162K SLoC