### Complete ActivityBuilder reference example Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html Connects to Discord and builds an activity with all available fields, starting with basic text fields. Requires DISCORD_CLIENT_ID via CLI arg, environment variable, or .env file. Prints setup instructions if the ID is missing. ```rust fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args .client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example builder_all -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example builder_all"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); println!("=== Complete ActivityBuilder Reference Example ===\n"); let mut client = DiscordIpcClient::new(client_id)?; // Perform handshake println!("Connecting to Discord..."); client.connect()?; println!("✓ Connected!\n"); // Create an activity with ALL possible fields println!("Setting activity with all available options...\n"); let mut builder = ActivityBuilder::new() // ═══════════════════════════════════════════════════════════ // BASIC TEXT FIELDS // ═══════════════════════════════════════════════════════════ // State: First line of text (smaller text) // Example: "In a Match" or "Editing main.rs" .state("Playing a custom game") // Details: Second line of text (larger text above state) // Example: "Competitive - Rank 50" or "Workspace: my-project" .details("Custom game mode with friends") // ═══════════════════════════════════════════════════════════ ``` -------------------------------- ### Unified API example: AsyncDiscordIpcClient with ActivityBuilder Source: https://docs.rs/presenceforge/latest/presenceforge/async_io/index.html Shows how to set up a presence using the unified AsyncDiscordIpcClient, which works with any runtime (Tokio, async-std, smol) by enabling the corresponding feature flag. The example creates a client, connects, builds an activity with state, details, and start timestamp, then sets it. ```rust // Same code works with any runtime! use presenceforge::{AsyncDiscordIpcClient, ActivityBuilder, Result}; async fn setup_presence() -> 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()? .build(); client.set_activity(&activity).await?; Ok(()) } ``` -------------------------------- ### Partial builder_all.rs example (examples/builder_all.rs) Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html This is a partial example from `examples/builder_all.rs` that shows the beginning of a `main` function. It loads environment variables and parses command-line arguments, but the code is truncated in the source and does not include the full activity building logic. ```rust fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args .client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) ``` -------------------------------- ### Build activity with retry (examples/connection_retry.rs) Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html Example from examples/connection_retry.rs (line 90) showing how to build an activity after connecting with automatic retry. Uses RetryConfig::with_max_attempts(3) and with_retry to create a DiscordIpcClient, then builds an activity with state, details, and start timestamp. ```rust fn example_basic_retry(client_id: &str) -> Result { println!("Attempting to connect with automatic retry (3 attempts)..."); // Use the retry utility to automatically retry connection let config = RetryConfig::with_max_attempts(3); let mut client = with_retry(&config, || { println!(" Connecting..."); DiscordIpcClient::new(client_id) })?; println!("✓ Connected successfully!"); // Perform handshake client.connect()?; println!("✓ Handshake completed!"); // Set activity let activity = ActivityBuilder::new() .state("Example 1: Auto Retry") .details("Testing connection retry") .start_timestamp_now()? .build(); client.set_activity(&activity)?; println!("✓ Activity set!"); std::thread::sleep(Duration::from_secs(2)); } ``` -------------------------------- ### Async Tokio Reconnect Example - Full Source Source: https://docs.rs/presenceforge/latest/src/async_tokio_reconnect/async_tokio_reconnect.rs.html This is the complete source code for the async_tokio_reconnect example. It includes three examples: basic activity setting, activity with timestamps, and a resilient loop with reconnection. The main function runs the example when the 'tokio-runtime' feature is enabled, otherwise it prints an error message. ```rust 129 .start_timestamp_now()? 130 .build(); 131 132 client.set_activity(&activity).await?; 133 println!("✓ Activity set!"); 134 135 sleep(Duration::from_secs(2)).await; 136 client.clear_activity().await?; 137 println!("✓ Activity cleared!"); 138 139 Ok(()) 140 } 141 142 /// Example 3: Resilient presence maintenance loop with automatic reconnection 143 async fn example_resilient_loop(client_id: &str) -> Result { 144 println!("Starting resilient presence maintenance loop..."); 145 146 // Connect with retry 147 let config = RetryConfig::with_max_attempts(3); 148 let mut client = 149 with_retry_async(&config, || Box::pin(AsyncDiscordIpcClient::new(client_id))).await?; 150 151 client.connect().await?; 152 println!("✓ Connected!"); 153 154 let activity = ActivityBuilder::new() 155 .state("Example 3: Resilient Loop") 156 .details("Auto-reconnecting") 157 .start_timestamp_now()? 158 .build(); 159 160 println!("Maintaining presence (will update 3 times)..."); 161 162 // Maintain presence with automatic reconnection 163 for i in 1..=3 { 164 match client.set_activity(&activity).await { 165 Ok(_) => { 166 println!(" ✓ Update #{} successful", i); 167 } 168 Err(e) if e.is_connection_error() => { 169 println!(" ⚠ Connection lost on update #{}, reconnecting...", i); 170 171 // Try to reconnect 172 match client.reconnect().await { 173 Ok(_) => { 174 println!(" ✓ Reconnected successfully!"); 175 // Retry the update 176 client.set_activity(&activity).await?; 177 println!(" ✓ Update #{} successful after reconnection", i); 178 } 179 Err(reconnect_err) => { 180 println!(" ✗ Reconnection failed: {}", reconnect_err); 181 println!(" 💡 Discord may have been closed"); 182 return Err(reconnect_err); 183 } 184 } 185 } 186 Err(e) => { 187 println!(" ✗ Unexpected error: {}", e); 188 return Err(e); 189 } 190 } 191 192 if i < 3 { 193 sleep(Duration::from_secs(2)).await; 194 } 195 } 196 197 client.clear_activity().await?; 198 println!("✓ Activity cleared!"); 199 println!("✓ Resilient loop completed successfully!"); 200 201 Ok(()) 202 } 203} 204 #[cfg(feature = "tokio-runtime")] #[tokio::main] async fn main() -> std::result::Result<(), Box> { tokio_example::run().await } #[cfg(not(feature = "tokio-runtime"))] fn main() { eprintln!("This example requires the `tokio-runtime` feature."); eprintln!("Run with: cargo run --example async_tokio_reconnect --features tokio-runtime"); } ``` -------------------------------- ### Complete ActivityBuilder Reference Example (builder_all.rs) Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html Example from examples/builder_all.rs that loads environment variables, parses CLI arguments, connects to Discord, and builds an activity with all available fields. Requires DISCORD_CLIENT_ID via command line, environment variable, or .env file. ```rust fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args .client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example builder_all -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example builder_all"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); println!("=== Complete ActivityBuilder Reference Example ===\n"); let mut client = DiscordIpcClient::new(client_id)?; // Perform handshake println!("Connecting to Discord..."); client.connect()?; println!("✓ Connected!\n"); // Create an activity with ALL possible fields println!("Setting activity with all available options...\n"); let mut builder = ActivityBuilder::new() ``` -------------------------------- ### Example: Connection retry and manual reconnection (examples/connection_retry.rs) Source: https://docs.rs/presenceforge/latest/presenceforge/sync/client/struct.DiscordIpcClient.html This example demonstrates three scenarios: automatic retry with RetryConfig, manual reconnection on connection errors, and custom retry with exponential backoff. It uses DiscordIpcClient::new, connect, set_activity, clear_activity, and reconnect methods. The example shows how to handle connection errors and retry logic. ```rust fn example_basic_retry(client_id: &str) -> Result { println!("Attempting to connect with automatic retry (3 attempts)..."); // Use the retry utility to automatically retry connection let config = RetryConfig::with_max_attempts(3); let mut client = with_retry(&config, || { println!(" Connecting..."); DiscordIpcClient::new(client_id) })?; println!("✓ Connected successfully!"); // Perform handshake client.connect()?; println!("✓ Handshake completed!"); // Set activity let activity = ActivityBuilder::new() .state("Example 1: Auto Retry") .details("Testing connection retry") .start_timestamp_now()? .build(); client.set_activity(&activity)?; println!("✓ Activity set!"); std::thread::sleep(Duration::from_secs(2)); client.clear_activity()?; Ok(()) } /// Example 2: Manual reconnection when connection is lost fn example_manual_reconnect(client_id: &str) -> Result { println!("Connecting to Discord..."); let mut client = DiscordIpcClient::new(client_id)?; client.connect()?; println!("✓ Connected!"); let activity = ActivityBuilder::new() .state("Example 2: Manual Reconnect") .details("Testing reconnection") .start_timestamp_now()? .build(); println!("Setting activity..."); // Try to set activity, and reconnect if connection fails match client.set_activity(&activity) { Ok(_) => println!("✓ Activity set!"), Err(e) if e.is_connection_error() => { println!("⚠ Connection error detected: {}", e); println!(" Attempting to reconnect..."); // Reconnect and retry client.reconnect()?; println!("✓ Reconnected!"); client.set_activity(&activity)?; println!("✓ Activity set after reconnection!"); } Err(e) => return Err(e), } std::thread::sleep(Duration::from_secs(2)); client.clear_activity()?; Ok(()) } /// Example 3: Custom retry configuration with exponential backoff fn example_custom_retry(client_id: &str) -> Result { println!("Connecting with custom retry configuration:"); println!(" - Max attempts: 5"); println!(" - Initial delay: 500ms"); println!(" - Max delay: 8000ms"); println!(" - Backoff multiplier: 2.0"); ``` -------------------------------- ### DiscordIpcClient Example Source: https://docs.rs/presenceforge/latest/presenceforge/sync/client/struct.DiscordIpcClient.html Example usage of DiscordIpcClient: create a client, connect, build an activity, set it, and handle connection errors by reconnecting. ```APIDOC ## DiscordIpcClient ### Description DiscordIpcClient is a struct that provides methods to interact with Discord's IPC. It allows creating a client, connecting, setting activity, and reconnecting when the connection is lost. ### Methods #### new(client_id: &str) -> Result - **Description**: Creates a new DiscordIpcClient instance with the given client ID. - **Parameters**: - `client_id` (string) - Required - The Discord application client ID. - **Returns**: Result containing the client or an error. #### connect(&mut self) -> Result<()> - **Description**: Establishes a connection to Discord IPC. - **Returns**: Result indicating success or failure. #### set_activity(&mut self, activity: &Activity) -> Result<()> - **Description**: Sets the user's activity on Discord. - **Parameters**: - `activity` (Activity) - Required - The activity to set, built using ActivityBuilder. - **Returns**: Result indicating success or failure. #### reconnect(&mut self) -> Result<()> - **Description**: Reconnects the client to Discord IPC after a connection loss. - **Returns**: Result indicating success or failure. ### Example ```rust use presenceforge::sync::DiscordIpcClient; use presenceforge::ActivityBuilder; let mut client = DiscordIpcClient::new("client_id")?; client.connect()?; let activity = ActivityBuilder::new().state("Testing").build(); // Later, if connection is lost if let Err(e) = client.set_activity(&activity) { if e.is_connection_error() { println!("Connection lost, reconnecting..."); client.reconnect()?; client.set_activity(&activity)?; } } ``` ``` -------------------------------- ### Complete ActivityBuilder Reference Example (builder_all.rs) Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html Full example from examples/builder_all.rs that loads environment variables, parses command-line arguments, connects to Discord, sets an activity with all available fields, and clears it. Includes visual layout reference and tips. Requires DISCORD_CLIENT_ID via command line, environment variable, or .env file. ```rust fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args .client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example builder_all -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example builder_all"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); println!("=== Complete ActivityBuilder Reference Example ===\n"); let mut client = DiscordIpcClient::new(client_id)?; // Perform handshake println!("Connecting to Discord..."); client.connect()?; println!("✓ Connected!\n"); // Create an activity with ALL possible fields println!("Setting activity with all available options...\n"); let mut builder = ActivityBuilder::new() // ═══════════════════════════════════════════════════════════ // BASIC TEXT FIELDS // ═══════════════════════════════════════════════════════════ // State: First line of text (smaller text) // Example: "In a Match" or "Editing main.rs" .state("Playing a custom game") // Details: Second line of text (larger text above state) // Example: "Competitive - Rank 50" or "Workspace: my-project" .details("Custom game mode with friends") // ═══════════════════════════════════════════════════════════ // TIMESTAMPS (Shows elapsed/remaining time) // ═══════════════════════════════════════════════════════════ // Start timestamp: Shows "elapsed" time (e.g., "00:15 elapsed") // Use .start_timestamp_now() for current time // End timestamp: Shows "remaining" time (e.g., "02:30 left") // Note: If you set both start and end, Discord shows remaining time // Uncomment to try: ``` -------------------------------- ### Async Tokio Reconnect Example (Partial) Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html Partial example showing manual reconnection with AsyncDiscordIpcClient in an async context. Uses await on connect and activity setting. ```rust async fn example_manual_reconnect(client_id: &str) -> Result { println!("Connecting to Discord..."); let mut client = AsyncDiscordIpcClient::new(client_id).await?; client.connect().await?; println!("✓ Connected!"); ``` -------------------------------- ### Rust example: Using start_timestamp to maintain a consistent timer across activity updates Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html This example from `examples/update_activity_tokio.rs` shows how to use `start_timestamp` to keep the same start timestamp across multiple activity updates, so the elapsed time on Discord continues without resetting. It uses `AsyncDiscordIpcClient` and `ActivityBuilder` to set activities with the same timestamp. The example includes error handling for missing `DISCORD_CLIENT_ID` and demonstrates three state updates with a 5-second sleep between each. ```rust async fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args .client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example update_activity_tokio --features tokio-runtime -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example update_activity_tokio --features tokio-runtime"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); let mut client = AsyncDiscordIpcClient::new(&client_id).await?; // Perform handshake client.connect().await?; println!("✓ Connected to Discord!"); // Get the initial timestamp - this will stay consistent across all updates let start_timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); println!("\n=== Demonstrating Activity Updates Without Timer Reset ===\n"); println!("The timer will remain consistent throughout all state changes!\n"); // Initial activity - Main Menu println!(" State 1: Main Menu"); let activity = ActivityBuilder::new() .state("Browsing menus") .details("In main menu") .start_timestamp(start_timestamp) .large_image("game") .large_text("Game Icon") .small_image("idle") .small_text("Idle") .button(" Play Game", "https://example.com") .build(); client.set_activity(&activity).await?; println!("✓ Activity set: In main menu"); println!(" Timer started at: {}", start_timestamp); // Wait 5 seconds println!("\n Waiting 5 seconds...\n"); tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; // Update to In Game - SAME TIMESTAMP println!(" State 2: In Game"); let activity = ActivityBuilder::new() .state("Playing Solo") .details("In game - Level 5") .start_timestamp(start_timestamp) // SAME timestamp = timer continues! .large_image("game") .large_text("Game Icon") .small_image("playing") .small_text("Playing") .button("🎮 Play Game", "https://example.com") .build(); client.set_activity(&activity).await?; println!("✓ Activity updated: In game"); println!(" Timer continues from: {}", start_timestamp); println!(" Notice: The elapsed time on Discord continues!"); // Wait 5 seconds println!("\n Waiting 5 seconds...\n"); tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; // Update to Multiplayer - SAME TIMESTAMP println!(" State 3: Multiplayer"); let activity = ActivityBuilder::new() .state("In Multiplayer Match") .details("Team Deathmatch - 2/8 players") .start_timestamp(start_timestamp) // SAME timestamp = timer continues! .large_image("game") .large_text("Game Icon") .small_image("multiplayer") .small_text("Online") .button(" Join Game", "https://example.com") .build(); client.set_activity(&activity).await?; println!("✓ Activity updated: Multiplayer"); println!(" Timer continues from: {}", start_timestamp); println!(" Notice: The elapsed time keeps going!"); // Wait 5 seconds println!("\n Waiting 5 seconds...\n"); tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; // Update to Loading - SAME TIMESTAMP } ``` -------------------------------- ### Complete ActivityBuilder Reference Example in Rust Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html This snippet is the full example from the source, showing how to construct an activity with all available options. It requires the DISCORD_CLIENT_ID to be provided via command line, environment variable, or .env file, and exits if missing. The example connects to Discord, builds an activity with state, details, timestamps, images, party, and instance, and conditionally adds buttons when the 'secrets' feature is not enabled. ```rust .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example builder_all -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example builder_all"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); println!("=== Complete ActivityBuilder Reference Example ===\n"); let mut client = DiscordIpcClient::new(client_id)?; // Perform handshake println!("Connecting to Discord..."); client.connect()?; println!("✓ Connected!\n"); // Create an activity with ALL possible fields println!("Setting activity with all available options...\n"); let mut builder = ActivityBuilder::new() // ═══════════════════════════════════════════════════════════ // BASIC TEXT FIELDS // ═══════════════════════════════════════════════════════════ // State: First line of text (smaller text) // Example: "In a Match" or "Editing main.rs" .state("Playing a custom game") // Details: Second line of text (larger text above state) // Example: "Competitive - Rank 50" or "Workspace: my-project" .details("Custom game mode with friends") // ═══════════════════════════════════════════════════════════ // TIMESTAMPS (Shows elapsed/remaining time) // ═══════════════════════════════════════════════════════════ // Start timestamp: Shows "elapsed" time (e.g., "00:15 elapsed") // Use .start_timestamp_now() for current time // End timestamp: Shows "remaining" time (e.g., "02:30 left") // Note: If you set both start and end, Discord shows remaining time // Uncomment to try: // .end_timestamp(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 300) // Or use the convenience method: .end_timestamp_from_now(Duration::from_secs(300))? // ═══════════════════════════════════════════════════════════ // IMAGES (Large and Small) // ═══════════════════════════════════════════════════════════ // Large Image: Main big image shown on the left // This should be an asset key uploaded to your Discord app // Go to: Discord Developer Portal > Your App > Rich Presence > Art Assets .large_image("car") // Large Image Text: Tooltip shown when hovering over large image .large_text("This is the large image - shows on hover!") // Small Image: Smaller circular image shown in bottom-right of large image // Also needs to be uploaded as an asset .small_image("rust_logo") // Small Image Text: Tooltip shown when hovering over small image .small_text("Built with Rust 🦀") // ═══════════════════════════════════════════════════════════ // PARTY (Multiplayer/Group information) // ═══════════════════════════════════════════════════════════ // Party: Shows "X of Y" (e.g., "2 of 4" for a party) // Useful for multiplayer games showing current players // Parameters: party_id, current_size, max_size // .party("party-12345", 2, 4) // Or use the simple party method which generates a unique ID: .party_simple(2, 4) // NOTE : BUTTONS and SECRETS are mutually exclusive. We add buttons // only when the `secrets` feature is NOT enabled so the example // remains valid in both configurations. // ═══════════════════════════════════════════════════════════ // SECRETS (For "Ask to Join" and spectate features) // ═══════════════════════════════════════════════════════════ // NOTE: Secret methods require the 'secrets' feature flag. // To enable, run the example with `--features secrets`. // The secret methods are only compiled when the feature is enabled, // so we call them inside a cfg block below. // ═══════════════════════════════════════════════════════════ // INSTANCE (Boolean flag) // ═══════════════════════════════════════════════════════════ // Instance: Whether this is an instanced context (like a match) // Set to true for unique game instances, false for general activities .instance(true); // Apply the start timestamp (this can fail if system time is invalid) builder = builder.start_timestamp_now()?; // Conditionally add buttons when the secrets feature is NOT enabled #[cfg(not(feature = "secrets"))] { builder = builder .button("View Game", "https://example.com/game") ``` -------------------------------- ### builder_all.rs: Complete ActivityBuilder Reference Example Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html This example shows how to set up a Discord IPC client, load environment variables, and build an activity with all available options. It includes error handling for missing DISCORD_CLIENT_ID and demonstrates the use of ActivityBuilder methods like state, details, and end_timestamp_from_now. ```rust fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args .client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example builder_all -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example builder_all"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); println!("=== Complete ActivityBuilder Reference Example ===\n"); let mut client = DiscordIpcClient::new(client_id)?; // Perform handshake println!("Connecting to Discord..."); client.connect()?; println!("✓ Connected!\n"); // Create an activity with ALL possible fields println!("Setting activity with all available options...\n"); let mut builder = ActivityBuilder::new() // ═══════════════════════════════════════════════════════════ // BASIC TEXT FIELDS // ═══════════════════════════════════════════════════════════ // State: First line of text (smaller text) // Example: "In a Match" or "Editing main.rs" .state("Playing a custom game") // Details: Second line of text (larger text above state) // Example: "Competitive - Rank 50" or "Workspace: my-project" .details("Custom game mode with friends") // ═══════════════════════════════════════════════════════════ // TIMESTAMPS (Shows elapsed/remaining time) // ═══════════════════════════════════════════════════════════ // Start timestamp: Shows "elapsed" time (e.g., "00:15 elapsed") // Use .start_timestamp_now() for current time // End timestamp: Shows "remaining" time (e.g., "02:30 left") // Note: If you set both start and end, Discord shows remaining time // Uncomment to try: // .end_timestamp(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 300) // Or use the convenience method: .end_timestamp_from_now(Duration::from_secs(300))? // ═══════════════════════════════════════════════════════════ // IMAGES (Large and Small) // ═══════════════════════════════════════════════════════════ // Large Image: Main big image shown on the left // This should be an asset key uploaded to your Discord app ``` -------------------------------- ### basic.rs - Discord Rich Presence Basic Example Source: https://docs.rs/presenceforge/latest/src/basic/basic.rs.html This example demonstrates how to use the presenceforge crate to set up a Discord Rich Presence client. It requires a Discord Application Client ID, which can be provided via command-line argument, environment variable, or .env file. The example connects to Discord, sets an activity with a state, details, timestamp, large image, and buttons, then clears it after 30 seconds. ```rust // SPDX-License-Identifier: MIT OR Apache-2.0 // Copyright (c) 2025-2026 Sreehari Anil and project contributors use clap::Parser; use presenceforge::sync::DiscordIpcClient; use presenceforge::{ActivityBuilder, Result}; use std::time::Duration; /// Discord Rich Presence Basic Example #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { /// Discord Application Client ID #[arg(short, long)] client_id: Option, } fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args .client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example basic -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example basic"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); let mut client = DiscordIpcClient::new(client_id)?; // Perform handshake client.connect()?; // Create activity using the builder pattern let activity = ActivityBuilder::new() .state("Playing a game") .details("In the menu") .start_timestamp_now()? .large_image("car") .large_text("This is a large image") .button(" View Car", "https://google.com") // anylink you can put i ran out of ideas .button(" Documentation", "https://docs.rs/presenceforge") .build(); // Set the activity client.set_activity(&activity)?; // Keep activity for some time std::thread::sleep(Duration::from_secs(30)); // Clear the activity client.clear_activity()?; // Connection is automatically closed when client is dropped Ok(()) } ``` -------------------------------- ### ActivityBuilder Example Source: https://docs.rs/presenceforge/latest/presenceforge/activity/builder/struct.ActivityBuilder.html A comprehensive example demonstrating how to use ActivityBuilder with various methods to set activity fields. ```APIDOC ## ActivityBuilder Example ### Description Demonstrates creating an activity with all available options using the ActivityBuilder. ### Example ```rust let mut builder = ActivityBuilder::new() .state("Playing a custom game") .details("Custom game mode with friends") .end_timestamp_from_now(Duration::from_secs(300))?; ``` ``` -------------------------------- ### connection_retry.rs - Full example with retry patterns Source: https://docs.rs/presenceforge/latest/src/connection_retry/connection_retry.rs.html Complete Rust example for the presenceforge library. It parses a client ID from command line, environment variable, or .env file, then runs three examples: basic retry with RetryConfig::with_max_attempts(3), manual reconnection using is_connection_error() and reconnect(), and custom retry configuration with exponential backoff. Requires DISCORD_CLIENT_ID or --client-id argument. ```rust 1// SPDX-License-Identifier: MIT OR Apache-2.0 2// Copyright (c) 2025-2026 Sreehari Anil and project contributors 3 4use clap::Parser; 5use presenceforge::retry::{with_retry, RetryConfig}; 6use presenceforge::sync::DiscordIpcClient; 7use presenceforge::{ActivityBuilder, Result}; 8use std::time::Duration; 9 10/// Discord Rich Presence Connection Retry Example 11/// 12/// This example demonstrates various retry patterns for handling connection issues 13#[derive(Parser, Debug)] 14#[command(author, version, about, long_about = None)] 15struct Args { 16 /// Discord Application Client ID 17 #[arg(short, long)] 18 client_id: Option, 19} 20 21fn main() -> std::result::Result<(), Box> { 22 // Load .env file if it exists (optional) 23 let _ = dotenvy::dotenv(); 24 25 let args = Args::parse(); 26 27 let client_id = args 28 .client_id 29 .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) 30 .unwrap_or_else(|| { 31 eprintln!("Error: DISCORD_CLIENT_ID is required!"); 32 eprintln!("Provide it via:"); 33 eprintln!( 34 " - Command line: cargo run --example connection_retry -- --client-id YOUR_ID" 35 ); 36 eprintln!( 37 " - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example connection_retry" 38 ); 39 eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); 40 std::process::exit(1); 41 }); 42 43 println!("=== Discord Rich Presence Connection Retry Examples ===\n"); 44 45 // Example 1: Basic connection with retry 46 println!("Example 1: Connect with automatic retry"); 47 println!("{}", "-".repeat(50)); 48 example_basic_retry(&client_id)?; 49 50 println!("\n"); 51 52 // Example 2: Manual reconnection on error 53 println!("Example 2: Manual reconnection on connection loss"); 54 println!("{}", "-".repeat(50)); 55 example_manual_reconnect(&client_id)?; 56 57 println!("\n"); 58 59 // Example 3: Custom retry configuration 60 println!("Example 3: Custom retry configuration with exponential backoff"); 61 println!("{}", "-".repeat(50)); 62 example_custom_retry(&client_id)?; 63 64 Ok(()) 65} 66 /// Example 1: Basic connection with automatic retry 67fn example_basic_retry(client_id: &str) -> Result { 68 println!("Attempting to connect with automatic retry (3 attempts)..."); 69 70 // Use the retry utility to automatically retry connection 71 let config = RetryConfig::with_max_attempts(3); 72 73 let mut client = with_retry(&config, || { 74 println!(" Connecting..."); 75 DiscordIpcClient::new(client_id) 76 })?; 77 78 println!("✓ Connected successfully!"); 79 80 // Perform handshake 81 client.connect()?; 82 println!("✓ Handshake completed!"); 83 84 // Set activity 85 let activity = ActivityBuilder::new() 86 .state("Example 1: Auto Retry") 87 .details("Testing connection retry") 88 .start_timestamp_now()? 89 .build(); 90 91 client.set_activity(&activity)?; 92 println!("✓ Activity set!"); 93 94 std::thread::sleep(Duration::from_secs(2)); 95 client.clear_activity()?; 96 97 Ok(()) 98} 99 /// Example 2: Manual reconnection when connection is lost 100fn example_manual_reconnect(client_id: &str) -> Result { 101 println!("Connecting to Discord..."); 102 103 let mut client = DiscordIpcClient::new(client_id)?; 104 client.connect()?; 105 println!("✓ Connected!"); 106 107 let activity = ActivityBuilder::new() 108 .state("Example 2: Manual Reconnect") 109 .details("Testing reconnection") 110 .start_timestamp_now()? 111 .build(); 112 113 println!("Setting activity..."); 114 115 // Try to set activity, and reconnect if connection fails 116 match client.set_activity(&activity) { 117 Ok(_) => println!("✓ Activity set!"), 118 Err(e) if e.is_connection_error() => { 119 println!("⚠ Connection error detected: {}", e); 120 println!(" Attempting to reconnect..."); 121 122 // Reconnect and retry 123 client.reconnect()?; 124 println!("✓ Reconnected!"); 125 126 client.set_activity(&activity)?; 127 println!("✓ Activity set after reconnection!"); 128 } 129 Err(e) => return Err(e), 130 } 131 132 std::thread::sleep(Duration::from_secs(2)); 133 client.clear_activity()?; 134 135 Ok(()) 136} 137 /// Example 3: Custom retry configuration with exponential backoff 138fn example_custom_retry(client_id: &str) -> Result { 139 println!("Connecting with custom retry configuration:"); 140 println!(" - Max attempts: 5"); 141 println!(" - Initial delay: 500ms"); 142 println!(" - Max delay: 8000ms"); 143 println!(" - Backoff multiplier: 2.0"); 144 ``` -------------------------------- ### Basic async Tokio example with activity setting and clearing Source: https://docs.rs/presenceforge/latest/presenceforge/async_io/tokio/client/struct.TokioDiscordIpcClient.html This example demonstrates how to use AsyncDiscordIpcClient with the Tokio runtime. It loads environment variables, parses command-line arguments, connects, builds an activity with buttons, sets it, sleeps for 10 seconds, and clears it. The connection is automatically closed when the client is dropped. ```rust async fn main() -> Result { // Load .env file if it exists (optional) let _ = dotenvy::dotenv(); let args = Args::parse(); let client_id = args.client_id .or_else(|| std::env::var("DISCORD_CLIENT_ID").ok()) .unwrap_or_else(|| { eprintln!("Error: DISCORD_CLIENT_ID is required!"); eprintln!("Provide it via:"); eprintln!(" - Command line: cargo run --example async_tokio --features tokio-runtime -- --client-id YOUR_ID"); eprintln!(" - Environment: DISCORD_CLIENT_ID=YOUR_ID cargo run --example async_tokio --features tokio-runtime"); eprintln!(" - .env file: Create .env from .env.example and set DISCORD_CLIENT_ID"); std::process::exit(1); }); let mut client = AsyncDiscordIpcClient::new(&client_id).await?; // Perform handshake client.connect().await?; // Create activity using the builder pattern let activity = ActivityBuilder::new() .state("Playing a game") .details("In the menu") .start_timestamp_now()? .large_image("car") .large_text("This is a large image") .button(" View Car", "https://google.com") .button(" Documentation", "https://docs.rs/presenceforge") .build(); println!( "Activity payload: {}", serde_json::to_string_pretty(&activity).unwrap() ); // Set the activity println!("Setting Discord Rich Presence activity..."); match client.set_activity(&activity).await { Ok(_) => println!("Successfully set activity!"), Err(e) => println!("Failed to set activity: {:?}", e), } // Keep activity for some time println!("Sleeping for 10 seconds to maintain presence..."); tokio::time::sleep(tokio::time::Duration::from_secs(10)).await; // Clear the activity client.clear_activity().await?; // Connection is automatically closed when client is dropped Ok(()) } ```