use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use log::debug; use serde_json::{json, Value}; use std::error::Error; use std::io::{Read, Write}; use std::{ env, net::Shutdown, os::unix::net::UnixStream, path::PathBuf, time }; use std::path::PathBuf; use crate::models::client::{commands::Commands, payload::OpCode, payload::Payload}; use crate::models::error::Error as ErrorMsg; use crate::models::error::Error::DiscordNotFound; /// Client used to communicate with Discord through IPC. pub struct DiscordClient { pub id: String, pub is_connected: bool, socket: Option, } impl DiscordClient { /// Used to instantiate a new Discord Client. pub fn new(id: &str) -> Self { Self { id: id.to_string(), is_connected: false, socket: None, } } /// Tries to enable a connection to the Discord Application. pub fn connect(&mut self) -> Result<(), ErrorMsg> { let path = self.fetch_process_pathbuf().join("discord-ipc-0"); match UnixStream::connect(&path) { Ok(socket) => { // self.socket.set_nonblocking(true)?; // self.socket.set_write_timeout(Some(time::Duration::from_secs(30)))?; // self.socket.set_read_timeout(Some(time::Duration::from_secs(30)))?; self.socket = Some(socket); self.handshake().expect("Could not handshake."); self.is_connected = true; Ok(()) } Err(_) => { self.is_connected = false; Err(DiscordNotFound) } } } pub fn send_payload(&mut self, payload: Payload) -> Result<(u32, Value), Box> { let payload = json!({ "cmd": Commands::SetActivity.as_string(), "args": { "pid": std::process::id(), payload.event_name: payload.event_data, }, "nonce": uuid::Uuid::new_v4().to_string(), }); match self.send(payload, OpCode::MESSAGE as u8) { Ok(retval) => { Ok(retval) }, Err(e) => { self.is_connected = false; Err(e) }, } } fn socket(&mut self) -> &mut PipeClient { match &mut self.socket { Some(socket) => socket, None => panic!("Socket is not initialized"), } } fn fetch_process_pathbuf(&mut self) -> PathBuf { let tmp = env::var("XDG_RUNTIME_DIR") .or_else(|_| env::var("TMPDIR")) .or_else(|_| env::var("TMP")) .or_else(|_| env::var("TEMP")) .unwrap_or_else(|_| "/tmp".to_owned()); PathBuf::from(tmp) } fn handshake(&mut self) -> Result<(u32, Value), Box> { let payload = json!({ "v": 1, "client_id": self.id}); Ok(self.send(payload, OpCode::HANDSHAKE as u8)?) } fn send(&mut self, payload: Value, opcode: u8) -> Result<(u32, Value), Box> { let payload = payload.to_string(); let mut data: Vec = Vec::new(); data.write_u32::(opcode as u32)?; data.write_u32::(payload.len() as u32)?; data.write_all(payload.as_bytes())?; self.socket().write_all(&data)?; Ok(self.recv()?) } fn recv(&mut self) -> Result<(u32, Value), Box> { let mut buf = [0; 2048]; let byte_count = self.socket().read(&mut buf)?; let (op, payload) = self.extract_payload(&buf[..byte_count])?; let json_data = serde_json::from_str::(&payload)?; debug!("{:?}", json_data); Ok((op, json_data)) } fn extract_payload(&mut self, mut data: &[u8]) -> Result<(u32, String), Box> { let opcode = data.read_u32::()?; let payload_len = data.read_u32::()? as usize; let mut payload = String::with_capacity(payload_len); data.read_to_string(&mut payload)?; Ok((opcode, payload)) } }