1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
use crate::error::DiscordError; use crate::gateway::{GateMessage, GateUrl, OpCode}; use crate::route::Route; use crate::{request_url, GATEWAY_VERSION}; use log::info; use serde_json::Value; use std::time::Duration; use tungstenite::connect; use ureq::{Agent, AgentBuilder}; #[derive(Debug)] pub struct Client { http: Agent, secret: String, } impl Client { pub fn new(secret: impl AsRef<str>) -> Self { let http = AgentBuilder::new().build(); let secret = secret.as_ref().to_string(); Client { http, secret } } pub fn run(self) -> Result<(), DiscordError> { let url = request_url(Route::Gateway); let url = format!("{}?v={}&encoding=json", url, GATEWAY_VERSION); let gateurl: GateUrl = self.http.get(&url).call()?.into_json()?; info!("Connecting to websocket stream."); let (mut socket, _response) = connect(&gateurl.url)?; match socket.get_mut() { tungstenite::stream::Stream::Plain(s) => { s.set_nonblocking(true)?; } tungstenite::stream::Stream::Tls(s) => { s.get_mut().set_nonblocking(true)?; } }; info!("Reading hello message."); let hello: GateMessage = serde_json::from_str(&socket.read_message()?.into_text()?)?; assert_eq!(hello.op, OpCode::Hello as u8); let interval = hello.d.get("heartbeat_interval").unwrap().as_u64().unwrap(); info!("Heartbeat interval is {} ms.", &interval); let heartbeat = GateMessage { op: OpCode::HeartBeat as u8, d: Value::default(), s: None, t: None, }; let mut delay = 0; loop { match socket.read_message() { Ok(m) => { let message: GateMessage = serde_json::from_str(&m.into_text()?)?; match message.op { 1 => { info!("Re-heartbeating."); socket.write_message(serde_json::to_string(&heartbeat)?.into())?; } 11 => info!("Heartbeat acknowledged."), _ => info!("Other message: {:?}", &message), } } Err(tungstenite::Error::Io(e)) if e.kind() == std::io::ErrorKind::WouldBlock => {} Err(e) => return Err(e.into()), }; std::thread::sleep(Duration::from_millis(100)); delay += 100; if delay >= interval { info!("Heartbeating."); socket.write_message(serde_json::to_string(&heartbeat)?.into())?; delay = 0; } } } }