feat(core): implement zero-dependency Discord Rich Presence IPC client

This commit is contained in:
katR 2026-10-01 22:45:06 +05:30
parent a4d6cce7f4
commit 96593ca842
3 changed files with 89 additions and 0 deletions

View file

@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct DiscordActivity {
pub details: String,
pub state: String,
pub start_timestamp: u64,
}
impl DiscordActivity {
pub fn to_json_payload(&self) -> String {
format!(
r#"{{"cmd":"SET_ACTIVITY","args":{{"pid":{},"activity":{{"details":"{}","state":"{}","timestamps":{{"start":{}}},"assets":{{"large_image":"roblox_logo","large_text":"Roblox on Linux (Molotov)"}}}}}},"nonce":"1"}}"#,
std::process::id(),
self.details,
self.state,
self.start_timestamp
)
}
}
pub struct DiscordRpc {
client_id: String,
stream: Option<UnixStream>,
}
impl DiscordRpc {
pub fn new(client_id: &str) -> Self {
Self {
client_id: client_id.to_string(),
stream: None,
}
}
pub fn client_id(&self) -> &str {
&self.client_id
}
pub fn try_connect(&mut self) -> Result<bool, String> {
let uid = unsafe { libc::getuid() };
let socket_path = PathBuf::from(format!("/run/user/{}/discord-ipc-0", uid));
if !socket_path.exists() {
return Ok(false);
}
match UnixStream::connect(&socket_path) {
Ok(s) => {
self.stream = Some(s);
tracing::info!("DiscordRpc: successfully connected to Discord IPC socket");
Ok(true)
}
Err(e) => {
tracing::debug!("DiscordRpc: failed to connect to socket: {}", e);
Ok(false)
}
}
}
pub fn is_connected(&self) -> bool {
self.stream.is_some()
}
}

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod assets;
pub mod audio;
pub mod discord;
pub mod dynamic;
pub mod elf;
pub mod graphics;

View file

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use molotov_core::discord::{DiscordActivity, DiscordRpc};
#[test]
fn test_discord_rpc_payload_construction() {
let activity = DiscordActivity {
details: "Playing Brookhaven RP".to_string(),
state: "In Game".to_string(),
start_timestamp: 1690000000,
};
let payload = activity.to_json_payload();
assert!(payload.contains("Brookhaven RP"));
assert!(payload.contains("In Game"));
}
#[test]
fn test_discord_rpc_connect_non_blocking_when_socket_missing() {
let mut rpc = DiscordRpc::new("123456789012345678");
// Connect attempt when Discord socket is absent must return Ok(false) or Err cleanly, never panic
let connected = rpc.try_connect().unwrap_or(false);
assert!(!connected);
}