Skip to content

Commit d21b2bf

Browse files
committed
fix builders, test, fixedstring, type resolved variant of media items
1 parent 3a7a956 commit d21b2bf

File tree

4 files changed

+98
-51
lines changed

4 files changed

+98
-51
lines changed

examples/e01_basic_ping_bot/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,5 @@ authors = ["my name <[email protected]>"]
55
edition.workspace = true
66

77
[dependencies]
8-
serde_json = "1.0.139"
98
serenity = { path = "../../", default-features = false, features = ["gateway", "model", "rustls_backend"] }
10-
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
11-
tracing-subscriber = "0.3.19"
9+
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }

examples/e01_basic_ping_bot/src/main.rs

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,64 +7,51 @@ struct Handler;
77

88
#[async_trait]
99
impl EventHandler for Handler {
10+
// Set a handler for the `message` event. This is called whenever a new message is received.
11+
//
12+
// Event handlers are dispatched through a threadpool, and so multiple events can be dispatched
13+
// simultaneously.
1014
async fn message(&self, ctx: Context, msg: Message) {
11-
if msg.content == "mtest" {
12-
let display = serenity::all::CreateTextDisplay::new("test");
13-
let thumbnail = serenity::all::CreateThumbnail::new(serenity::all::CreateUnfurledMediaItem::new("https://cdn.discordapp.com/avatars/158567567487795200/9aa2a84b9c7b3b147ebe88e799e535d3.webp?size=4096"));
14-
15-
let section = serenity::all::CreateSection::new(
16-
vec![serenity::all::CreateSectionComponent::TextDisplay(display)],
17-
serenity::all::CreateSectionAccessory::Thumbnail(thumbnail),
18-
);
19-
20-
let components = serenity::all::CreateComponent::Section(section);
21-
22-
let builder = serenity::all::CreateMessage::new()
23-
.flags(serenity::all::MessageFlags::IS_COMPONENTS_V2)
24-
.components(vec![components]);
25-
26-
if let Err(why) = msg.channel_id.send_message(&ctx.http, builder).await {
15+
if msg.content == "!ping" {
16+
// Sending a message can fail, due to a network error, an authentication error, or lack
17+
// of permissions to post in the channel, so log to stdout when some error happens,
18+
// with a description of it.
19+
if let Err(why) = msg.channel_id.say(&ctx.http, "Pong!").await {
2720
println!("Error sending message: {why:?}");
2821
}
2922
}
3023
}
3124

25+
// Set a handler to be called on the `ready` event. This is called when a shard is booted, and
26+
// a READY payload is sent by Discord. This payload contains data like the current user's guild
27+
// Ids, current user data, private channels, and more.
28+
//
29+
// In this case, just print what the current user's username is.
3230
async fn ready(&self, _: Context, ready: Ready) {
3331
println!("{} is connected!", ready.user.name);
34-
35-
let display = serenity::all::CreateTextDisplay::new("test");
36-
let button = serenity::all::CreateButton::new("h").label("test");
37-
38-
let section = serenity::all::CreateSection::new(
39-
vec![serenity::all::CreateSectionComponent::TextDisplay(display)],
40-
serenity::all::CreateSectionAccessory::Button(button),
41-
);
42-
43-
let components = serenity::all::CreateComponent::Section(section);
44-
45-
let builder = serenity::all::CreateMessage::new()
46-
.flags(serenity::all::MessageFlags::IS_COMPONENTS_V2)
47-
.components(vec![components]);
48-
49-
let json = serde_json::to_string_pretty(&builder).unwrap();
50-
51-
println!("{json}");
5232
}
5333
}
5434

5535
#[tokio::main]
5636
async fn main() {
57-
tracing_subscriber::fmt::init();
37+
// Configure the client with your Discord bot token in the environment.
5838
let token =
5939
Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment");
40+
// Set gateway intents, which decides what events the bot will be notified about
6041
let intents = GatewayIntents::GUILD_MESSAGES
6142
| GatewayIntents::DIRECT_MESSAGES
6243
| GatewayIntents::MESSAGE_CONTENT;
6344

45+
// Create a new instance of the Client, logging in as a bot. This will automatically prepend
46+
// your bot token with "Bot ", which is a requirement by Discord for bot users.
6447
let mut client =
6548
Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client");
6649

50+
// Finally, start a single shard, and start listening to events.
51+
//
52+
// Shards will automatically attempt to reconnect, and will perform exponential backoff until
53+
// it reconnects.
6754
if let Err(why) = client.start().await {
6855
println!("Client error: {why:?}");
6956
}
70-
}
57+
}

src/builder/create_components.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ pub enum CreateComponent<'a> {
6565
ActionRow(CreateActionRow<'a>),
6666
/// A section, V2 component.
6767
Section(CreateSection<'a>),
68+
TextDisplay(CreateTextDisplay<'a>),
69+
Thumbnail(CreateThumbnail<'a>),
70+
MediaGallery(CreateMediaGallery<'a>),
71+
File(CreateFile<'a>),
72+
Separator(CreateSeparator),
73+
Container(CreateContainer<'a>),
6874
}
6975

7076
#[derive(Clone, Debug, Serialize)]
@@ -78,7 +84,6 @@ pub struct CreateSection<'a> {
7884
}
7985

8086
impl<'a> CreateSection<'a> {
81-
// TODO: change type
8287
pub fn new(
8388
components: impl Into<Cow<'a, [CreateSectionComponent<'a>]>>,
8489
accessory: CreateSectionAccessory<'a>,
@@ -190,18 +195,18 @@ impl<'a> CreateUnfurledMediaItem<'a> {
190195
pub struct CreateMediaGallery<'a> {
191196
#[serde(rename = "type")]
192197
kind: StaticU8<12>,
193-
items: Cow<'a, [CreateSectionComponent<'a>]>,
198+
items: Cow<'a, [CreateMediaGalleryItem<'a>]>,
194199
}
195200

196201
impl<'a> CreateMediaGallery<'a> {
197-
pub fn new(items: impl Into<Cow<'a, [CreateSectionComponent<'a>]>>) -> Self {
202+
pub fn new(items: impl Into<Cow<'a, [CreateMediaGalleryItem<'a>]>>) -> Self {
198203
CreateMediaGallery {
199204
kind: StaticU8::<12>,
200205
items: items.into(),
201206
}
202207
}
203208

204-
pub fn items(mut self, items: impl Into<Cow<'a, [CreateSectionComponent<'a>]>>) -> Self {
209+
pub fn items(mut self, items: impl Into<Cow<'a, [CreateMediaGalleryItem<'a>]>>) -> Self {
205210
self.items = items.into();
206211
self
207212
}
@@ -217,6 +222,31 @@ pub struct CreateMediaGalleryItem<'a> {
217222
spoiler: Option<bool>,
218223
}
219224

225+
impl<'a> CreateMediaGalleryItem<'a> {
226+
pub fn new(media: CreateUnfurledMediaItem<'a>) -> Self {
227+
CreateMediaGalleryItem {
228+
media,
229+
description: None,
230+
spoiler: None,
231+
}
232+
}
233+
234+
pub fn media(mut self, media: CreateUnfurledMediaItem<'a>) -> Self {
235+
self.media = media;
236+
self
237+
}
238+
239+
pub fn description(mut self, description: impl Into<Cow<'a, str>>) -> Self {
240+
self.description = Some(description.into());
241+
self
242+
}
243+
244+
pub fn spoiler(mut self, spoiler: bool) -> Self {
245+
self.spoiler = Some(spoiler);
246+
self
247+
}
248+
}
249+
220250
#[derive(Clone, Debug, Serialize)]
221251
#[must_use]
222252
pub struct CreateFile<'a> {

src/model/application/component.rs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use nonmax::NonMaxU32;
12
use serde::de::Error as DeError;
23
use serde::ser::{Serialize, Serializer};
34
use serde_json::from_value;
@@ -52,10 +53,6 @@ pub enum Component {
5253

5354
// TODO: add something like this to every variant.
5455
// The component type, it will always be [`ComponentType::Thing`].
55-
// #[serde(rename = "type")]
56-
// pub kind: ComponentType,
57-
58-
// TODO: use fixedstring is places i missed when i find suitable lengths
5956

6057
// Define the macro to implement Deserialize
6158
macro_rules! impl_deserialize_component {
@@ -119,15 +116,50 @@ pub struct Section {
119116
#[non_exhaustive]
120117
pub struct Thumbnail {
121118
media: UnfurledMediaItem,
122-
description: Option<String>,
119+
description: Option<FixedString<u16>>,
123120
spoiler: Option<bool>,
124121
}
125122

123+
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
124+
#[derive(Clone, Debug, Deserialize, Serialize)]
125+
#[non_exhaustive]
126+
#[serde(untagged)]
127+
pub enum MediaItem {
128+
Resolved(ResolvedUnfurledMediaItem),
129+
Unresolved(UnfurledMediaItem),
130+
}
131+
126132
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
127133
#[derive(Clone, Debug, Deserialize, Serialize)]
128134
#[non_exhaustive]
129135
pub struct UnfurledMediaItem {
130-
url: String,
136+
url: FixedString<u16>,
137+
}
138+
139+
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
140+
#[derive(Clone, Debug, Deserialize, Serialize)]
141+
#[non_exhaustive]
142+
pub struct ResolvedUnfurledMediaItem {
143+
url: FixedString<u16>,
144+
proxy_url: FixedString<u16>,
145+
width: NonMaxU32,
146+
height: NonMaxU32,
147+
content_type: FixedString,
148+
loading_state: UnfurledMediaItemLoadingState,
149+
}
150+
151+
enum_number! {
152+
/// The loading state of the media item.
153+
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
154+
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
155+
#[non_exhaustive]
156+
pub enum UnfurledMediaItemLoadingState {
157+
DiscordUnknown = 0,
158+
Loading = 1,
159+
LoadingSuccess = 2,
160+
LoadingNotFound = 3,
161+
_ => Unknown(u8),
162+
}
131163
}
132164

133165
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
@@ -158,7 +190,7 @@ pub enum SectionComponent {
158190
#[derive(Clone, Debug, Deserialize, Serialize)]
159191
#[non_exhaustive]
160192
pub struct TextDisplay {
161-
content: String,
193+
content: FixedString<u16>,
162194
}
163195

164196
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
@@ -173,7 +205,7 @@ pub struct MediaGallery {
173205
#[non_exhaustive]
174206
pub struct MediaGalleryItem {
175207
media: UnfurledMediaItem,
176-
description: Option<String>,
208+
description: Option<FixedString<u16>>,
177209
spoiler: Option<bool>,
178210
}
179211

0 commit comments

Comments
 (0)