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
#[cfg(feature = "model")]
use reqwest::Client as ReqwestClient;
use crate::internal::is_false;
#[cfg(feature = "model")]
use crate::internal::prelude::*;
use crate::model::id::AttachmentId;
/// A file uploaded with a message. Not to be confused with [`Embed`]s.
///
/// [`Embed`]: super::Embed
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct Attachment {
/// The unique ID given to this attachment.
pub id: AttachmentId,
/// The filename of the file that was uploaded. This is equivalent to what
/// the uploader had their file named.
pub filename: String,
/// If the attachment is an image, then the height of the image is provided.
pub height: Option<u64>,
/// The proxy URL.
pub proxy_url: String,
/// The size of the file in bytes.
pub size: u64,
/// The URL of the uploaded attachment.
pub url: String,
/// If the attachment is an image, then the width of the image is provided.
pub width: Option<u64>,
/// The attachment's [media type].
///
/// [media type]: https://en.wikipedia.org/wiki/Media_type
pub content_type: Option<String>,
/// Whether this attachment is ephemeral.
///
/// Ephemeral attachments will automatically be removed after a set period of time.
///
/// Ephemeral attachments on messages are guaranteed to be available as long as
/// the message itself exists.
#[serde(default, skip_serializing_if = "is_false")]
pub ephemeral: bool,
}
#[cfg(feature = "model")]
impl Attachment {
/// If this attachment is an image, then a tuple of the width and height
/// in pixels is returned.
pub fn dimensions(&self) -> Option<(u64, u64)> {
self.width.and_then(|width| self.height.map(|height| (width, height)))
}
/// Downloads the attachment, returning back a vector of bytes.
///
/// # Examples
///
/// Download all of the attachments associated with a [`Message`]:
///
/// ```rust,no_run
/// # #[cfg(feature = "client")]
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// use std::io::Write;
/// use std::path::Path;
///
/// use serenity::model::prelude::*;
/// use serenity::prelude::*;
/// use tokio::fs::File;
/// use tokio::io::AsyncWriteExt;
///
/// struct Handler;
///
/// #[serenity::async_trait]
/// impl EventHandler for Handler {
/// async fn message(&self, context: Context, mut message: Message) {
/// for attachment in message.attachments {
/// let content = match attachment.download().await {
/// Ok(content) => content,
/// Err(why) => {
/// println!("Error downloading attachment: {:?}", why);
/// let _ =
/// message.channel_id.say(&context, "Error downloading attachment").await;
///
/// return;
/// },
/// };
///
/// let mut file = match File::create(&attachment.filename).await {
/// Ok(file) => file,
/// Err(why) => {
/// println!("Error creating file: {:?}", why);
/// let _ = message.channel_id.say(&context, "Error creating file").await;
///
/// return;
/// },
/// };
///
/// if let Err(why) = file.write_all(&content).await {
/// println!("Error writing to file: {:?}", why);
///
/// return;
/// }
///
/// let _ = message
/// .channel_id
/// .say(&context, &format!("Saved {:?}", attachment.filename))
/// .await;
/// }
/// }
///
/// async fn ready(&self, _: Context, ready: Ready) {
/// println!("{} is connected!", ready.user.name);
/// }
/// }
/// let token = std::env::var("DISCORD_TOKEN")?;
/// let mut client = Client::builder(&token).event_handler(Handler).await?;
///
/// client.start().await?;
/// # Ok(())
/// # }
/// ```
///
/// # Errors
///
/// Returns an [`Error::Io`] when there is a problem reading the contents
/// of the HTTP response.
///
/// Returns an [`Error::Http`] when there is a problem retrieving the
/// attachment.
///
/// [`Error::Http`]: crate::Error::Http
/// [`Error::Io`]: crate::Error::Io
/// [`Message`]: super::Message
pub async fn download(&self) -> Result<Vec<u8>> {
let reqwest = ReqwestClient::new();
let bytes = reqwest.get(&self.url).send().await?.bytes().await?;
Ok(bytes.to_vec())
}
}