Skip to main content

fluxer/model/
mod.rs

1//! Typed structs for everything the Fluxer API returns -- users, guilds,
2//! channels, messages, embeds, and all the gateway event payloads.
3//!
4//! Most fields are `Option<T>` because the API doesn't always include
5//! everything depending on the endpoint.
6
7pub mod voice;
8use serde::{Deserialize, Serialize};
9
10/// All entity IDs in the Fluxer API are snowflake strings.
11pub type Snowflake = String;
12
13/// A Fluxer user account. Both bot and human accounts share this struct.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct User {
16    pub id: Snowflake,
17    #[serde(default)]
18    pub username: String,
19    pub discriminator: Option<String>,
20    pub avatar: Option<String>,
21    pub banner: Option<String>,
22    #[serde(default)]
23    pub bot: Option<bool>,
24    #[serde(default)]
25    pub system: Option<bool>,
26    pub public_flags: Option<u64>,
27    pub premium: Option<u64>,
28}
29
30/// A guild (server). Full data arrives via `GUILD_CREATE`; [`UnavailableGuild`] is used before then.
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct Guild {
33    pub id: Snowflake,
34    pub name: Option<String>,
35    pub icon: Option<String>,
36    pub banner: Option<String>,
37    pub splash: Option<String>,
38    pub owner_id: Option<Snowflake>,
39    pub afk_channel_id: Option<Snowflake>,
40    /// AFK timeout in seconds.
41    pub afk_timeout: Option<u64>,
42    pub verification_level: Option<u64>,
43    /// 0 = all messages, 1 = only mentions.
44    pub default_message_notifications: Option<u64>,
45    pub explicit_content_filter: Option<u64>,
46    pub roles: Option<Vec<Role>>,
47    pub emojis: Option<Vec<Emoji>>,
48    pub features: Option<Vec<String>>,
49    pub member_count: Option<u64>,
50    pub max_members: Option<u64>,
51    pub description: Option<String>,
52    pub preferred_locale: Option<String>,
53    pub vanity_url_code: Option<String>,
54}
55
56/// A guild member. Wraps a [`User`] with guild-specific info like nickname and roles.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct Member {
59    pub user: Option<User>,
60    pub nick: Option<String>,
61    pub avatar: Option<String>,
62    pub roles: Vec<Snowflake>,
63    pub joined_at: String,
64    pub deaf: Option<bool>,
65    pub mute: Option<bool>,
66    pub pending: Option<bool>,
67    pub permissions: Option<String>,
68    /// ISO 8601 timestamp. If set, the member is timed out until then.
69    pub communication_disabled_until: Option<String>,
70}
71
72impl Member {
73    /// Returns the member's nickname if they have one, otherwise their username.
74    pub fn display_name(&self) -> &str {
75        if let Some(nick) = &self.nick {
76            return nick.as_str();
77        }
78        self.user
79            .as_ref()
80            .map(|u| u.username.as_str())
81            .unwrap_or("")
82    }
83}
84
85/// A guild role.
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct Role {
88    pub id: Snowflake,
89    pub name: String,
90    /// Integer color value. 0 means no color.
91    pub color: Option<u64>,
92    /// Whether this role shows up separately in the member list.
93    pub hoist: Option<bool>,
94    pub icon: Option<String>,
95    pub position: Option<i64>,
96    /// Permission bitfield.
97    pub permissions: Option<String>,
98    /// Managed roles are created by integrations (bot roles, booster role, etc).
99    pub managed: Option<bool>,
100    pub mentionable: Option<bool>,
101}
102
103/// Custom or standard emoji. Custom emojis have both `name` and `id`,
104/// standard unicode emojis only have `name`.
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct Emoji {
107    pub id: Option<Snowflake>,
108    pub name: Option<String>,
109    pub roles: Option<Vec<Snowflake>>,
110    pub user: Option<User>,
111    pub require_colons: Option<bool>,
112    pub managed: Option<bool>,
113    pub animated: Option<bool>,
114    pub available: Option<bool>,
115}
116
117impl Emoji {
118    /// Formats the emoji for use in reaction endpoints. Returns `"name:id"` for
119    /// custom emojis or just the name for unicode ones.
120    pub fn to_reaction_string(&self) -> String {
121        match (&self.name, &self.id) {
122            (Some(name), Some(id)) => format!("{}:{}", name, id),
123            (Some(name), None) => name.clone(),
124            _ => String::new(),
125        }
126    }
127}
128
129/// A channel — text, voice, DM, category, etc. Check `kind` to tell them apart.
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct Channel {
132    pub id: Snowflake,
133    /// Channel type, see [`ChannelType`] for values.
134    #[serde(rename = "type")]
135    pub kind: Option<u8>,
136    pub guild_id: Option<Snowflake>,
137    pub position: Option<i64>,
138    pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
139    pub name: Option<String>,
140    pub topic: Option<String>,
141    pub nsfw: Option<bool>,
142    pub last_message_id: Option<Snowflake>,
143    /// Bits per second, for voice channels.
144    pub bitrate: Option<u64>,
145    /// 0 = unlimited.
146    pub user_limit: Option<u64>,
147    /// Slowmode, in seconds.
148    pub rate_limit_per_user: Option<u64>,
149    /// Recipients for DM/group DM channels.
150    pub recipients: Option<Vec<User>>,
151    pub icon: Option<String>,
152    pub owner_id: Option<Snowflake>,
153    pub parent_id: Option<Snowflake>,
154    pub last_pin_timestamp: Option<String>,
155}
156
157/// Numeric channel type values that map to [`Channel::kind`].
158#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
159#[repr(u8)]
160pub enum ChannelType {
161    Text = 0,
162    Dm = 1,
163    Voice = 2,
164    GroupDm = 3,
165    Category = 4,
166    Announcement = 5,
167    Stage = 13,
168}
169
170/// Permission overwrite for a channel. `kind` is 0 for role, 1 for member.
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct PermissionOverwrite {
173    pub id: Snowflake,
174    #[serde(rename = "type")]
175    pub kind: u8,
176    pub allow: Option<String>,
177    pub deny: Option<String>,
178}
179
180/// A message sent in a channel.
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct Message {
183    pub id: Snowflake,
184    pub channel_id: Option<Snowflake>,
185    pub guild_id: Option<Snowflake>,
186    pub author: User,
187    /// Only present in guild messages.
188    pub member: Option<Member>,
189    pub content: Option<String>,
190    pub timestamp: Option<String>,
191    pub edited_timestamp: Option<String>,
192    pub tts: Option<bool>,
193    pub mention_everyone: Option<bool>,
194    pub mentions: Option<Vec<User>>,
195    pub mention_roles: Option<Vec<Snowflake>>,
196    pub attachments: Option<Vec<Attachment>>,
197    pub embeds: Option<Vec<Embed>>,
198    pub reactions: Option<Vec<Reaction>>,
199    pub pinned: Option<bool>,
200    pub webhook_id: Option<Snowflake>,
201    /// 0 = default, 19 = reply, etc.
202    #[serde(rename = "type")]
203    pub kind: Option<u8>,
204    pub referenced_message: Option<Box<Message>>,
205    pub flags: Option<u64>,
206    pub stickers: Option<Vec<serde_json::Value>>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct PinnedMessage {
211    pub message: Message,
212    pub pinned_at: Option<String>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct PinsResponse {
217    pub items: Vec<PinnedMessage>,
218    pub has_more: Option<bool>,
219}
220
221/// A file attached to a message.
222#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct Attachment {
224    pub id: Snowflake,
225    pub filename: Option<String>,
226    pub description: Option<String>,
227    pub content_type: Option<String>,
228    pub size: Option<u64>,
229    pub url: Option<String>,
230    pub proxy_url: Option<String>,
231    pub height: Option<u64>,
232    pub width: Option<u64>,
233    pub ephemeral: Option<bool>,
234}
235
236/// A file to attach when sending a message. Pass one or more of these to
237/// [`Http::send_files`] or [`Http::send_message_with_files`].
238pub struct AttachmentFile {
239    /// Filename shown in the client, e.g. `"image.png"`.
240    pub filename: String,
241    pub data: Vec<u8>,
242    /// MIME type, e.g. `"image/png"`. If `None`, defaults to `"application/octet-stream"`.
243    pub content_type: Option<String>,
244}
245
246/// Rich embed. Use [`EmbedBuilder`] to construct these.
247#[derive(Debug, Clone, Serialize, Deserialize, Default)]
248pub struct Embed {
249    pub title: Option<String>,
250    #[serde(rename = "type")]
251    pub kind: Option<String>,
252    pub description: Option<String>,
253    pub url: Option<String>,
254    pub timestamp: Option<String>,
255    /// Sidebar color as an integer, e.g. `0xFF0000` for red.
256    pub color: Option<u64>,
257    pub footer: Option<EmbedFooter>,
258    pub image: Option<EmbedMedia>,
259    pub thumbnail: Option<EmbedMedia>,
260    pub video: Option<EmbedMedia>,
261    pub author: Option<EmbedAuthor>,
262    pub fields: Option<Vec<EmbedField>>,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct EmbedFooter {
267    pub text: String,
268    pub icon_url: Option<String>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct EmbedMedia {
273    pub url: String,
274    pub height: Option<u64>,
275    pub width: Option<u64>,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct EmbedAuthor {
280    pub name: String,
281    pub url: Option<String>,
282    pub icon_url: Option<String>,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct EmbedField {
287    pub name: String,
288    pub value: String,
289    /// If true, fields can sit side-by-side.
290    #[serde(default)]
291    pub inline: bool,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct Reaction {
296    pub count: u64,
297    pub me: bool,
298    pub emoji: Emoji,
299}
300
301/// Builder for [`Embed`]. Chain methods and call `.build()` at the end.
302///
303/// ```rust
304/// use fluxer::prelude::*;
305///
306/// let embed = EmbedBuilder::new()
307///     .title("Hello")
308///     .description("World")
309///     .color(0x00FF00)
310///     .build();
311/// ```
312#[derive(Debug, Default)]
313pub struct EmbedBuilder(Embed);
314
315impl EmbedBuilder {
316    pub fn new() -> Self {
317        Self::default()
318    }
319
320    pub fn title(mut self, title: impl Into<String>) -> Self {
321        self.0.title = Some(title.into());
322        self
323    }
324    pub fn description(mut self, desc: impl Into<String>) -> Self {
325        self.0.description = Some(desc.into());
326        self
327    }
328    pub fn url(mut self, url: impl Into<String>) -> Self {
329        self.0.url = Some(url.into());
330        self
331    }
332    pub fn color(mut self, color: u64) -> Self {
333        self.0.color = Some(color);
334        self
335    }
336    pub fn timestamp(mut self, ts: impl Into<String>) -> Self {
337        self.0.timestamp = Some(ts.into());
338        self
339    }
340    pub fn footer(mut self, text: impl Into<String>, icon_url: Option<String>) -> Self {
341        self.0.footer = Some(EmbedFooter { text: text.into(), icon_url });
342        self
343    }
344    pub fn image(mut self, url: impl Into<String>) -> Self {
345        self.0.image = Some(EmbedMedia { url: url.into(), height: None, width: None });
346        self
347    }
348    pub fn thumbnail(mut self, url: impl Into<String>) -> Self {
349        self.0.thumbnail = Some(EmbedMedia { url: url.into(), height: None, width: None });
350        self
351    }
352    pub fn author(mut self, name: impl Into<String>, url: Option<String>, icon_url: Option<String>) -> Self {
353        self.0.author = Some(EmbedAuthor { name: name.into(), url, icon_url });
354        self
355    }
356    pub fn field(mut self, name: impl Into<String>, value: impl Into<String>, inline: bool) -> Self {
357        let fields = self.0.fields.get_or_insert_with(Vec::new);
358        fields.push(EmbedField { name: name.into(), value: value.into(), inline });
359        self
360    }
361    pub fn build(self) -> Embed {
362        self.0
363    }
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct Invite {
368    pub code: String,
369    pub guild: Option<PartialGuild>,
370    pub channel: Option<PartialChannel>,
371    pub inviter: Option<User>,
372    pub target_user: Option<User>,
373    pub approximate_member_count: Option<u64>,
374    pub expires_at: Option<String>,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct PartialGuild {
379    pub id: Snowflake,
380    pub name: String,
381    pub icon: Option<String>,
382    pub splash: Option<String>,
383    pub banner: Option<String>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
387pub struct PartialChannel {
388    pub id: Snowflake,
389    pub name: Option<String>,
390    #[serde(rename = "type")]
391    pub kind: Option<u8>,
392}
393
394/// A webhook. Use [`Http::execute_webhook`](crate::http::Http::execute_webhook) to post through one.
395#[derive(Debug, Clone, Serialize, Deserialize)]
396pub struct Webhook {
397    pub id: Snowflake,
398    #[serde(rename = "type")]
399    pub kind: Option<u8>,
400    pub guild_id: Option<Snowflake>,
401    pub channel_id: Option<Snowflake>,
402    pub user: Option<User>,
403    pub name: Option<String>,
404    pub avatar: Option<String>,
405    pub token: Option<String>,
406    pub url: Option<String>,
407}
408
409// --- Gateway event payloads ---
410
411#[derive(Debug, Clone, Serialize, Deserialize)]
412pub struct TypingStart {
413    pub channel_id: Option<Snowflake>,
414    pub guild_id: Option<Snowflake>,
415    pub user_id: Snowflake,
416    /// Unix timestamp in seconds.
417    pub timestamp: u64,
418    pub member: Option<Member>,
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct ReactionAdd {
423    pub user_id: Snowflake,
424    pub channel_id: Option<Snowflake>,
425    pub message_id: Snowflake,
426    pub guild_id: Option<Snowflake>,
427    pub member: Option<Member>,
428    pub emoji: Emoji,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize)]
432pub struct ReactionRemove {
433    pub user_id: Snowflake,
434    pub channel_id: Option<Snowflake>,
435    pub message_id: Snowflake,
436    pub guild_id: Option<Snowflake>,
437    pub emoji: Emoji,
438}
439
440#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct ReactionRemoveAll {
442    pub channel_id: Option<Snowflake>,
443    pub message_id: Snowflake,
444    pub guild_id: Option<Snowflake>,
445}
446
447#[derive(Debug, Clone, Serialize, Deserialize)]
448pub struct ReactionRemoveEmoji {
449    pub channel_id: Option<Snowflake>,
450    pub guild_id: Option<Snowflake>,
451    pub message_id: Snowflake,
452    pub emoji: Emoji,
453}
454
455/// Partial message data from an edit. Only changed fields are populated.
456#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct MessageUpdate {
458    pub id: Snowflake,
459    pub channel_id: Option<Snowflake>,
460    pub guild_id: Option<Snowflake>,
461    pub author: Option<User>,
462    pub content: Option<String>,
463    pub edited_timestamp: Option<String>,
464    pub embeds: Option<Vec<Embed>>,
465    pub attachments: Option<Vec<Attachment>>,
466    pub pinned: Option<bool>,
467    pub flags: Option<u64>,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct MessageDelete {
472    pub id: Snowflake,
473    pub channel_id: Option<Snowflake>,
474    pub guild_id: Option<Snowflake>,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct MessageDeleteBulk {
479    pub ids: Vec<Snowflake>,
480    pub channel_id: Option<Snowflake>,
481    pub guild_id: Option<Snowflake>,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct GuildMemberAdd {
486    pub guild_id: Snowflake,
487    #[serde(flatten)]
488    pub member: Member,
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct GuildMemberRemove {
493    pub guild_id: Snowflake,
494    pub user: User,
495}
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
498pub struct GuildMemberUpdate {
499    pub guild_id: Snowflake,
500    pub roles: Vec<Snowflake>,
501    pub user: User,
502    pub nick: Option<String>,
503    pub joined_at: Option<String>,
504    pub pending: Option<bool>,
505    pub communication_disabled_until: Option<String>,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct Ban {
510    pub reason: Option<String>,
511    pub user: User,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
515pub struct GuildBanAdd {
516    pub guild_id: Snowflake,
517    pub user: User,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
521pub struct GuildBanRemove {
522    pub guild_id: Snowflake,
523    pub user: User,
524}
525
526#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct GuildRoleCreate {
528    pub guild_id: Snowflake,
529    pub role: Role,
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct GuildRoleUpdate {
534    pub guild_id: Snowflake,
535    pub role: Role,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
539pub struct GuildRoleDelete {
540    pub guild_id: Snowflake,
541    pub role_id: Snowflake,
542}
543
544#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct ChannelDelete {
546    pub id: Snowflake,
547    #[serde(rename = "type")]
548    pub kind: Option<u8>,
549    pub guild_id: Option<Snowflake>,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct ChannelPinsUpdate {
554    pub guild_id: Option<Snowflake>,
555    pub channel_id: Option<Snowflake>,
556    pub last_pin_timestamp: Option<String>,
557}
558
559/// Received once after connecting. Contains the bot user, session info for
560/// resuming, and the initial guild list.
561#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct Ready {
563    pub v: Option<u64>,
564    pub session_id: String,
565    pub resume_gateway_url: Option<String>,
566    pub user: User,
567    /// Guilds come in as unavailable first; full data arrives via GUILD_CREATE events.
568    pub guilds: Option<Vec<UnavailableGuild>>,
569    /// `[shard_id, num_shards]`
570    pub shard: Option<[u64; 2]>,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct UnavailableGuild {
575    pub id: Snowflake,
576    /// `true` = temporary outage, `false`/absent = bot was removed.
577    pub unavailable: Option<bool>,
578}
579
580#[derive(Debug, Deserialize)]
581pub struct GatewayBotResponse {
582    pub url: String,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct GuildEmojisUpdate {
587    pub guild_id: Snowflake,
588    pub emojis: Vec<serde_json::Value>,
589}
590
591#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct GuildStickersUpdate {
593    pub guild_id: Snowflake,
594    pub stickers: Vec<serde_json::Value>,
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
598pub struct GuildRoleUpdateBulk {
599    pub guild_id: Snowflake,
600    pub roles: Vec<serde_json::Value>,
601}
602
603#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct ChannelUpdateBulk {
605    pub guild_id: Option<Snowflake>,
606    pub channels: Vec<serde_json::Value>,
607}
608
609#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct InviteCreate {
611    pub channel_id: Option<Snowflake>,
612    pub guild_id: Option<Snowflake>,
613    pub code: Option<String>,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct InviteDelete {
618    pub channel_id: Option<Snowflake>,
619    pub guild_id: Option<Snowflake>,
620    pub code: String,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize)]
624pub struct WebhooksUpdate {
625    pub channel_id: Snowflake,
626    pub guild_id: Option<Snowflake>,
627}
628
629// --- Request payloads ---
630
631/// Attachment metadata entry for the `attachments` field of [`MessageCreatePayload`].
632/// `id` must match the index used in the corresponding `files[N]` multipart part.
633#[derive(Debug, Clone, Serialize)]
634pub struct AttachmentMetadata {
635    pub id: u64,
636    pub filename: String,
637    #[serde(skip_serializing_if = "Option::is_none")]
638    pub description: Option<String>,
639}
640
641/// Payload for sending/editing messages. All fields optional; only set what you need.
642#[derive(Debug, Clone, Serialize, Default)]
643pub struct MessageCreatePayload {
644    #[serde(skip_serializing_if = "Option::is_none")]
645    pub content: Option<String>,
646    #[serde(skip_serializing_if = "Option::is_none")]
647    pub tts: Option<bool>,
648    #[serde(skip_serializing_if = "Option::is_none")]
649    pub embeds: Option<Vec<Embed>>,
650    #[serde(skip_serializing_if = "Option::is_none")]
651    pub flags: Option<u64>,
652    #[serde(skip_serializing_if = "Option::is_none")]
653    pub message_reference: Option<MessageReference>,
654    #[serde(skip_serializing_if = "Option::is_none")]
655    pub referenced_message_id: Option<Snowflake>,
656    /// Required when uploading files. Populated automatically by
657    /// [`Http::send_message_with_files`]; you don't need to set this manually.
658    #[serde(skip_serializing_if = "Option::is_none")]
659    pub attachments: Option<Vec<AttachmentMetadata>>,
660}
661
662#[derive(Debug, Clone, Serialize)]
663pub struct MessageReference {
664    pub message_id: Snowflake,
665    pub channel_id: Option<Snowflake>,
666    pub guild_id: Option<Snowflake>,
667    #[serde(skip_serializing_if = "Option::is_none")]
668    pub fail_if_not_exists: Option<bool>,
669}
670
671#[derive(Debug, Clone, Serialize, Default)]
672pub struct ChannelCreatePayload {
673    pub name: String,
674    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
675    pub kind: Option<u8>,
676    #[serde(skip_serializing_if = "Option::is_none")]
677    pub topic: Option<String>,
678    #[serde(skip_serializing_if = "Option::is_none")]
679    pub bitrate: Option<u64>,
680    #[serde(skip_serializing_if = "Option::is_none")]
681    pub user_limit: Option<u64>,
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub rate_limit_per_user: Option<u64>,
684    #[serde(skip_serializing_if = "Option::is_none")]
685    pub position: Option<i64>,
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub parent_id: Option<Snowflake>,
688    #[serde(skip_serializing_if = "Option::is_none")]
689    pub nsfw: Option<bool>,
690}
691
692/// For nullable fields like `nick`, use `Some(None)` to clear them.
693#[derive(Debug, Clone, Serialize, Default)]
694pub struct EditMemberPayload {
695    #[serde(skip_serializing_if = "Option::is_none")]
696    pub nick: Option<Option<String>>,
697    #[serde(skip_serializing_if = "Option::is_none")]
698    pub roles: Option<Vec<Snowflake>>,
699    #[serde(skip_serializing_if = "Option::is_none")]
700    pub mute: Option<bool>,
701    #[serde(skip_serializing_if = "Option::is_none")]
702    pub deaf: Option<bool>,
703    #[serde(skip_serializing_if = "Option::is_none")]
704    pub channel_id: Option<Option<Snowflake>>,
705    #[serde(skip_serializing_if = "Option::is_none")]
706    pub communication_disabled_until: Option<Option<String>>,
707}
708
709#[derive(Debug, Clone, Serialize, Default)]
710pub struct CreateRolePayload {
711    pub name: String,
712    #[serde(skip_serializing_if = "Option::is_none")]
713    pub permissions: Option<String>,
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub color: Option<u64>,
716    #[serde(skip_serializing_if = "Option::is_none")]
717    pub hoist: Option<bool>,
718    #[serde(skip_serializing_if = "Option::is_none")]
719    pub mentionable: Option<bool>,
720}
721
722#[derive(Debug, Clone, Serialize, Default)]
723pub struct EditRolePayload {
724    #[serde(skip_serializing_if = "Option::is_none")]
725    pub name: Option<String>,
726    #[serde(skip_serializing_if = "Option::is_none")]
727    pub permissions: Option<String>,
728    #[serde(skip_serializing_if = "Option::is_none")]
729    pub color: Option<u64>,
730    #[serde(skip_serializing_if = "Option::is_none")]
731    pub hoist: Option<bool>,
732    #[serde(skip_serializing_if = "Option::is_none")]
733    pub mentionable: Option<bool>,
734}
735
736#[derive(Debug, Clone, Serialize, Default)]
737pub struct CreateInvitePayload {
738    /// Seconds. 0 = never expires.
739    #[serde(skip_serializing_if = "Option::is_none")]
740    pub max_age: Option<u64>,
741    /// 0 = unlimited.
742    #[serde(skip_serializing_if = "Option::is_none")]
743    pub max_uses: Option<u64>,
744    /// If true, members joined via this invite get kicked when they disconnect
745    /// (unless they've been given a role).
746    #[serde(skip_serializing_if = "Option::is_none")]
747    pub temporary: Option<bool>,
748    #[serde(skip_serializing_if = "Option::is_none")]
749    pub unique: Option<bool>,
750}
751
752/// Query params for fetching messages. Only set one of `before`/`after`/`around`.
753#[derive(Debug, Clone, Default)]
754pub struct GetMessagesQuery {
755    /// 1-100.
756    pub limit: Option<u8>,
757    pub before: Option<Snowflake>,
758    pub after: Option<Snowflake>,
759    pub around: Option<Snowflake>,
760}
761
762impl GetMessagesQuery {
763    pub fn to_query_string(&self) -> String {
764        let mut parts = Vec::new();
765        if let Some(l) = self.limit {
766            parts.push(format!("limit={}", l.min(100)));
767        }
768        if let Some(ref b) = self.before {
769            parts.push(format!("before={}", b));
770        }
771        if let Some(ref a) = self.after {
772            parts.push(format!("after={}", a));
773        }
774        if let Some(ref ar) = self.around {
775            parts.push(format!("around={}", ar));
776        }
777        if parts.is_empty() {
778            String::new()
779        } else {
780            format!("?{}", parts.join("&"))
781        }
782    }
783}
784
785#[derive(Debug, Clone, Serialize, Default)]
786pub struct EditGuildPayload {
787    #[serde(skip_serializing_if = "Option::is_none")]
788    pub name: Option<String>,
789    #[serde(skip_serializing_if = "Option::is_none")]
790    pub description: Option<String>,
791    #[serde(skip_serializing_if = "Option::is_none")]
792    pub preferred_locale: Option<String>,
793    #[serde(skip_serializing_if = "Option::is_none")]
794    pub afk_channel_id: Option<Option<Snowflake>>,
795    #[serde(skip_serializing_if = "Option::is_none")]
796    pub afk_timeout: Option<u64>,
797    #[serde(skip_serializing_if = "Option::is_none")]
798    pub verification_level: Option<u64>,
799    #[serde(skip_serializing_if = "Option::is_none")]
800    pub default_message_notifications: Option<u64>,
801    #[serde(skip_serializing_if = "Option::is_none")]
802    pub explicit_content_filter: Option<u64>,
803}
804
805#[derive(Debug, Clone, Serialize, Default)]
806pub struct WebhookExecutePayload {
807    #[serde(skip_serializing_if = "Option::is_none")]
808    pub content: Option<String>,
809    /// Override the webhook's name for this message.
810    #[serde(skip_serializing_if = "Option::is_none")]
811    pub username: Option<String>,
812    /// Override the webhook's avatar for this message.
813    #[serde(skip_serializing_if = "Option::is_none")]
814    pub avatar_url: Option<String>,
815    #[serde(skip_serializing_if = "Option::is_none")]
816    pub tts: Option<bool>,
817    #[serde(skip_serializing_if = "Option::is_none")]
818    pub embeds: Option<Vec<Embed>>,
819}