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