1use anyhow::Result;
4use tokio::{
5 runtime::{Builder, Runtime},
6 task::JoinHandle,
7};
8use uuid::Uuid;
9
10use crate::{
11 socket::DiscordIPCSocket,
12 utils::{get_current_timestamp, pack},
13};
14
15pub struct DiscordIPCSync {
17 inner: DiscordIPC,
18 rt: Runtime,
19}
20
21impl DiscordIPCSync {
22 pub fn new(client_id: &str) -> Result<Self> {
27 let rt = Builder::new_multi_thread().enable_all().build()?;
28 let inner = rt.block_on(DiscordIPC::new(client_id))?;
29 Ok(Self { inner, rt })
30 }
31
32 pub fn run(&mut self) -> Result<()> {
34 self.rt.block_on(self.inner.run())
35 }
36
37 pub fn set_activity(&mut self, details: &str, state: &str) -> Result<()> {
39 self.rt.block_on(self.inner.set_activity(details, state))
40 }
41
42 pub fn wait(&mut self) -> Result<()> {
44 self.rt.block_on(self.inner.wait())
45 }
46}
47
48pub struct DiscordIPC {
50 sock: DiscordIPCSocket,
51 ipc_task: Option<JoinHandle<Result<()>>>,
52 timestamp: u64,
53 client_id: String,
54}
55
56impl DiscordIPC {
57 async fn send_json(&mut self, json: String, opcode: u32) -> Result<()> {
58 let bytes = json.as_bytes();
59
60 let packed = pack(opcode, bytes.len() as u32)?;
61 self.sock.write(&packed).await?;
62 self.sock.write(bytes).await?;
63
64 Ok(())
65 }
66
67 pub async fn new(client_id: &str) -> Result<Self> {
70 let sock = DiscordIPCSocket::new().await?;
71
72 Ok(Self {
73 sock,
74 ipc_task: None,
75 timestamp: get_current_timestamp()?,
76 client_id: client_id.to_string(),
77 })
78 }
79
80 async fn handshake(&mut self) -> Result<()> {
81 let json = format!(r#"{{"v":1,"client_id":"{}"}}"#, self.client_id);
82 self.send_json(json, 0u32).await?;
83
84 Ok(())
85 }
86
87 async fn wait_for_ready(&mut self) -> Result<()> {
88 loop {
89 let frame = self.sock.read_frame().await?;
90
91 if frame.opcode == 1 && frame.body.windows(5).any(|w| w == b"READY") {
92 break;
93 }
94 }
95 Ok(())
96 }
97
98 pub async fn run(&mut self) -> Result<()> {
101 if self.ipc_task.is_some() {
102 return Ok(());
103 }
104
105 self.handshake().await?;
106 self.wait_for_ready().await?;
107
108 let mut sock = self.sock.clone();
109 self.ipc_task = Some(tokio::spawn(async move { sock.handle_ipc().await }));
110
111 Ok(())
112 }
113
114 pub async fn wait(&mut self) -> Result<()> {
116 if let Some(handle) = &mut self.ipc_task {
117 match handle.await {
118 Ok(res) => res?,
119 Err(e) if e.is_cancelled() => {}
120 Err(e) => return Err(e.into()),
121 }
122 }
123 Ok(())
124 }
125
126 pub async fn set_activity(&mut self, details: &str, state: &str) -> Result<()> {
128 let pid = std::process::id();
129 let uuid = Uuid::new_v4();
130
131 let json = format!(
132 r#"
133{{
134 "cmd":"SET_ACTIVITY",
135 "args": {{
136 "pid": {},
137 "activity": {{
138 "details":"{}",
139 "state":"{}",
140 "timestamps": {{
141 "start": {}
142 }}
143 }}
144 }},
145 "nonce":"{}"
146}}
147"#,
148 pid, details, state, self.timestamp, uuid
149 );
150
151 self.send_json(json, 1u32).await?;
152 Ok(())
153 }
154}