1mod callback;
2pub mod future;
3pub mod option;
4pub mod private;
5pub mod response;
6pub mod spot;
7
8use callback::Arg;
9use callback::Callback;
10use log::*;
11use serde::Serialize;
12use std::net::TcpStream;
13use std::sync::mpsc::Receiver;
14use std::{sync::mpsc, thread, time::Duration};
15use tungstenite::{connect, stream::MaybeTlsStream, Message, WebSocket};
16
17use crate::error::Result;
18use crate::util::millis;
19use crate::util::sign;
20use crate::FutureRole;
21
22use self::future::FutureWebSocketApiClientBuilder;
23use self::option::OptionWebSocketApiClientBuilder;
24use self::private::PrivateWebSocketApiClientBuilder;
25use self::spot::SpotWebSocketApiClientBuilder;
26
27pub struct WebSocketApiClient;
29
30impl WebSocketApiClient {
31 pub fn spot() -> SpotWebSocketApiClientBuilder {
33 SpotWebSocketApiClientBuilder::new()
34 }
35
36 pub fn future_inverse() -> FutureWebSocketApiClientBuilder {
38 FutureWebSocketApiClientBuilder::new(FutureRole::Inverse)
39 }
40
41 pub fn future_linear() -> FutureWebSocketApiClientBuilder {
43 FutureWebSocketApiClientBuilder::new(FutureRole::Linear)
44 }
45
46 pub fn option() -> OptionWebSocketApiClientBuilder {
48 OptionWebSocketApiClientBuilder::new()
49 }
50
51 pub fn private() -> PrivateWebSocketApiClientBuilder {
53 PrivateWebSocketApiClientBuilder::new()
54 }
55}
56
57struct Subscriber {
58 topics: Vec<String>,
59}
60
61impl Subscriber {
62 fn new() -> Self {
63 Self { topics: Vec::new() }
64 }
65
66 fn topics(&self) -> &Vec<String> {
67 &self.topics
68 }
69
70 fn sub_orderbook(&mut self, symbol: &str, depth: u16) {
71 self.sub(format!("orderbook.{depth}.{symbol}"));
72 }
73
74 fn sub_ticker(&mut self, symbol: &str) {
75 self.sub(format!("tickers.{symbol}"));
76 }
77
78 fn sub_trade(&mut self, symbol: &str) {
79 self.sub(format!("publicTrade.{symbol}"));
80 }
81
82 fn sub_kline(&mut self, symbol: &str, interval: &str) {
83 self.sub(format!("kline.{interval}.{symbol}"));
84 }
85
86 fn sub_liquidation(&mut self, symbol: &str) {
87 self.sub(format!("liquidation.{symbol}"));
88 }
89
90 fn sub_lt_kline(&mut self, symbol: &str, interval: &str) {
91 self.sub(format!("kline_lt.{interval}.{symbol}"));
92 }
93
94 fn sub_lt_ticker(&mut self, symbol: &str) {
95 self.sub(format!("tickers_lt.{symbol}"));
96 }
97
98 fn sub_lt_nav(&mut self, symbol: &str) {
99 self.sub(format!("lt.{symbol}"));
100 }
101
102 fn sub_position(&mut self) {
103 self.sub("position".to_string());
104 }
105
106 fn sub_execution(&mut self) {
107 self.sub("execution".to_string());
108 }
109
110 fn sub_order(&mut self) {
111 self.sub("order".to_string());
112 }
113
114 fn sub_wallet(&mut self) {
115 self.sub("wallet".to_string());
116 }
117
118 fn sub_greek(&mut self) {
119 self.sub("greeks".to_string());
120 }
121
122 fn sub(&mut self, topic: String) {
123 self.topics.push(topic);
124 }
125}
126
127#[derive(Serialize)]
128struct Op<'a> {
129 op: &'a str,
130 args: Vec<String>,
131}
132
133struct Credentials {
134 api_key: String,
135 secret: String,
136}
137
138fn run<A, C>(
139 uri: &str,
140 topics: &Vec<String>,
141 credentials: Option<&Credentials>,
142 mut callback: C,
143) -> Result<()>
144where
145 A: Arg,
146 C: Callback<A>,
147{
148 let (mut ws, _) = connect(uri)?;
149
150 set_read_timeout(&ws);
156
157 if let Some(credentials) = credentials {
159 let req = auth_req(credentials);
160 ws.write_message(Message::Text(req))?;
161 }
162
163 ws.write_message(Message::Text(subscription(topics)))?;
165
166 let rx = ping();
167 loop {
168 if let Ok(ping) = rx.try_recv() {
170 ws.write_message(Message::Text(ping.into()))?
171 }
172
173 match ws.read_message() {
174 Ok(msg) => match msg {
175 Message::Text(content) => {
176 debug!("Received: {}", content);
177 match serde_json::from_str(&content) {
178 Ok(res) => callback(res),
179 Err(e) => error!("Error: {}", e),
180 }
181 }
182 _ => {}
183 },
184 Err(e) => match e {
185 tungstenite::Error::Io(ref ee) => {
186 if ee.kind() != std::io::ErrorKind::WouldBlock
187 && ee.kind() != std::io::ErrorKind::TimedOut
188 {
189 Err(e)?
190 }
191 }
192 _ => Err(e)?,
193 },
194 }
195 }
196}
197
198fn set_read_timeout(ws: &WebSocket<MaybeTlsStream<TcpStream>>) {
199 match ws.get_ref() {
200 MaybeTlsStream::Plain(s) => {
201 s.set_read_timeout(Some(Duration::from_secs(10))).unwrap();
202 }
203 MaybeTlsStream::NativeTls(t) => {
204 t.get_ref()
205 .set_read_timeout(Some(Duration::from_secs(10)))
206 .unwrap();
207 }
208 _ => unreachable!(),
209 };
210}
211
212fn auth_req(credentials: &Credentials) -> String {
213 let expires = millis() + 10000;
214 let val = format!("GET/realtime{}", expires);
215 let signature = sign(&credentials.secret, &val);
216 let auth_req = Op {
217 op: "auth",
218 args: vec![credentials.api_key.clone(), expires.to_string(), signature],
219 };
220 serde_json::to_string(&auth_req).unwrap()
221}
222
223fn subscription(topics: &Vec<String>) -> String {
224 let sub = Op {
225 op: "subscribe",
226 args: topics.clone(),
227 };
228 serde_json::to_string(&sub).unwrap()
229}
230
231fn ping() -> Receiver<&'static str> {
232 let (tx, rx) = mpsc::channel();
233 thread::spawn(move || loop {
234 if let Err(_) = tx.send("{\"op\":\"ping\"}") {
235 break;
236 };
237 thread::sleep(Duration::from_secs(20));
238 });
239 rx
240}