diff --git a/crates/molotov-core/src/discord.rs b/crates/molotov-core/src/discord.rs new file mode 100644 index 0000000..99099a6 --- /dev/null +++ b/crates/molotov-core/src/discord.rs @@ -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, +} + +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 { + 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() + } +} diff --git a/crates/molotov-core/src/lib.rs b/crates/molotov-core/src/lib.rs index 61ec6f1..eec42cd 100644 --- a/crates/molotov-core/src/lib.rs +++ b/crates/molotov-core/src/lib.rs @@ -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; diff --git a/crates/molotov-core/tests/discord_rpc_test.rs b/crates/molotov-core/tests/discord_rpc_test.rs new file mode 100644 index 0000000..1685a95 --- /dev/null +++ b/crates/molotov-core/tests/discord_rpc_test.rs @@ -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); +}