1use futures;
2use hyper;
3use hyper_tls;
4use serde_json;
5use url;
6
7use Error;
8
9use futures::{Future, IntoFuture, Stream};
10
11mod stream;
12
13pub use self::stream::GatewayConnection;
14
15pub struct Client {
17 http_client: hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>,
18 token: String,
19}
20
21impl Client {
22 pub fn connect(
24 token: &str,
25 ) -> impl Future<Item = (Client, stream::GatewayConnection), Error = Error> + Send {
26 let token = token.to_owned();
27 {
28 let token_ref: &str = &token;
29 hyper_tls::HttpsConnector::new(1)
30 .map(|connector| hyper::Client::builder().build(connector))
31 .map_err(|e| e.into())
32 .into_future()
33 .join(
34 hyper::Request::get("https://discordapp.com/api/v6/gateway/bot")
35 .header(hyper::header::AUTHORIZATION, token_ref)
36 .body(Default::default())
37 .map_err(|e| Error::Other(format!("{:?}", e)))
38 )
39 }
40 .and_then(|(http, gateway_req)| {
41 http.request(gateway_req)
42 .map_err(|e| e.into())
43 .and_then(
44 |resp| -> Box<Future<Item = hyper::Chunk, Error = Error> + Send> {
45 match resp.status() {
46 hyper::StatusCode::UNAUTHORIZED => {
47 Box::new(futures::future::err(Error::AuthenticationFailed))
48 }
49 hyper::StatusCode::OK => {
50 Box::new(resp.into_body().concat2().map_err(|e| e.into()))
51 }
52 status => Box::new(futures::future::err(Error::Other(format!(
53 "Gateway request returned unexpected status {}",
54 status
55 )))),
56 }
57 },
58 )
59 .and_then(|body| {
60 #[derive(Deserialize)]
61 struct GetGatewayResult<'a> {
62 url: &'a str,
63 }
64 let result: GetGatewayResult = serde_json::from_slice(&body).map_err(|e| {
65 Error::Other(format!("Unable to parse Gateway API response: {:?}", e))
66 })?;
67
68 println!("{}", result.url);
69 url::Url::parse(&result.url)
70 .map_err(|e| Error::Other(format!("Unable to parse Gateway URL: {:?}", e)))
71 .map(|url| {
72 (
73 Client {
74 http_client: http,
75 token: token.clone(),
76 },
77 stream::GatewayConnection::connect_new(url, token),
78 )
79 })
80 })
81 })
82 }
83
84 pub fn send_message(
86 &self,
87 message: &::MessageBuilder,
88 channel: &str,
89 ) -> impl Future<Item = (), Error = Error> + Send {
90 message.to_request_body(channel)
91 .and_then(|body| {
92 let auth_value = format!("Bot {}", self.token);
93 let auth_value_ref: &str = &auth_value;
94 hyper::Request::post(format!(
95 "https://discordapp.com/api/v6/channels/{}/messages",
96 channel
97 )).header(hyper::header::AUTHORIZATION, auth_value_ref)
98 .header(hyper::header::CONTENT_TYPE, "application/json")
99 .header(hyper::header::CONTENT_LENGTH, body.len())
100 .body(body.into())
101 .map_err(|e| Error::Other(format!("Failed to create request: {:?}", e)))
102 })
103 .and_then(|req| {
104 Ok(self.http_client.request(req)
105 .map_err(|e| e.into()))
106 })
107 .into_future()
108 .and_then(|x| x)
109 .and_then(|resp| -> Box<Future<Item = (), Error = Error> + Send> {
110 match resp.status() {
111 hyper::StatusCode::OK => Box::new(futures::future::ok(())),
112 _ => Box::new(resp.into_body().concat2().map_err(|e| e.into()).and_then(
113 |body| {
114 Err(Error::Other(format!(
115 "Message sending failed {}",
116 String::from_utf8_lossy(&body.to_vec())
117 )))
118 },
119 )),
120 }
121 })
122 }
123}