1use std::thread::{self, JoinHandle};
4
5use anyhow::{Result, bail};
6use uuid::Uuid;
7
8use crate::{
9 socket::DiscordIPCSocket,
10 utils::{get_current_timestamp_unix, pack},
11};
12
13pub struct DiscordIPC {
16 sock: DiscordIPCSocket,
17 ipc_task: Option<JoinHandle<Result<()>>>,
18 timestamp: u64,
19 client_id: String,
20}
21
22impl DiscordIPC {
23 fn send_json(&mut self, json: String, opcode: u32) -> Result<()> {
24 let bytes = json.as_bytes();
25
26 let packed = pack(opcode, bytes.len() as u32)?;
27 self.sock.write(&packed)?;
28 self.sock.write(bytes)?;
29
30 Ok(())
31 }
32
33 pub fn new_from(client_id: &str) -> Result<Self> {
36 let sock = DiscordIPCSocket::new()?;
37
38 Ok(Self {
39 sock,
40 ipc_task: None,
41 timestamp: get_current_timestamp_unix()?,
42 client_id: client_id.to_string(),
43 })
44 }
45
46 pub fn handshake(&mut self) -> Result<()> {
49 let json = format!(r#"{{"v":1,"client_id":"{}"}}"#, self.client_id);
50 self.send_json(json, 0u32)?;
51
52 Ok(())
53 }
54
55 pub fn wait_for_ready(&mut self) -> Result<()> {
57 loop {
58 let frame = self.sock.read_frame()?;
59
60 if frame.opcode == 1 && frame.body.windows(5).any(|w| w == b"READY") {
61 break;
62 }
63 }
64 Ok(())
65 }
66
67 pub fn run(&mut self) -> Result<()> {
70 if self.ipc_task.is_some() {
71 return Ok(());
72 }
73
74 self.handshake()?;
75 self.wait_for_ready()?;
76
77 let mut sock = self.sock.clone();
78 self.ipc_task = Some(thread::spawn(move || sock.handle_ipc()));
79
80 Ok(())
81 }
82
83 pub fn wait(&mut self) -> Result<()> {
85 if let Some(handle) = self.ipc_task.take() {
86 match handle.join() {
87 Ok(res) => res?,
88 Err(e) => bail!("Thread panicked: {e:?}"),
89 }
90 }
91 Ok(())
92 }
93
94 pub fn set_activity(&mut self, details: &str, state: &str) -> Result<()> {
96 let pid = std::process::id();
97 let uuid = Uuid::new_v4();
98
99 let json = format!(
100 r#"
101{{
102 "cmd":"SET_ACTIVITY",
103 "args": {{
104 "pid": {},
105 "activity": {{
106 "details":"{}",
107 "state":"{}",
108 "timestamps": {{
109 "start": {}
110 }}
111 }}
112 }},
113 "nonce":"{}"
114}}
115"#,
116 pid, details, state, self.timestamp, uuid
117 );
118
119 self.send_json(json, 1u32)?;
120 Ok(())
121 }
122}