diff --git a/Cargo.toml b/Cargo.toml index 75448ca..74587b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ cfg-if = "1" reqwest = { version = "0.11", optional = true } serde = { version = "1", features = ["derive"] } tokio = { version = "1", features = ["rt", "sync", "time"], optional = true } -urlencoding = { version = "2", optional = true } +urlencoding = "2" serenity = { version = "0.12", features = ["builder", "client", "gateway", "model", "utils"], optional = true } @@ -39,7 +39,7 @@ rustc-args = ["--cfg", "docsrs"] [features] default = ["api"] -api = ["chrono", "reqwest", "serde_json", "urlencoding"] +api = ["chrono", "reqwest", "serde_json"] autoposter = ["api", "tokio"] serenity = ["dep:serenity"] diff --git a/src/bot.rs b/src/bot.rs index 9991ef3..1a59080 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -365,7 +365,7 @@ impl<'a> GetBots<'a> { /// Sets the maximum amount of bots to be queried. limit: u16 = query("limit={}&", min(limit, 500)); - /// Sets the amount of bots to be skipped during the GetBots. + /// Sets the amount of bots to be skipped during the query. skip: u16 = query("offset={}&", min(skip, 499)); /// Queries only Discord bots that matches this username. diff --git a/src/lib.rs b/src/lib.rs index 0749924..769ac0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,12 @@ #![cfg_attr(docsrs, feature(doc_cfg))] mod snowflake; +mod util; cfg_if::cfg_if! { if #[cfg(feature = "api")] { mod client; mod error; - mod util; #[cfg(feature = "autoposter")] pub(crate) use client::InnerClient; @@ -41,6 +41,7 @@ cfg_if::cfg_if! { if #[cfg(feature = "webhook")] { mod webhook; + #[cfg_attr(docsrs, doc(cfg(feature = "webhook")))] pub use webhook::*; } } diff --git a/src/util.rs b/src/util.rs index 6f34774..76720ab 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,10 +1,3 @@ -use crate::Error; -use chrono::{DateTime, TimeZone, Utc}; -use reqwest::Response; -use serde::{de::DeserializeOwned, Deserialize, Deserializer}; - -const DISCORD_EPOCH: u64 = 1_420_070_400_000; - macro_rules! debug_struct { ( $(#[$struct_attr:meta])* @@ -76,66 +69,77 @@ macro_rules! debug_struct { pub(crate) use debug_struct; -#[inline(always)] -pub(crate) fn deserialize_optional_string<'de, D>( - deserializer: D, -) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - Ok(match ::deserialize(deserializer) { - Ok(s) => { - if s.is_empty() { - None - } else { - Some(s) - } - } - _ => None, - }) -} +cfg_if::cfg_if! { + if #[cfg(feature = "api")] { + use crate::Error; + use chrono::{DateTime, TimeZone, Utc}; + use reqwest::Response; + use serde::{de::DeserializeOwned, Deserialize, Deserializer}; -#[inline(always)] -pub(crate) fn deserialize_default<'de, D, T>(deserializer: D) -> Result -where - T: Default + Deserialize<'de>, - D: Deserializer<'de>, -{ - Option::deserialize(deserializer).map(|res| res.unwrap_or_default()) -} + const DISCORD_EPOCH: u64 = 1_420_070_400_000; -#[inline(always)] -pub(crate) fn get_creation_date(id: u64) -> DateTime { - Utc - .timestamp_millis_opt(((id >> 22) + DISCORD_EPOCH) as _) - .single() - .unwrap() -} + #[inline(always)] + pub(crate) fn deserialize_optional_string<'de, D>( + deserializer: D, + ) -> Result, D::Error> + where + D: Deserializer<'de>, + { + Ok(match ::deserialize(deserializer) { + Ok(s) => { + if s.is_empty() { + None + } else { + Some(s) + } + } + _ => None, + }) + } -#[inline(always)] -pub(crate) async fn parse_json(response: Response) -> crate::Result -where - T: DeserializeOwned, -{ - if let Ok(bytes) = response.bytes().await { - if let Ok(json) = serde_json::from_slice(&bytes) { - return Ok(json); + #[inline(always)] + pub(crate) fn deserialize_default<'de, D, T>(deserializer: D) -> Result + where + T: Default + Deserialize<'de>, + D: Deserializer<'de>, + { + Option::deserialize(deserializer).map(|res| res.unwrap_or_default()) } - } - Err(Error::InternalServerError) -} + #[inline(always)] + pub(crate) fn get_creation_date(id: u64) -> DateTime { + Utc + .timestamp_millis_opt(((id >> 22) + DISCORD_EPOCH) as _) + .single() + .unwrap() + } + + #[inline(always)] + pub(crate) async fn parse_json(response: Response) -> crate::Result + where + T: DeserializeOwned, + { + if let Ok(bytes) = response.bytes().await { + if let Ok(json) = serde_json::from_slice(&bytes) { + return Ok(json); + } + } + + Err(Error::InternalServerError) + } -pub(crate) fn get_avatar(hash: &Option, id: u64) -> String { - match hash { - Some(hash) => { - let ext = if hash.starts_with("a_") { "gif" } else { "png" }; + pub(crate) fn get_avatar(hash: &Option, id: u64) -> String { + match hash { + Some(hash) => { + let ext = if hash.starts_with("a_") { "gif" } else { "png" }; - format!("https://cdn.discordapp.com/avatars/{id}/{hash}.{ext}?size=1024") + format!("https://cdn.discordapp.com/avatars/{id}/{hash}.{ext}?size=1024") + } + _ => format!( + "https://cdn.discordapp.com/embed/avatars/{}.png", + (id >> 22) % 5 + ), + } } - _ => format!( - "https://cdn.discordapp.com/embed/avatars/{}.png", - (id >> 22) % 5 - ), } } diff --git a/src/webhook/vote.rs b/src/webhook/vote.rs index ca09880..fcb5a3f 100644 --- a/src/webhook/vote.rs +++ b/src/webhook/vote.rs @@ -1,46 +1,7 @@ -use crate::snowflake; +use crate::{snowflake, util}; use serde::{Deserialize, Deserializer}; use std::collections::HashMap; -/// A struct representing a dispatched [Top.gg](https://top.gg) bot/server vote event. -#[must_use] -#[cfg_attr(docsrs, doc(cfg(feature = "webhook")))] -#[derive(Clone, Debug, Deserialize)] -pub struct Vote { - /// The ID of the bot/server that received a vote. - #[serde( - deserialize_with = "snowflake::deserialize", - alias = "bot", - alias = "guild" - )] - pub receiver_id: u64, - - /// The ID of the user who voted. - #[serde(deserialize_with = "snowflake::deserialize", rename = "user")] - pub voter_id: u64, - - /// Whether this vote's receiver is a server or not (bot otherwise). - #[serde( - default = "_true", - deserialize_with = "deserialize_is_server", - rename = "bot" - )] - pub is_server: bool, - - /// Whether this vote is just a test coming from the bot/server owner or not. Most of the time this would be `false`. - #[serde(deserialize_with = "deserialize_is_test", rename = "type")] - pub is_test: bool, - - /// Whether the weekend multiplier is active or not, meaning a single vote counts as two. - /// If the dispatched event came from a server being voted, this will always be `false`. - #[serde(default, rename = "isWeekend")] - pub is_weekend: bool, - - /// GetBots strings found on the vote page. - #[serde(default, deserialize_with = "deserialize_GetBots_string")] - pub GetBots: HashMap, -} - #[inline(always)] fn deserialize_is_test<'de, D>(deserializer: D) -> Result where @@ -61,7 +22,7 @@ where Ok(String::deserialize(deserializer).is_err()) } -fn deserialize_GetBots_string<'de, D>(deserializer: D) -> Result, D::Error> +fn deserialize_query_string<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { @@ -84,6 +45,49 @@ where ) } +util::debug_struct! { + /// A struct representing a dispatched [Top.gg](https://top.gg) bot/server vote event. + #[must_use] + #[cfg_attr(docsrs, doc(cfg(feature = "webhook")))] + #[derive(Clone, Deserialize)] + Vote { + public { + /// The ID of the bot/server that received a vote. + #[serde( + deserialize_with = "snowflake::deserialize", + alias = "bot", + alias = "guild" + )] + receiver_id: u64, + + /// The ID of the user who voted. + #[serde(deserialize_with = "snowflake::deserialize", rename = "user")] + voter_id: u64, + + /// Whether this vote's receiver is a server or not (bot otherwise). + #[serde( + default = "_true", + deserialize_with = "deserialize_is_server", + rename = "bot" + )] + is_server: bool, + + /// Whether this vote is just a test coming from the bot/server owner or not. Most of the time this would be `false`. + #[serde(deserialize_with = "deserialize_is_test", rename = "type")] + is_test: bool, + + /// Whether the weekend multiplier is active or not, meaning a single vote counts as two. + /// If the dispatched event came from a server being voted, this will always be `false`. + #[serde(default, rename = "isWeekend")] + is_weekend: bool, + + /// query strings found on the vote page. + #[serde(default, deserialize_with = "deserialize_query_string")] + query: HashMap, + } + } +} + cfg_if::cfg_if! { if #[cfg(any(feature = "actix-web", feature = "rocket"))] { /// A struct that represents an **unauthenticated** request containing a [`Vote`] data.