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
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Bridge to provide a client implementation for the `reqwest` crate.
//!
//! # Examples
//!
//! Refer to the documentation for [`Requester`].
//!
//! [`Requester`]: trait.Requester.html

use crate::{
    endpoints,
    model::*,
    Result,
};
use futures::compat::Future01CompatExt;
use reqwest::{
    r#async::Client as ReqwestClient,
    header::{AUTHORIZATION, HeaderValue},
    Url,
};
use serde::de::DeserializeOwned;
use std::sync::Arc;

/// Struct which defines the methods necessary to interact with the service.
///
/// # Examples
///
/// To bring in the API client:
///
/// ```rust,no_run
/// use discord_bots_org::bridge::reqwest::r#async::Client;
/// ```
#[derive(Clone, Debug)]
pub struct Client {
    inner: Arc<ReqwestClient>,
}

impl Client {
    /// Creates a new client to interact with the API.
    ///
    /// This accepts an existing reqwest Client so a single HTTP client may be
    /// shared across your application.
    ///
    /// # Examples
    ///
    /// Create a new API client:
    ///
    /// ```rust
    /// extern crate reqwest;
    ///
    /// use discord_bots_org::ReqwestAsyncClient as ApiClient;
    /// use reqwest::r#async::Client as ReqwestClient;
    /// use std::sync::Arc;
    ///
    /// let reqwest_client = Arc::new(ReqwestClient::new());
    /// let api_client = ApiClient::new(Arc::clone(&reqwest_client));
    pub fn new(reqwest_client: Arc<ReqwestClient>) -> Self {
        Self {
            inner: reqwest_client,
        }
    }

    /// Retrieves information about a bot.
    pub async fn get_bot(&self, user_id: u64) -> Result<Bot> {
        await!(self.get(Url::parse(&endpoints::bot(user_id))?))
    }

    /// Retrieves a list of bots via a search.
    pub async fn get_bots<'a>(
        &'a self,
        params: Vec<(&'a str, String)>,
    ) -> Result<SearchResponse<Bot>> {
        await!(self.get(Url::parse_with_params(&endpoints::bots(), params)?))
    }

    /// Retrieves information about a bot's specific stats.
    pub async fn get_bot_stats(&self, user_id: u64) -> Result<BotStats> {
        await!(self.get(Url::parse(&endpoints::bot_stats(user_id))?))
    }

    /// Retrieve whether a user has upvoted a bot in the last 24 hours.
    ///
    /// You can use this if your bot has over 1000 votes.
    pub async fn get_bot_vote_check<'a>(
        &'a self,
        auth: impl AsRef<str> + 'a,
        bot_id: u64,
        user_id: u64,
    ) -> Result<bool> {
        let path = endpoints::bot_vote_check(bot_id, user_id);
        let params = &[("userId", user_id.to_string())];
        let url = Url::parse_with_params(&path, params)?;
        let (k, v) = (AUTHORIZATION, HeaderValue::from_str(auth.as_ref())?);

        let mut resp = await!(self.inner.get(url).header(k, v).send().compat())?;
        let body = await!(resp.json::<ResponseUserVoted>().compat())?;

        Ok(body.voted == 1)
    }

    /// Retrieves information to see who has upvoted a bot.
    ///
    /// **Note**: If your bot has over 1000 votes per month, then this can not
    /// be used. Webhooks must instead be used.
    pub async fn get_bot_votes<'a>(
        &'a self,
        auth: impl AsRef<str> + 'a,
        bot_id: u64,
    ) -> Result<BotVotes> {
        let url = Url::parse(&endpoints::bot_votes(bot_id))?;
        let (k, v) = (AUTHORIZATION, HeaderValue::from_str(auth.as_ref())?);
        let mut resp = await!(self.inner.get(url).header(k, v).send().compat())?;

        await!(resp.json().compat()).map_err(From::from)
    }

    /// Retrieves information about a user.
    pub async fn get_user(&self, user_id: u64) -> Result<User> {
        await!(self.get(Url::parse(&endpoints::user(user_id))?))
    }

    /// Posts a bot's shard stats.
    pub async fn post_stats<'a>(
        &'a self,
        auth: impl AsRef<str> + 'a,
        bot_id: u64,
        stats: &'a ShardStats,
    ) -> Result<()> {
        let url = Url::parse(&endpoints::bot_stats(bot_id))?;
        let (k, v) = (AUTHORIZATION, HeaderValue::from_str(auth.as_ref())?);
        await!(self
            .inner
            .post(url)
            .header(k, v)
            .json(stats)
            .send()
            .compat())?;

        Ok(())
    }

    async fn get<'a, T: DeserializeOwned>(&'a self, url: Url) -> Result<T> {
        let mut resp = await!(self.inner.get(url).send().compat())?;

        await!(resp.json::<T>().compat()).map_err(From::from)
    }
}