pub mod ratelimiting;
pub mod request;
pub mod routing;
mod error;
pub use hyper::status::{StatusClass, StatusCode};
pub use self::error::Error as HttpError;
use constants;
use hyper::{
client::{
Client as HyperClient,
Request as HyperRequest,
Response as HyperResponse
},
header::{ContentType, Headers},
method::Method,
mime::{Mime, SubLevel, TopLevel},
net::HttpsConnector,
header,
Error as HyperError,
Result as HyperResult,
Url
};
use hyper_native_tls::NativeTlsClient;
use internal::prelude::*;
use model::prelude::*;
use multipart::client::Multipart;
use parking_lot::Mutex;
use self::{
request::Request,
routing::RouteInfo,
};
use serde::de::DeserializeOwned;
use serde_json;
use std::{
collections::BTreeMap,
default::Default,
fs::File,
io::ErrorKind as IoErrorKind,
path::{Path, PathBuf},
sync::Arc
};
lazy_static! {
static ref CLIENT: HyperClient = {
let tc = NativeTlsClient::new().expect("Unable to make http client");
let connector = HttpsConnector::new(tc);
HyperClient::with_connector(connector)
};
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LightMethod {
Delete,
Get,
Patch,
Post,
Put,
}
impl LightMethod {
pub fn hyper_method(&self) -> Method {
match *self {
LightMethod::Delete => Method::Delete,
LightMethod::Get => Method::Get,
LightMethod::Patch => Method::Patch,
LightMethod::Post => Method::Post,
LightMethod::Put => Method::Put,
}
}
}
lazy_static! {
static ref TOKEN: Arc<Mutex<String>> = Arc::new(Mutex::new(String::default()));
}
pub fn set_token(token: &str) { TOKEN.lock().clone_from(&token.to_string()); }
pub fn add_group_recipient(group_id: u64, user_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::AddGroupRecipient { group_id, user_id },
})
}
pub fn add_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::AddMemberRole { guild_id, role_id, user_id },
})
}
pub fn ban_user(guild_id: u64, user_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::GuildBanUser {
delete_message_days: Some(delete_message_days),
reason: Some(reason),
guild_id,
user_id,
},
})
}
pub fn ban_zeyla(guild_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
ban_user(guild_id, 114_941_315_417_899_012, delete_message_days, reason)
}
pub fn ban_luna(guild_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
ban_user(guild_id, 180_731_582_049_550_336, delete_message_days, reason)
}
pub fn ban_servermoms(guild_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
ban_zeyla(guild_id, delete_message_days, reason)?;
ban_luna(guild_id, delete_message_days, reason)
}
pub fn broadcast_typing(channel_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::BroadcastTyping { channel_id },
})
}
pub fn create_channel(guild_id: u64, map: &Value) -> Result<GuildChannel> {
fire(Request {
body: Some(map.to_string().as_bytes()),
headers: None,
route: RouteInfo::CreateChannel { guild_id },
})
}
pub fn create_emoji(guild_id: u64, map: &Value) -> Result<Emoji> {
fire(Request {
body: Some(map.to_string().as_bytes()),
headers: None,
route: RouteInfo::CreateEmoji { guild_id },
})
}
pub fn create_guild(map: &Value) -> Result<PartialGuild> {
fire(Request {
body: Some(map.to_string().as_bytes()),
headers: None,
route: RouteInfo::CreateGuild,
})
}
pub fn create_guild_integration(guild_id: u64, integration_id: u64, map: &Value) -> Result<()> {
wind(204, Request {
body: Some(map.to_string().as_bytes()),
headers: None,
route: RouteInfo::CreateGuildIntegration { guild_id, integration_id },
})
}
pub fn create_invite(channel_id: u64, map: &JsonMap) -> Result<RichInvite> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::CreateInvite { channel_id },
})
}
pub fn create_permission(channel_id: u64, target_id: u64, map: &Value) -> Result<()> {
let body = serde_json::to_vec(map)?;
wind(204, Request {
body: Some(&body),
headers: None,
route: RouteInfo::CreatePermission { channel_id, target_id },
})
}
pub fn create_private_channel(map: &Value) -> Result<PrivateChannel> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::GetUserDmChannels,
})
}
pub fn create_reaction(channel_id: u64,
message_id: u64,
reaction_type: &ReactionType)
-> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::CreateReaction {
reaction: &reaction_type.as_data(),
channel_id,
message_id,
},
})
}
pub fn create_role(guild_id: u64, map: &JsonMap) -> Result<Role> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::CreateRole {guild_id },
})
}
pub fn create_webhook(channel_id: u64, map: &Value) -> Result<Webhook> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::CreateWebhook { channel_id },
})
}
pub fn delete_channel(channel_id: u64) -> Result<Channel> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::DeleteChannel { channel_id },
})
}
pub fn delete_emoji(guild_id: u64, emoji_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteEmoji { guild_id, emoji_id },
})
}
pub fn delete_guild(guild_id: u64) -> Result<PartialGuild> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::DeleteGuild { guild_id },
})
}
pub fn delete_guild_integration(guild_id: u64, integration_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteGuildIntegration { guild_id, integration_id },
})
}
pub fn delete_invite(code: &str) -> Result<Invite> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::DeleteInvite { code },
})
}
pub fn delete_message(channel_id: u64, message_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteMessage { channel_id, message_id },
})
}
pub fn delete_messages(channel_id: u64, map: &Value) -> Result<()> {
wind(204, Request {
body: Some(map.to_string().as_bytes()),
headers: None,
route: RouteInfo::DeleteMessages { channel_id },
})
}
pub fn delete_message_reactions(channel_id: u64, message_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteMessageReactions { channel_id, message_id },
})
}
pub fn delete_permission(channel_id: u64, target_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeletePermission { channel_id, target_id },
})
}
pub fn delete_reaction(channel_id: u64,
message_id: u64,
user_id: Option<u64>,
reaction_type: &ReactionType)
-> Result<()> {
let user = user_id
.map(|uid| uid.to_string())
.unwrap_or_else(|| "@me".to_string());
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteReaction {
reaction: &reaction_type.as_data(),
user: &user,
channel_id,
message_id,
},
})
}
pub fn delete_role(guild_id: u64, role_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteRole { guild_id, role_id },
})
}
pub fn delete_webhook(webhook_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteWebhook { webhook_id },
})
}
pub fn delete_webhook_with_token(webhook_id: u64, token: &str) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::DeleteWebhookWithToken { token, webhook_id },
})
}
pub fn edit_channel(channel_id: u64, map: &JsonMap) -> Result<GuildChannel> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditChannel {channel_id },
})
}
pub fn edit_emoji(guild_id: u64, emoji_id: u64, map: &Value) -> Result<Emoji> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditEmoji { guild_id, emoji_id },
})
}
pub fn edit_guild(guild_id: u64, map: &JsonMap) -> Result<PartialGuild> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditGuild { guild_id },
})
}
pub fn edit_guild_channel_positions(guild_id: u64, value: &Value)
-> Result<()> {
let body = serde_json::to_vec(value)?;
wind(204, Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditGuildChannels { guild_id },
})
}
pub fn edit_guild_embed(guild_id: u64, map: &Value) -> Result<GuildEmbed> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditGuildEmbed { guild_id },
})
}
pub fn edit_member(guild_id: u64, user_id: u64, map: &JsonMap) -> Result<()> {
let body = serde_json::to_vec(map)?;
wind(204, Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditMember { guild_id, user_id },
})
}
pub fn edit_message(channel_id: u64, message_id: u64, map: &Value) -> Result<Message> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditMessage { channel_id, message_id },
})
}
pub fn edit_nickname(guild_id: u64, new_nickname: Option<&str>) -> Result<()> {
let map = json!({ "nick": new_nickname });
let body = serde_json::to_vec(&map)?;
wind(200, Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditNickname { guild_id },
})
}
pub fn edit_profile(map: &JsonMap) -> Result<CurrentUser> {
let body = serde_json::to_vec(map)?;
let response = request(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditProfile,
})?;
let mut value = serde_json::from_reader::<HyperResponse, Value>(response)?;
if let Some(map) = value.as_object_mut() {
if !TOKEN.lock().starts_with("Bot ") {
if let Some(Value::String(token)) = map.remove("token") {
set_token(&token);
}
}
}
serde_json::from_value::<CurrentUser>(value).map_err(From::from)
}
pub fn edit_role(guild_id: u64, role_id: u64, map: &JsonMap) -> Result<Role> {
let body = serde_json::to_vec(&map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditRole { guild_id, role_id },
})
}
pub fn edit_role_position(guild_id: u64, role_id: u64, position: u64) -> Result<Vec<Role>> {
let body = serde_json::to_vec(&json!({
"id": role_id,
"position": position,
}))?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditRole { guild_id, role_id },
})
}
pub fn edit_webhook(webhook_id: u64, map: &Value) -> Result<Webhook> {
fire(Request {
body: Some(map.to_string().as_bytes()),
headers: None,
route: RouteInfo::EditWebhook { webhook_id },
})
}
pub fn edit_webhook_with_token(webhook_id: u64, token: &str, map: &JsonMap) -> Result<Webhook> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::EditWebhookWithToken { token, webhook_id },
})
}
pub fn execute_webhook(webhook_id: u64,
token: &str,
wait: bool,
map: &JsonMap)
-> Result<Option<Message>> {
let body = serde_json::to_vec(map)?;
let mut headers = Headers::new();
headers.set(ContentType(
Mime(TopLevel::Application, SubLevel::Json, vec![]),
));
let response = request(Request {
body: Some(&body),
headers: Some(headers),
route: RouteInfo::ExecuteWebhook { token, wait, webhook_id },
})?;
if response.status == StatusCode::NoContent {
return Ok(None);
}
serde_json::from_reader::<HyperResponse, Message>(response)
.map(Some)
.map_err(From::from)
}
pub fn get_active_maintenances() -> Result<Vec<Maintenance>> {
let response = request(Request {
body: None,
headers: None,
route: RouteInfo::GetActiveMaintenance,
})?;
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
match map.remove("scheduled_maintenances") {
Some(v) => serde_json::from_value::<Vec<Maintenance>>(v)
.map_err(From::from),
None => Ok(vec![]),
}
}
pub fn get_bans(guild_id: u64) -> Result<Vec<Ban>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetBans { guild_id },
})
}
pub fn get_audit_logs(guild_id: u64,
action_type: Option<u8>,
user_id: Option<u64>,
before: Option<u64>,
limit: Option<u8>) -> Result<AuditLogs> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetAuditLogs {
action_type,
before,
guild_id,
limit,
user_id,
},
})
}
pub fn get_bot_gateway() -> Result<BotGateway> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetBotGateway,
})
}
pub fn get_channel_invites(channel_id: u64) -> Result<Vec<RichInvite>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetChannelInvites { channel_id },
})
}
pub fn get_channel_webhooks(channel_id: u64) -> Result<Vec<Webhook>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetChannelWebhooks { channel_id },
})
}
pub fn get_channel(channel_id: u64) -> Result<Channel> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetChannel { channel_id },
})
}
pub fn get_channels(guild_id: u64) -> Result<Vec<GuildChannel>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetChannels { guild_id },
})
}
pub fn get_current_application_info() -> Result<CurrentApplicationInfo> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetCurrentApplicationInfo,
})
}
pub fn get_current_user() -> Result<CurrentUser> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetCurrentUser,
})
}
pub fn get_gateway() -> Result<Gateway> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGateway,
})
}
pub fn get_guild(guild_id: u64) -> Result<PartialGuild> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuild { guild_id },
})
}
pub fn get_guild_embed(guild_id: u64) -> Result<GuildEmbed> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildEmbed { guild_id },
})
}
pub fn get_guild_integrations(guild_id: u64) -> Result<Vec<Integration>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildIntegrations { guild_id },
})
}
pub fn get_guild_invites(guild_id: u64) -> Result<Vec<RichInvite>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildInvites { guild_id },
})
}
pub fn get_guild_vanity_url(guild_id: u64) -> Result<String> {
#[derive(Deserialize)]
struct GuildVanityUrl {
code: String,
}
let response = request(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildVanityUrl { guild_id },
})?;
serde_json::from_reader::<HyperResponse, GuildVanityUrl>(response)
.map(|x| x.code)
.map_err(From::from)
}
pub fn get_guild_members(guild_id: u64,
limit: Option<u64>,
after: Option<u64>)
-> Result<Vec<Member>> {
let response = request(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildMembers { after, guild_id, limit },
})?;
let mut v = serde_json::from_reader::<HyperResponse, Value>(response)?;
if let Some(values) = v.as_array_mut() {
let num = Value::Number(Number::from(guild_id));
for value in values {
if let Some(element) = value.as_object_mut() {
element.insert("guild_id".to_string(), num.clone());
}
}
}
serde_json::from_value::<Vec<Member>>(v).map_err(From::from)
}
pub fn get_guild_prune_count(guild_id: u64, map: &Value) -> Result<GuildPrune> {
#[derive(Deserialize)]
struct GetGuildPruneCountRequest {
days: u64,
}
let req = serde_json::from_value::<GetGuildPruneCountRequest>(map.clone())?;
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildPruneCount {
days: req.days,
guild_id,
},
})
}
pub fn get_guild_regions(guild_id: u64) -> Result<Vec<VoiceRegion>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildRegions { guild_id },
})
}
pub fn get_guild_roles(guild_id: u64) -> Result<Vec<Role>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildRoles { guild_id },
})
}
pub fn get_guild_webhooks(guild_id: u64) -> Result<Vec<Webhook>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuildWebhooks { guild_id },
})
}
pub fn get_guilds(target: &GuildPagination, limit: u64) -> Result<Vec<GuildInfo>> {
let (after, before) = match *target {
GuildPagination::After(id) => (Some(id.0), None),
GuildPagination::Before(id) => (None, Some(id.0)),
};
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetGuilds { after, before, limit },
})
}
#[allow(unused_mut)]
pub fn get_invite(mut code: &str, stats: bool) -> Result<Invite> {
#[cfg(feature = "utils")]
{
code = ::utils::parse_invite(code);
}
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetInvite { code, stats },
})
}
pub fn get_member(guild_id: u64, user_id: u64) -> Result<Member> {
let response = request(Request {
body: None,
headers: None,
route: RouteInfo::GetMember { guild_id, user_id },
})?;
let mut v = serde_json::from_reader::<HyperResponse, Value>(response)?;
if let Some(map) = v.as_object_mut() {
map.insert("guild_id".to_string(), Value::Number(Number::from(guild_id)));
}
serde_json::from_value::<Member>(v).map_err(From::from)
}
pub fn get_message(channel_id: u64, message_id: u64) -> Result<Message> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetMessage { channel_id, message_id },
})
}
pub fn get_messages(channel_id: u64, query: &str) -> Result<Vec<Message>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetMessages {
query: query.to_owned(),
channel_id,
},
})
}
pub fn get_pins(channel_id: u64) -> Result<Vec<Message>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetPins { channel_id },
})
}
pub fn get_reaction_users(channel_id: u64,
message_id: u64,
reaction_type: &ReactionType,
limit: u8,
after: Option<u64>)
-> Result<Vec<User>> {
let reaction = reaction_type.as_data();
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetReactionUsers {
after,
channel_id,
limit,
message_id,
reaction,
},
})
}
pub fn get_unresolved_incidents() -> Result<Vec<Incident>> {
let response = request(Request {
body: None,
headers: None,
route: RouteInfo::GetUnresolvedIncidents,
})?;
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
match map.remove("incidents") {
Some(v) => serde_json::from_value::<Vec<Incident>>(v)
.map_err(From::from),
None => Ok(vec![]),
}
}
pub fn get_upcoming_maintenances() -> Result<Vec<Maintenance>> {
let response = request(Request {
body: None,
headers: None,
route: RouteInfo::GetUpcomingMaintenances,
})?;
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
match map.remove("scheduled_maintenances") {
Some(v) => serde_json::from_value::<Vec<Maintenance>>(v)
.map_err(From::from),
None => Ok(vec![]),
}
}
pub fn get_user(user_id: u64) -> Result<User> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetUser { user_id },
})
}
pub fn get_user_dm_channels() -> Result<Vec<PrivateChannel>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetUserDmChannels,
})
}
pub fn get_voice_regions() -> Result<Vec<VoiceRegion>> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetVoiceRegions,
})
}
pub fn get_webhook(webhook_id: u64) -> Result<Webhook> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetWebhook { webhook_id },
})
}
pub fn get_webhook_with_token(webhook_id: u64, token: &str) -> Result<Webhook> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::GetWebhookWithToken { token, webhook_id },
})
}
pub fn kick_member(guild_id: u64, user_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::KickMember { guild_id, user_id },
})
}
pub fn leave_group(group_id: u64) -> Result<Group> {
fire(Request {
body: None,
headers: None,
route: RouteInfo::LeaveGroup { group_id },
})
}
pub fn leave_guild(guild_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::LeaveGuild { guild_id },
})
}
pub fn remove_group_recipient(group_id: u64, user_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::RemoveGroupRecipient { group_id, user_id },
})
}
pub fn send_files<'a, T, It: IntoIterator<Item=T>>(channel_id: u64, files: It, map: JsonMap) -> Result<Message>
where T: Into<AttachmentType<'a>> {
let uri = api!("/channels/{}/messages", channel_id);
let url = match Url::parse(&uri) {
Ok(url) => url,
Err(_) => return Err(Error::Url(uri)),
};
let tc = NativeTlsClient::new()?;
let connector = HttpsConnector::new(tc);
let mut request = HyperRequest::with_connector(Method::Post, url, &connector)?;
request
.headers_mut()
.set(header::Authorization(TOKEN.lock().clone()));
request
.headers_mut()
.set(header::UserAgent(constants::USER_AGENT.to_string()));
let mut request = Multipart::from_request(request)?;
let mut file_num = "0".to_string();
for file in files {
match file.into() {
AttachmentType::Bytes((mut bytes, filename)) => {
request
.write_stream(&file_num, &mut bytes, Some(filename), None)?;
},
AttachmentType::File((mut f, filename)) => {
request
.write_stream(&file_num, &mut f, Some(filename), None)?;
},
AttachmentType::Path(p) => {
request.write_file(&file_num, &p)?;
},
}
unsafe {
let vec = file_num.as_mut_vec();
vec[0] += 1;
}
}
for (k, v) in map {
match v {
Value::Bool(false) => request.write_text(&k, "false")?,
Value::Bool(true) => request.write_text(&k, "true")?,
Value::Number(inner) => request.write_text(&k, inner.to_string())?,
Value::String(inner) => request.write_text(&k, inner)?,
Value::Object(inner) => request.write_text(&k, serde_json::to_string(&inner)?)?,
_ => continue,
};
}
let response = request.send()?;
if response.status.class() != StatusClass::Success {
return Err(Error::Http(HttpError::UnsuccessfulRequest(response)));
}
serde_json::from_reader(response).map_err(From::from)
}
pub fn send_message(channel_id: u64, map: &Value) -> Result<Message> {
let body = serde_json::to_vec(map)?;
fire(Request {
body: Some(&body),
headers: None,
route: RouteInfo::CreateMessage { channel_id },
})
}
pub fn pin_message(channel_id: u64, message_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::PinMessage { channel_id, message_id },
})
}
pub fn remove_ban(guild_id: u64, user_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::RemoveBan { guild_id, user_id },
})
}
pub fn remove_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::RemoveMemberRole { guild_id, user_id, role_id },
})
}
pub fn start_guild_prune(guild_id: u64, map: &Value) -> Result<GuildPrune> {
#[derive(Deserialize)]
struct StartGuildPruneRequest {
days: u64,
}
let req = serde_json::from_value::<StartGuildPruneRequest>(map.clone())?;
fire(Request {
body: None,
headers: None,
route: RouteInfo::StartGuildPrune {
days: req.days,
guild_id,
},
})
}
pub fn start_integration_sync(guild_id: u64, integration_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::StartIntegrationSync { guild_id, integration_id },
})
}
pub fn unpin_message(channel_id: u64, message_id: u64) -> Result<()> {
wind(204, Request {
body: None,
headers: None,
route: RouteInfo::UnpinMessage { channel_id, message_id },
})
}
pub fn fire<T: DeserializeOwned>(req: Request) -> Result<T> {
let response = request(req)?;
serde_json::from_reader(response).map_err(From::from)
}
pub fn request(req: Request) -> Result<HyperResponse> {
let response = ratelimiting::perform(req)?;
if response.status.class() == StatusClass::Success {
Ok(response)
} else {
Err(Error::Http(HttpError::UnsuccessfulRequest(response)))
}
}
fn retry(request: &Request) -> HyperResult<HyperResponse> {
for _ in 0..3 {
match request.build().send() {
Err(HyperError::Io(ref io))
if io.kind() == IoErrorKind::ConnectionAborted => continue,
other => return other,
}
}
request.build().send()
}
fn wind(expected: u16, req: Request) -> Result<()> {
let resp = request(req)?;
if resp.status.to_u16() == expected {
return Ok(());
}
debug!("Expected {}, got {}", expected, resp.status);
trace!("Unsuccessful response: {:?}", resp);
Err(Error::Http(HttpError::UnsuccessfulRequest(resp)))
}
pub enum AttachmentType<'a> {
Bytes((&'a [u8], &'a str)),
File((&'a File, &'a str)),
Path(&'a Path),
}
impl<'a> From<(&'a [u8], &'a str)> for AttachmentType<'a> {
fn from(params: (&'a [u8], &'a str)) -> AttachmentType { AttachmentType::Bytes(params) }
}
impl<'a> From<&'a str> for AttachmentType<'a> {
fn from(s: &'a str) -> AttachmentType { AttachmentType::Path(Path::new(s)) }
}
impl<'a> From<&'a Path> for AttachmentType<'a> {
fn from(path: &'a Path) -> AttachmentType {
AttachmentType::Path(path)
}
}
impl<'a> From<&'a PathBuf> for AttachmentType<'a> {
fn from(pathbuf: &'a PathBuf) -> AttachmentType { AttachmentType::Path(pathbuf.as_path()) }
}
impl<'a> From<(&'a File, &'a str)> for AttachmentType<'a> {
fn from(f: (&'a File, &'a str)) -> AttachmentType<'a> { AttachmentType::File((f.0, f.1)) }
}
pub enum GuildPagination {
After(GuildId),
Before(GuildId),
}
#[cfg(test)]
mod test {
use super::AttachmentType;
use std::path::Path;
#[test]
fn test_attachment_type() {
assert!(match AttachmentType::from(Path::new("./dogs/corgis/kona.png")) {
AttachmentType::Path(_) => true,
_ => false,
});
assert!(match AttachmentType::from("./cats/copycat.png") {
AttachmentType::Path(_) => true,
_ => false,
});
}
}