1use anyhow::Result;
2use tokio::task::JoinHandle;
3use uuid::Uuid;
4
5use crate::{socket::DiscordIPCSocket, utils::pack};
6
7pub struct DiscordIPC {
10 sock: DiscordIPCSocket,
11 ipc_task: Option<JoinHandle<Result<()>>>,
12 client_id: String,
13}
14
15impl DiscordIPC {
16 pub async fn new_from(client_id: &str) -> Result<Self> {
19 let sock = DiscordIPCSocket::new().await?;
20
21 Ok(Self {
22 sock,
23 ipc_task: None,
24 client_id: client_id.to_string(),
25 })
26 }
27
28 pub async fn handshake(&mut self) -> Result<()> {
31 let json = format!(r#"{{"v":1,"client_id":"{}"}}"#, self.client_id);
32 let bytes = json.as_bytes();
33
34 let pack = pack(0u32, bytes.len() as u32)?;
35 self.sock.write(&pack).await?;
36 self.sock.write(bytes).await?;
37
38 Ok(())
39 }
40
41 pub async fn wait_for_ready(&mut self) -> Result<()> {
43 loop {
44 let frame = self.sock.read_frame().await?;
45
46 if frame.opcode == 1 && frame.body.windows(5).any(|w| w == b"READY") {
47 break;
48 }
49 }
50 Ok(())
51 }
52
53 pub async fn run(&mut self) -> Result<()> {
56 if self.ipc_task.is_some() {
57 return Ok(());
58 }
59
60 self.handshake().await?;
61 self.wait_for_ready().await?;
62
63 let mut sock = self.sock.clone();
64 self.ipc_task = Some(tokio::spawn(async move { sock.handle_ipc().await }));
65
66 Ok(())
67 }
68
69 pub async fn wait(&mut self) -> Result<()> {
71 if let Some(handle) = &mut self.ipc_task {
72 match handle.await {
73 Ok(res) => res?,
74 Err(e) if e.is_cancelled() => {}
75 Err(e) => return Err(e.into()),
76 }
77 }
78 Ok(())
79 }
80
81 pub async fn set_activity(&mut self, top: &str, bottom: &str) -> Result<()> {
83 let pid = std::process::id();
84 let uuid = Uuid::new_v4();
85
86 let json = format!(
87 r#"
88{{
89 "cmd":"SET_ACTIVITY",
90 "args": {{
91 "pid": {},
92 "activity": {{
93 "details":"{}",
94 "state":"{}"
95 }}
96 }},
97 "nonce":"{}"
98}}
99"#,
100 pid, top, bottom, uuid
101 );
102
103 let bytes = json.as_bytes();
104 let pack = pack(1u32, bytes.len() as u32)?;
105
106 self.sock.write(&pack).await?;
107 self.sock.write(bytes).await?;
108 Ok(())
109 }
110}