serenity/model/guild/member.rs
1#[cfg(feature = "cache")]
2use std::cmp::Reverse;
3use std::fmt;
4
5#[cfg(feature = "model")]
6use crate::builder::EditMember;
7#[cfg(feature = "cache")]
8use crate::cache::Cache;
9#[cfg(feature = "model")]
10use crate::http::{CacheHttp, Http};
11#[cfg(all(feature = "cache", feature = "model"))]
12use crate::internal::prelude::*;
13use crate::model::prelude::*;
14#[cfg(feature = "model")]
15use crate::model::utils::avatar_url;
16
17/// Information about a member of a guild.
18///
19/// [Discord docs](https://discord.com/developers/docs/resources/guild#guild-member-object),
20/// [extra fields](https://discord.com/developers/docs/topics/gateway-events#guild-member-add-guild-member-add-extra-fields).
21#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
22#[derive(Clone, Debug, Default, Deserialize, Serialize)]
23#[non_exhaustive]
24pub struct Member {
25 /// Attached User struct.
26 pub user: User,
27 /// The member's nickname, if present.
28 ///
29 /// Can't be longer than 32 characters.
30 pub nick: Option<String>,
31 /// The guild avatar hash
32 pub avatar: Option<ImageHash>,
33 /// Vector of Ids of [`Role`]s given to the member.
34 pub roles: Vec<RoleId>,
35 /// Timestamp representing the date when the member joined.
36 pub joined_at: Option<Timestamp>,
37 /// Timestamp representing the date since the member is boosting the guild.
38 pub premium_since: Option<Timestamp>,
39 /// Indicator of whether the member can hear in voice channels.
40 pub deaf: bool,
41 /// Indicator of whether the member can speak in voice channels.
42 pub mute: bool,
43 /// Guild member flags.
44 pub flags: GuildMemberFlags,
45 /// Indicator that the member hasn't accepted the rules of the guild yet.
46 #[serde(default)]
47 pub pending: bool,
48 /// The total permissions of the member in a channel, including overrides.
49 ///
50 /// This is only [`Some`] when returned in an [`Interaction`] object.
51 ///
52 /// [`Interaction`]: crate::model::application::Interaction
53 pub permissions: Option<Permissions>,
54 /// When the user's timeout will expire and the user will be able to communicate in the guild
55 /// again.
56 ///
57 /// Will be None or a time in the past if the user is not timed out.
58 pub communication_disabled_until: Option<Timestamp>,
59 /// The unique Id of the guild that the member is a part of.
60 #[serde(default)]
61 pub guild_id: GuildId,
62 /// If the member is currently flagged for sending excessive DMs to non-friend server members
63 /// in the last 24 hours.
64 ///
65 /// Will be None or a time in the past if the user is not flagged.
66 pub unusual_dm_activity_until: Option<Timestamp>,
67}
68
69bitflags! {
70 /// Flags for a guild member.
71 ///
72 /// [Discord docs](https://discord.com/developers/docs/resources/guild#guild-member-object-guild-member-flags).
73 #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
74 #[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
75 pub struct GuildMemberFlags: u32 {
76 /// Member has left and rejoined the guild. Not editable
77 const DID_REJOIN = 1 << 0;
78 /// Member has completed onboarding. Not editable
79 const COMPLETED_ONBOARDING = 1 << 1;
80 /// Member is exempt from guild verification requirements. Editable
81 const BYPASSES_VERIFICATION = 1 << 2;
82 /// Member has started onboarding. Not editable
83 const STARTED_ONBOARDING = 1 << 3;
84 /// Member is a guest and can only access the voice channel they were invited to. Not
85 /// editable
86 const IS_GUEST = 1 << 4;
87 /// Member has started Server Guide new member actions. Not editable
88 const STARTED_HOME_ACTIONS = 1 << 5;
89 /// Member has completed Server Guide new member actions. Not editable
90 const COMPLETED_HOME_ACTIONS = 1 << 6;
91 /// Member's username, display name, or nickname is blocked by AutoMod. Not editable
92 const AUTOMOD_QUARANTINED_USERNAME = 1 << 7;
93 /// Member has dismissed the DM settings upsell. Not editable
94 const DM_SETTINGS_UPSELL_ACKNOWLEDGED = 1 << 9;
95 }
96}
97
98#[cfg(feature = "model")]
99impl Member {
100 /// Adds a [`Role`] to the member.
101 ///
102 /// **Note**: Requires the [Manage Roles] permission.
103 ///
104 /// # Errors
105 ///
106 /// Returns [`Error::Http`] if the current user lacks permission, or if a role with the given
107 /// Id does not exist.
108 ///
109 /// [Manage Roles]: Permissions::MANAGE_ROLES
110 #[inline]
111 pub async fn add_role(&self, http: impl AsRef<Http>, role_id: impl Into<RoleId>) -> Result<()> {
112 http.as_ref().add_member_role(self.guild_id, self.user.id, role_id.into(), None).await
113 }
114
115 /// Adds one or multiple [`Role`]s to the member.
116 ///
117 /// **Note**: Requires the [Manage Roles] permission.
118 ///
119 /// # Errors
120 ///
121 /// Returns [`Error::Http`] if the current user lacks permission, or if a role with a given Id
122 /// does not exist.
123 ///
124 /// [Manage Roles]: Permissions::MANAGE_ROLES
125 pub async fn add_roles(&self, http: impl AsRef<Http>, role_ids: &[RoleId]) -> Result<()> {
126 for role_id in role_ids {
127 self.add_role(http.as_ref(), role_id).await?;
128 }
129
130 Ok(())
131 }
132
133 /// Ban a [`User`] from the guild, deleting a number of days' worth of messages (`dmd`) between
134 /// the range 0 and 7.
135 ///
136 /// **Note**: Requires the [Ban Members] permission.
137 ///
138 /// # Errors
139 ///
140 /// Returns a [`ModelError::DeleteMessageDaysAmount`] if the `dmd` is greater than 7. Can also
141 /// return [`Error::Http`] if the current user lacks permission to ban this member.
142 ///
143 /// [Ban Members]: Permissions::BAN_MEMBERS
144 #[inline]
145 pub async fn ban(&self, http: impl AsRef<Http>, dmd: u8) -> Result<()> {
146 self.ban_with_reason(http, dmd, "").await
147 }
148
149 /// Ban the member from the guild with a reason. Refer to [`Self::ban`] to further
150 /// documentation.
151 ///
152 /// # Errors
153 ///
154 /// In addition to the errors [`Self::ban`] may return, can also return
155 /// [`Error::ExceededLimit`] if the length of the reason is greater than 512.
156 #[inline]
157 pub async fn ban_with_reason(
158 &self,
159 http: impl AsRef<Http>,
160 dmd: u8,
161 reason: impl AsRef<str>,
162 ) -> Result<()> {
163 self.guild_id.ban_with_reason(http, self.user.id, dmd, reason).await
164 }
165
166 /// Determines the member's colour.
167 #[cfg(feature = "cache")]
168 pub fn colour(&self, cache: impl AsRef<Cache>) -> Option<Colour> {
169 let guild = cache.as_ref().guild(self.guild_id)?;
170
171 let mut roles = self
172 .roles
173 .iter()
174 .filter_map(|role_id| guild.roles.get(role_id))
175 .collect::<Vec<&Role>>();
176
177 roles.sort_by_key(|&b| Reverse(b));
178
179 let default = Colour::default();
180
181 roles.iter().find(|r| r.colour.0 != default.0).map(|r| r.colour)
182 }
183
184 /// Returns the "default channel" of the guild for the member. (This returns the first channel
185 /// that can be read by the member, if there isn't one returns [`None`])
186 #[cfg(feature = "cache")]
187 pub fn default_channel(&self, cache: impl AsRef<Cache>) -> Option<GuildChannel> {
188 let guild = self.guild_id.to_guild_cached(&cache)?;
189
190 let member = guild.members.get(&self.user.id)?;
191
192 for channel in guild.channels.values() {
193 if channel.kind != ChannelType::Category
194 && guild.user_permissions_in(channel, member).view_channel()
195 {
196 return Some(channel.clone());
197 }
198 }
199
200 None
201 }
202
203 /// Times the user out until `time`.
204 ///
205 /// Requires the [Moderate Members] permission.
206 ///
207 /// **Note**: [Moderate Members]: crate::model::permission::Permissions::MODERATE_MEMBERS
208 ///
209 /// # Errors
210 ///
211 /// Returns [`Error::Http`] if the current user lacks permission or if `time` is greater than
212 /// 28 days from the current time.
213 ///
214 /// [Moderate Members]: Permissions::MODERATE_MEMBERS
215 #[doc(alias = "timeout")]
216 pub async fn disable_communication_until_datetime(
217 &mut self,
218 cache_http: impl CacheHttp,
219 time: Timestamp,
220 ) -> Result<()> {
221 let builder = EditMember::new().disable_communication_until_datetime(time);
222 match self.guild_id.edit_member(cache_http, self.user.id, builder).await {
223 Ok(_) => {
224 self.communication_disabled_until = Some(time);
225 Ok(())
226 },
227 Err(why) => Err(why),
228 }
229 }
230
231 /// Calculates the member's display name.
232 ///
233 /// The nickname takes priority over the member's username if it exists.
234 #[inline]
235 #[must_use]
236 pub fn display_name(&self) -> &str {
237 self.nick.as_ref().or(self.user.global_name.as_ref()).unwrap_or(&self.user.name)
238 }
239
240 /// Returns the DiscordTag of a Member, taking possible nickname into account.
241 #[inline]
242 #[must_use]
243 pub fn distinct(&self) -> String {
244 if let Some(discriminator) = self.user.discriminator {
245 format!("{}#{:04}", self.display_name(), discriminator.get())
246 } else {
247 self.display_name().to_string()
248 }
249 }
250
251 /// Edits the member in place with the given data.
252 ///
253 /// See [`EditMember`] for the permission(s) required for separate builder methods, as well as
254 /// usage of this.
255 ///
256 /// # Examples
257 ///
258 /// See [`GuildId::edit_member`] for details.
259 ///
260 /// # Errors
261 ///
262 /// Returns [`Error::Http`] if the current user lacks necessary permissions.
263 pub async fn edit(
264 &mut self,
265 cache_http: impl CacheHttp,
266 builder: EditMember<'_>,
267 ) -> Result<()> {
268 *self = self.guild_id.edit_member(cache_http, self.user.id, builder).await?;
269 Ok(())
270 }
271
272 /// Allow a user to communicate, removing their timeout, if there is one.
273 ///
274 /// **Note**: Requires the [Moderate Members] permission.
275 ///
276 /// # Errors
277 ///
278 /// Returns [`Error::Http`] if the current user lacks permission.
279 ///
280 /// [Moderate Members]: Permissions::MODERATE_MEMBERS
281 #[doc(alias = "timeout")]
282 pub async fn enable_communication(&mut self, cache_http: impl CacheHttp) -> Result<()> {
283 let builder = EditMember::new().enable_communication();
284 *self = self.guild_id.edit_member(cache_http, self.user.id, builder).await?;
285 Ok(())
286 }
287
288 /// Retrieves the ID and position of the member's highest role in the hierarchy, if they have
289 /// one.
290 ///
291 /// This _may_ return [`None`] if the user has roles, but they are not present in the cache for
292 /// cache inconsistency reasons.
293 ///
294 /// The "highest role in hierarchy" is defined as the role with the highest position. If two or
295 /// more roles have the same highest position, then the role with the lowest ID is the highest.
296 #[cfg(feature = "cache")]
297 #[deprecated = "Use Guild::member_highest_role"]
298 pub fn highest_role_info(&self, cache: impl AsRef<Cache>) -> Option<(RoleId, u16)> {
299 cache
300 .as_ref()
301 .guild(self.guild_id)
302 .as_ref()
303 .and_then(|g| g.member_highest_role(self))
304 .map(|r| (r.id, r.position))
305 }
306
307 /// Kick the member from the guild.
308 ///
309 /// **Note**: Requires the [Kick Members] permission.
310 ///
311 /// # Examples
312 ///
313 /// Kick a member from its guild:
314 ///
315 /// ```rust,ignore
316 /// // assuming a `member` has already been bound
317 /// match member.kick().await {
318 /// Ok(()) => println!("Successfully kicked member"),
319 /// Err(Error::Model(ModelError::GuildNotFound)) => {
320 /// println!("Couldn't determine guild of member");
321 /// },
322 /// Err(Error::Model(ModelError::InvalidPermissions(missing_perms))) => {
323 /// println!("Didn't have permissions; missing: {:?}", missing_perms);
324 /// },
325 /// _ => {},
326 /// }
327 /// ```
328 ///
329 /// # Errors
330 ///
331 /// Returns a [`ModelError::GuildNotFound`] if the Id of the member's guild could not be
332 /// determined.
333 ///
334 /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
335 /// does not have permission to perform the kick.
336 ///
337 /// Otherwise will return [`Error::Http`] if the current user lacks permission.
338 ///
339 /// [Kick Members]: Permissions::KICK_MEMBERS
340 #[inline]
341 pub async fn kick(&self, cache_http: impl CacheHttp) -> Result<()> {
342 self.kick_with_reason(cache_http, "").await
343 }
344
345 /// Kicks the member from the guild, with a reason.
346 ///
347 /// **Note**: Requires the [Kick Members] permission.
348 ///
349 /// # Examples
350 ///
351 /// Kicks a member from it's guild, with an optional reason:
352 ///
353 /// ```rust,ignore
354 /// match member.kick(&ctx.http, "A Reason").await {
355 /// Ok(()) => println!("Successfully kicked member"),
356 /// Err(Error::Model(ModelError::GuildNotFound)) => {
357 /// println!("Couldn't determine guild of member");
358 /// },
359 /// Err(Error::Model(ModelError::InvalidPermissions(missing_perms))) => {
360 /// println!("Didn't have permissions; missing: {:?}", missing_perms);
361 /// },
362 /// _ => {},
363 /// }
364 /// ```
365 ///
366 /// # Errors
367 ///
368 /// In addition to the reasons [`Self::kick`] may return an error, can also return an error if
369 /// the given reason is too long.
370 ///
371 /// [Kick Members]: Permissions::KICK_MEMBERS
372 pub async fn kick_with_reason(&self, cache_http: impl CacheHttp, reason: &str) -> Result<()> {
373 #[cfg(feature = "cache")]
374 {
375 if let Some(cache) = cache_http.cache() {
376 let lookup = cache.guild(self.guild_id).as_deref().cloned();
377 if let Some(guild) = lookup {
378 guild.require_perms(cache, Permissions::KICK_MEMBERS)?;
379
380 guild.check_hierarchy(cache, self.user.id)?;
381 }
382 }
383 }
384
385 self.guild_id.kick_with_reason(cache_http.http(), self.user.id, reason).await
386 }
387
388 /// Moves the member to a voice channel.
389 ///
390 /// Requires the [Move Members] permission.
391 ///
392 /// # Errors
393 ///
394 /// Returns [`Error::Http`] if the member is not currently in a voice channel, or if the
395 /// current user lacks permission.
396 ///
397 /// [Move Members]: Permissions::MOVE_MEMBERS
398 pub async fn move_to_voice_channel(
399 &self,
400 cache_http: impl CacheHttp,
401 channel: impl Into<ChannelId>,
402 ) -> Result<Member> {
403 self.guild_id.move_member(cache_http, self.user.id, channel).await
404 }
405
406 /// Disconnects the member from their voice channel if any.
407 ///
408 /// Requires the [Move Members] permission.
409 ///
410 /// # Errors
411 ///
412 /// Returns [`Error::Http`] if the member is not currently in a voice channel, or if the
413 /// current user lacks permission.
414 ///
415 /// [Move Members]: Permissions::MOVE_MEMBERS
416 pub async fn disconnect_from_voice(&self, cache_http: impl CacheHttp) -> Result<Member> {
417 self.guild_id.disconnect_member(cache_http, self.user.id).await
418 }
419
420 /// Returns the guild-level permissions for the member.
421 ///
422 /// # Examples
423 ///
424 /// ```rust,ignore
425 /// // assuming there's a `member` variable gotten from anything.
426 /// println!("The permission bits for the member are: {}",
427 /// member.permissions(&cache).expect("permissions").bits());
428 /// ```
429 ///
430 /// # Errors
431 ///
432 /// Returns a [`ModelError::GuildNotFound`] if the guild the member's in could not be
433 /// found in the cache.
434 ///
435 /// And/or returns [`ModelError::ItemMissing`] if the "default channel" of the guild is not
436 /// found.
437 #[cfg(feature = "cache")]
438 #[deprecated = "Use Guild::member_permissions_in, as this doesn't consider permission overwrites"]
439 pub fn permissions(&self, cache: impl AsRef<Cache>) -> Result<Permissions> {
440 let guild = cache.as_ref().guild(self.guild_id).ok_or(ModelError::GuildNotFound)?;
441
442 #[allow(deprecated)]
443 Ok(guild.member_permissions(self))
444 }
445
446 /// Removes a [`Role`] from the member.
447 ///
448 /// **Note**: Requires the [Manage Roles] permission.
449 ///
450 /// # Errors
451 ///
452 /// Returns [`Error::Http`] if a role with the given Id does not exist, or if the current user
453 /// lacks permission.
454 ///
455 /// [Manage Roles]: Permissions::MANAGE_ROLES
456 pub async fn remove_role(
457 &self,
458 http: impl AsRef<Http>,
459 role_id: impl Into<RoleId>,
460 ) -> Result<()> {
461 http.as_ref().remove_member_role(self.guild_id, self.user.id, role_id.into(), None).await
462 }
463
464 /// Removes one or multiple [`Role`]s from the member.
465 ///
466 /// **Note**: Requires the [Manage Roles] permission.
467 ///
468 /// # Errors
469 ///
470 /// Returns [`Error::Http`] if a role with a given Id does not exist, or if the current user
471 /// lacks permission.
472 ///
473 /// [Manage Roles]: Permissions::MANAGE_ROLES
474 pub async fn remove_roles(&self, http: impl AsRef<Http>, role_ids: &[RoleId]) -> Result<()> {
475 for role_id in role_ids {
476 self.remove_role(http.as_ref(), role_id).await?;
477 }
478
479 Ok(())
480 }
481
482 /// Retrieves the full role data for the user's roles.
483 ///
484 /// This is shorthand for manually searching through the Cache.
485 ///
486 /// If role data can not be found for the member, then [`None`] is returned.
487 #[cfg(feature = "cache")]
488 pub fn roles(&self, cache: impl AsRef<Cache>) -> Option<Vec<Role>> {
489 Some(
490 cache
491 .as_ref()
492 .guild(self.guild_id)?
493 .roles
494 .iter()
495 .filter(|(id, _)| self.roles.contains(id))
496 .map(|(_, role)| role.clone())
497 .collect(),
498 )
499 }
500
501 /// Unbans the [`User`] from the guild.
502 ///
503 /// **Note**: Requires the [Ban Members] permission.
504 ///
505 /// # Errors
506 ///
507 /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
508 /// does not have permission to perform bans.
509 ///
510 /// [Ban Members]: Permissions::BAN_MEMBERS
511 #[inline]
512 pub async fn unban(&self, http: impl AsRef<Http>) -> Result<()> {
513 http.as_ref().remove_ban(self.guild_id, self.user.id, None).await
514 }
515
516 /// Returns the formatted URL of the member's per guild avatar, if one exists.
517 ///
518 /// This will produce a WEBP image URL, or GIF if the member has a GIF avatar.
519 #[inline]
520 #[must_use]
521 pub fn avatar_url(&self) -> Option<String> {
522 avatar_url(Some(self.guild_id), self.user.id, self.avatar.as_ref())
523 }
524
525 /// Retrieves the URL to the current member's avatar, falling back to the user's avatar, then
526 /// default avatar if needed.
527 ///
528 /// This will call [`Self::avatar_url`] first, and if that returns [`None`], it then falls back
529 /// to [`User::face()`].
530 #[inline]
531 #[must_use]
532 pub fn face(&self) -> String {
533 self.avatar_url().unwrap_or_else(|| self.user.face())
534 }
535}
536
537impl fmt::Display for Member {
538 /// Mentions the user so that they receive a notification.
539 ///
540 /// # Examples
541 ///
542 /// ```rust,ignore
543 /// // assumes a `member` has already been bound
544 /// println!("{} is a member!", member);
545 /// ```
546 ///
547 /// This is in the format of `<@USER_ID>`.
548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549 fmt::Display::fmt(&self.user.mention(), f)
550 }
551}
552
553/// A partial amount of data for a member.
554///
555/// This is used in [`Message`]s from [`Guild`]s.
556///
557/// [Discord docs](https://discord.com/developers/docs/resources/guild#guild-member-object),
558/// subset specification unknown (field type "partial member" is used in
559/// [link](https://discord.com/developers/docs/topics/gateway-events#message-create),
560/// [link](https://discord.com/developers/docs/resources/invite#invite-stage-instance-object),
561/// [link](https://discord.com/developers/docs/topics/gateway-events#message-create),
562/// [link](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure),
563/// [link](https://discord.com/developers/docs/interactions/receiving-and-responding#message-interaction-object))
564#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
565#[derive(Clone, Debug, Deserialize, Serialize)]
566#[non_exhaustive]
567pub struct PartialMember {
568 /// Indicator of whether the member can hear in voice channels.
569 #[serde(default)]
570 pub deaf: bool,
571 /// Timestamp representing the date when the member joined.
572 pub joined_at: Option<Timestamp>,
573 /// Indicator of whether the member can speak in voice channels
574 #[serde(default)]
575 pub mute: bool,
576 /// The member's nickname, if present.
577 ///
578 /// Can't be longer than 32 characters.
579 pub nick: Option<String>,
580 /// Vector of Ids of [`Role`]s given to the member.
581 pub roles: Vec<RoleId>,
582 /// Indicator that the member hasn't accepted the rules of the guild yet.
583 #[serde(default)]
584 pub pending: bool,
585 /// Timestamp representing the date since the member is boosting the guild.
586 pub premium_since: Option<Timestamp>,
587 /// The unique Id of the guild that the member is a part of.
588 ///
589 /// Manually inserted in [`Reaction::deserialize`].
590 pub guild_id: Option<GuildId>,
591 /// Attached User struct.
592 pub user: Option<User>,
593 /// The total permissions of the member in a channel, including overrides.
594 ///
595 /// This is only [`Some`] when returned in an [`Interaction`] object.
596 ///
597 /// [`Interaction`]: crate::model::application::Interaction
598 pub permissions: Option<Permissions>,
599 /// If the member is currently flagged for sending excessive DMs to non-friend server members
600 /// in the last 24 hours.
601 ///
602 /// Will be None or a time in the past if the user is not flagged.
603 pub unusual_dm_activity_until: Option<Timestamp>,
604}
605
606impl From<PartialMember> for Member {
607 fn from(partial: PartialMember) -> Self {
608 Member {
609 user: partial.user.unwrap_or_default(),
610 nick: partial.nick,
611 avatar: None,
612 roles: partial.roles,
613 joined_at: partial.joined_at,
614 premium_since: partial.premium_since,
615 deaf: partial.deaf,
616 mute: partial.mute,
617 flags: GuildMemberFlags::default(),
618 pending: partial.pending,
619 permissions: partial.permissions,
620 communication_disabled_until: None,
621 guild_id: partial.guild_id.unwrap_or_default(),
622 unusual_dm_activity_until: partial.unusual_dm_activity_until,
623 }
624 }
625}
626
627impl From<Member> for PartialMember {
628 fn from(member: Member) -> Self {
629 PartialMember {
630 deaf: member.deaf,
631 joined_at: member.joined_at,
632 mute: member.mute,
633 nick: member.nick,
634 roles: member.roles,
635 pending: member.pending,
636 premium_since: member.premium_since,
637 guild_id: Some(member.guild_id),
638 user: Some(member.user),
639 permissions: member.permissions,
640 unusual_dm_activity_until: member.unusual_dm_activity_until,
641 }
642 }
643}
644
645#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
646#[derive(Clone, Debug, Deserialize, Serialize)]
647#[non_exhaustive]
648pub struct PartialThreadMember {
649 /// The time the current user last joined the thread.
650 pub join_timestamp: Timestamp,
651 /// Any user-thread settings, currently only used for notifications
652 pub flags: ThreadMemberFlags,
653}
654
655/// A model representing a user in a Guild Thread.
656///
657/// [Discord docs](https://discord.com/developers/docs/resources/channel#thread-member-object),
658/// [extra fields](https://discord.com/developers/docs/topics/gateway-events#thread-member-update-thread-member-update-event-extra-fields).
659#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
660#[derive(Clone, Debug, Deserialize, Serialize)]
661#[non_exhaustive]
662pub struct ThreadMember {
663 #[serde(flatten)]
664 pub inner: PartialThreadMember,
665 /// The id of the thread.
666 pub id: ChannelId,
667 /// The id of the user.
668 pub user_id: UserId,
669 /// Additional information about the user.
670 ///
671 /// This field is only present when `with_member` is set to `true` when calling
672 /// List Thread Members or Get Thread Member, or inside [`ThreadMembersUpdateEvent`].
673 pub member: Option<Member>,
674 /// ID of the guild.
675 ///
676 /// Always present in [`ThreadMemberUpdateEvent`], otherwise `None`.
677 pub guild_id: Option<GuildId>,
678 // According to https://discord.com/developers/docs/topics/gateway-events#thread-members-update,
679 // > the thread member objects will also include the guild member and nullable presence objects
680 // > for each added thread member
681 // Which implies that ThreadMember has a presence field. But https://discord.com/developers/docs/resources/channel#thread-member-object
682 // says that's not true. I'm not adding the presence field here for now
683}
684
685bitflags! {
686 /// Describes extra features of the message.
687 ///
688 /// Discord docs: flags field on [Thread Member](https://discord.com/developers/docs/resources/channel#thread-member-object).
689 #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
690 #[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
691 pub struct ThreadMemberFlags: u64 {
692 // Not documented.
693 const NOTIFICATIONS = 1 << 0;
694 }
695}