feat(core): implement zero-dependency Discord Rich Presence IPC client
This commit is contained in:
parent
a4d6cce7f4
commit
96593ca842
3 changed files with 89 additions and 0 deletions
65
crates/molotov-core/src/discord.rs
Normal file
65
crates/molotov-core/src/discord.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
23
crates/molotov-core/tests/discord_rpc_test.rs
Normal file
23
crates/molotov-core/tests/discord_rpc_test.rs
Normal 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);
|
||||
}
|
||||
Reference in a new issue