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
48#[derive(Debug)]
50pub struct DiscordIPC {
51 sock: DiscordIPCSocket,
52 ipc_task: Option<JoinHandle<Result<()>>>,
53 timestamp: u64,
54 client_id: String,
55}
56
57impl DiscordIPC {
58 async fn send_json(&mut self, json: String, opcode: u32) -> Result<()> {
59 let bytes = json.as_bytes();
60
61 let packed = pack(opcode, bytes.len() as u32)?;
62 self.sock.write(&packed).await?;
63 self.sock.write(bytes).await?;
64
65 Ok(())
66 }
67
68 pub async fn new(client_id: &str) -> Result<Self> {
71 let sock = DiscordIPCSocket::new().await?;
72
73 Ok(Self {
74 sock,
75 ipc_task: None,
76 timestamp: get_current_timestamp()?,
77 client_id: client_id.to_string(),
78 })
79 }
80
81 async fn handshake(&mut self) -> Result<()> {
82 let json = format!(r#"{{"v":1,"client_id":"{}"}}"#, self.client_id);
83 self.send_json(json, 0u32).await?;
84
85 Ok(())
86 }
87
88 async fn wait_for_ready(&mut self) -> Result<()> {
89 loop {
90 let frame = self.sock.read_frame().await?;
91
92 if frame.opcode == 1 && frame.body.windows(5).any(|w| w == b"READY") {
93 break;
94 }
95 }
96 Ok(())
97 }
98
99 pub async fn run(&mut self) -> Result<()> {
102 if self.ipc_task.is_some() {
103 return Ok(());
104 }
105
106 self.handshake().await?;
107 self.wait_for_ready().await?;
108
109 let mut sock = self.sock.clone();
110 self.ipc_task = Some(tokio::spawn(async move { sock.handle_ipc().await }));
111
112 Ok(())
113 }
114
115 pub async fn wait(&mut self) -> Result<()> {
117 if let Some(handle) = &mut self.ipc_task {
118 match handle.await {
119 Ok(res) => res?,
120 Err(e) if e.is_cancelled() => {}
121 Err(e) => return Err(e.into()),
122 }
123 }
124 Ok(())
125 }
126
127 pub async fn set_activity(&mut self, details: &str, state: &str) -> Result<()> {
129 let pid = std::process::id();
130 let uuid = Uuid::new_v4();
131
132 let json = format!(
133 r#"
134{{
135 "cmd":"SET_ACTIVITY",
136 "args": {{
137 "pid": {},
138 "activity": {{
139 "details":"{}",
140 "state":"{}",
141 "timestamps": {{
142 "start": {}
143 }}
144 }}
145 }},
146 "nonce":"{}"
147}}
148"#,
149 pid, details, state, self.timestamp, uuid
150 );
151
152 self.send_json(json, 1u32).await?;
153 Ok(())
154 }
155}