1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Comprehensive example demonstrating ALL ActivityBuilder options
//
// This example shows every available builder method with explanations
// of what each field does and how it appears in Discord.
use clap::Parser;
use presenceforge::sync::DiscordIpcClient;
use presenceforge::{ActivityBuilder, Result};
use std::time::Duration;
/// Discord Rich Presence Complete Builder Example
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Discord Application Client ID
#[arg(short, long)]
client_id: Option<String>,
}
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 activity = 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
.start_timestamp_now()?
// 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)
// ═══════════════════════════════════════════════════════════
// 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)
// ═══════════════════════════════════════════════════════════
// BUTTONS (Clickable buttons - max 2)
// ═══════════════════════════════════════════════════════════
// Button 1: First clickable button
// Parameters: label (button text), url (where it goes)
.button(" View Game", "https://example.com/game")
// Button 2: Second clickable button
// Note: Discord only allows up to 2 buttons
.button(" Documentation", "https://docs.rs/presenceforge")
// ═══════════════════════════════════════════════════════════
// SECRETS (For "Ask to Join" and spectate features)
// ═══════════════════════════════════════════════════════════
// NOTE: Secret methods require the 'secrets' feature flag
// Uncomment and enable the feature to use these:
//
// #[cfg(feature = "secrets")]
// .join_secret("join_secret_12345")
// #[cfg(feature = "secrets")]
// .spectate_secret("spectate_secret_67890")
// #[cfg(feature = "secrets")]
// .match_secret("match_secret_abcde")
// ═══════════════════════════════════════════════════════════
// 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)
// Build the activity
.build();
// Set the activity
// println!("{activity:?}");
client.set_activity(&activity)?;
println!("✓ Activity set successfully!");
println!("\n📱 Check your Discord profile to see the activity!");
println!(" You should see:");
println!(" • Details: 'Custom game mode with friends'");
println!(" • State: 'Playing a custom game'");
println!(" • Large image with tooltip");
println!(" • Small image (Rust logo) in corner");
println!(" • Party info: '2 of 4'");
println!(" • Two clickable buttons");
println!(" • Elapsed time counter");
// Keep activity visible for 30 seconds
println!("\nKeeping activity visible for 30 seconds...");
for i in 1..=30 {
print!("\r {} seconds remaining... ", 31 - i);
std::io::Write::flush(&mut std::io::stdout()).unwrap();
std::thread::sleep(Duration::from_secs(1));
}
println!("\r ✓ Time's up! ");
// Clear the activity
println!("\nClearing activity...");
client.clear_activity()?;
println!("✓ Activity cleared!");
println!("\n=== Example completed! ===\n");
// ═══════════════════════════════════════════════════════════
// VISUAL REFERENCE
// ═══════════════════════════════════════════════════════════
println!("📊 Visual Layout Reference:");
println!("┌─────────────────────────────────────────┐");
println!("│ [Large Image] DETAILS │");
println!("│ [Small] State │");
println!("│ Party: 2 of 4 │");
println!("│ ⏱ 00:15 elapsed │");
println!("│ │");
println!("│ [Button 1] [Button 2] │");
println!("└─────────────────────────────────────────┘");
println!();
println!("💡 Tips:");
println!(" • Large/Small images must be uploaded as assets in Discord Developer Portal");
println!(" • Asset keys are case-sensitive");
println!(" • Maximum 2 buttons allowed");
println!(" • Timestamps are Unix timestamps (seconds since epoch)");
println!(" • Party size shows as 'X of Y' in Discord");
println!(" • Secrets (join/spectate/match) require the 'secrets' feature flag");
println!(" • Hover tooltips work on large_text and small_text");
Ok(())
}