1use anyhow::Result;
4use tokio::task::JoinHandle;
5use uuid::Uuid;
6
7use crate::{
8 socket::DiscordIPCSocket,
9 utils::{get_current_timestamp_unix, pack},
10};
11
12pub struct DiscordIPC {
15 sock: DiscordIPCSocket,
16 ipc_task: Option<JoinHandle<Result<()>>>,
17 timestamp: u64,
18 client_id: String,
19}
20
21impl DiscordIPC {
22 async fn send_json(&mut self, json: String, opcode: u32) -> Result<()> {
23 let bytes = json.as_bytes();
24
25 let packed = pack(opcode, bytes.len() as u32)?;
26 self.sock.write(&packed).await?;
27 self.sock.write(bytes).await?;
28
29 Ok(())
30 }
31
32 pub async fn new_from(client_id: &str) -> Result<Self> {
35 let sock = DiscordIPCSocket::new().await?;
36
37 Ok(Self {
38 sock,
39 ipc_task: None,
40 timestamp: get_current_timestamp_unix()?,
41 client_id: client_id.to_string(),
42 })
43 }
44
45 pub async fn handshake(&mut self) -> Result<()> {
48 let json = format!(r#"{{"v":1,"client_id":"{}"}}"#, self.client_id);
49 self.send_json(json, 0u32).await?;
50
51 Ok(())
52 }
53
54 pub async fn wait_for_ready(&mut self) -> Result<()> {
56 loop {
57 let frame = self.sock.read_frame().await?;
58
59 if frame.opcode == 1 && frame.body.windows(5).any(|w| w == b"READY") {
60 break;
61 }
62 }
63 Ok(())
64 }
65
66 pub async fn run(&mut self) -> Result<()> {
69 if self.ipc_task.is_some() {
70 return Ok(());
71 }
72
73 self.handshake().await?;
74 self.wait_for_ready().await?;
75
76 let mut sock = self.sock.clone();
77 self.ipc_task = Some(tokio::spawn(async move { sock.handle_ipc().await }));
78
79 Ok(())
80 }
81
82 pub async fn wait(&mut self) -> Result<()> {
84 if let Some(handle) = &mut self.ipc_task {
85 match handle.await {
86 Ok(res) => res?,
87 Err(e) if e.is_cancelled() => {}
88 Err(e) => return Err(e.into()),
89 }
90 }
91 Ok(())
92 }
93
94 pub async 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).await?;
120 Ok(())
121 }
122}