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