serenity/model/channel/embed.rs
1use crate::model::{Colour, Timestamp};
2
3/// Represents a rich embed which allows using richer markdown, multiple fields and more. This was
4/// heavily inspired by [slack's attachments].
5///
6/// You can include an attachment in your own message by a user or a bot, or in a webhook.
7///
8/// **Note**: Maximum amount of characters you can put is 256 in a field name,
9/// 1024 in a field value, and 2048 in a description.
10///
11/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object).
12///
13/// [slack's attachments]: https://api.slack.com/docs/message-attachments
14#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
15#[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq)]
16#[non_exhaustive]
17pub struct Embed {
18 /// The title of the embed.
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub title: Option<String>,
21 /// The type of the embed. For embeds not generated by Discord's backend, this will always be
22 /// "rich".
23 #[serde(rename = "type")]
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub kind: Option<String>,
26 /// The description of the embed.
27 ///
28 /// The maximum value for this field is 2048 unicode codepoints.
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub description: Option<String>,
31 /// The URL of the embed.
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub url: Option<String>,
34 /// Timestamp information.
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub timestamp: Option<Timestamp>,
37 /// The colour code of the embed.
38 #[serde(rename = "color")]
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub colour: Option<Colour>,
41 /// Footer information for the embed.
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub footer: Option<EmbedFooter>,
44 /// Image information of the embed.
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub image: Option<EmbedImage>,
47 /// Thumbnail information of the embed.
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub thumbnail: Option<EmbedThumbnail>,
50 /// The embed's video information.
51 ///
52 /// This is present if the [`Self::kind`] is `"video"`.
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub video: Option<EmbedVideo>,
55 /// Provider information for the embed.
56 ///
57 /// For example, if the embed [`Self::kind`] is `"video"`, the provider might contain
58 /// information about YouTube.
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub provider: Option<EmbedProvider>,
61 /// Information about the author of the embed.
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub author: Option<EmbedAuthor>,
64 /// The array of fields.
65 ///
66 /// The maximum number of fields is 25.
67 #[serde(default)]
68 #[serde(skip_serializing_if = "Vec::is_empty")]
69 pub fields: Vec<EmbedField>,
70}
71
72/// An author object in an embed.
73///
74/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure).
75#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
76#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
77#[non_exhaustive]
78pub struct EmbedAuthor {
79 /// The name of the author.
80 pub name: String,
81 /// The URL of the author.
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub url: Option<String>,
84 /// The URL of the author icon.
85 ///
86 /// This only supports HTTP(S) and attachments.
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub icon_url: Option<String>,
89 /// A proxied URL of the author icon.
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub proxy_icon_url: Option<String>,
92}
93
94/// A field object in an embed.
95///
96/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure).
97#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
98#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
99#[non_exhaustive]
100pub struct EmbedField {
101 /// The name of the field.
102 ///
103 /// The maximum length of this field is 512 unicode codepoints.
104 pub name: String,
105 /// The value of the field.
106 ///
107 /// The maximum length of this field is 1024 unicode codepoints.
108 pub value: String,
109 /// Indicator of whether the field should display as inline.
110 #[serde(default)]
111 pub inline: bool,
112}
113
114impl EmbedField {
115 /// Creates a new embed field.
116 ///
117 /// **Note**: Refer to the [`Self::name`] and [`Self::value`] documentation for maximum
118 /// lengths.
119 pub fn new<T, U>(name: T, value: U, inline: bool) -> Self
120 where
121 T: Into<String>,
122 U: Into<String>,
123 {
124 Self::new_(name.into(), value.into(), inline)
125 }
126
127 pub(crate) const fn new_(name: String, value: String, inline: bool) -> Self {
128 Self {
129 name,
130 value,
131 inline,
132 }
133 }
134}
135
136/// Footer information for an embed.
137///
138/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure).
139#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
140#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
141#[non_exhaustive]
142pub struct EmbedFooter {
143 /// The associated text with the footer.
144 pub text: String,
145 /// The URL of the footer icon.
146 ///
147 /// This only supports HTTP(S) and attachments.
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub icon_url: Option<String>,
150 /// A proxied URL of the footer icon.
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub proxy_icon_url: Option<String>,
153}
154
155/// An image object in an embed.
156///
157/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure).
158#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
159#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
160#[non_exhaustive]
161pub struct EmbedImage {
162 /// Source URL of the image.
163 ///
164 /// This only supports HTTP(S) and attachments.
165 pub url: String,
166 /// A proxied URL of the image.
167 pub proxy_url: Option<String>,
168 /// The height of the image.
169 pub height: Option<u32>,
170 /// The width of the image.
171 pub width: Option<u32>,
172}
173
174/// The provider of an embed.
175///
176/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-provider-structure).
177#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
178#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
179#[non_exhaustive]
180pub struct EmbedProvider {
181 /// The name of the provider.
182 pub name: Option<String>,
183 /// The URL of the provider.
184 pub url: Option<String>,
185}
186
187/// The dimensions and URL of an embed thumbnail.
188///
189/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure).
190#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
191#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
192#[non_exhaustive]
193pub struct EmbedThumbnail {
194 /// The source URL of the thumbnail.
195 ///
196 /// This only supports HTTP(S) and attachments.
197 pub url: String,
198 /// A proxied URL of the thumbnail.
199 pub proxy_url: Option<String>,
200 /// The height of the thumbnail in pixels.
201 pub height: Option<u32>,
202 /// The width of the thumbnail in pixels.
203 pub width: Option<u32>,
204}
205
206/// Video information for an embed.
207///
208/// [Discord docs](https://discord.com/developers/docs/resources/channel#embed-object-embed-video-structure).
209#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
210#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
211#[non_exhaustive]
212pub struct EmbedVideo {
213 /// The source URL of the video.
214 pub url: String,
215 /// A proxied URL of the thumbnail.
216 pub proxy_url: Option<String>,
217 /// The height of the video in pixels.
218 pub height: Option<u32>,
219 /// The width of the video in pixels.
220 pub width: Option<u32>,
221}