1#![warn(missing_docs)]
3
4pub mod component;
5pub mod embed;
6pub mod sticker;
7
8mod activity;
9mod allowed_mentions;
10mod application;
11mod call;
12mod flags;
13mod interaction;
14mod kind;
15mod mention;
16mod message_pin;
17mod pins_listing;
18mod reaction;
19mod reaction_type;
20mod reference;
21mod reference_type;
22mod role_subscription_data;
23mod snapshot;
24
25pub use self::{
26 activity::{MessageActivity, MessageActivityType},
27 allowed_mentions::{AllowedMentions, MentionType},
28 application::MessageApplication,
29 call::MessageCall,
30 component::Component,
31 embed::Embed,
32 flags::MessageFlags,
33 interaction::MessageInteraction,
34 kind::MessageType,
35 mention::Mention,
36 message_pin::MessagePin,
37 pins_listing::PinsListing,
38 reaction::{EmojiReactionType, Reaction, ReactionCountDetails},
39 reaction_type::ReactionType,
40 reference::MessageReference,
41 reference_type::MessageReferenceType,
42 role_subscription_data::RoleSubscriptionData,
43 snapshot::MessageSnapshot,
44 sticker::{MessageSticker, Sticker},
45};
46
47use crate::{
48 application::interaction::InteractionMetadata,
49 channel::{Attachment, Channel, ChannelMention},
50 guild::PartialMember,
51 id::{
52 Id,
53 marker::{
54 ApplicationMarker, ChannelMarker, GuildMarker, MessageMarker, RoleMarker, WebhookMarker,
55 },
56 },
57 poll::Poll,
58 user::User,
59 util::Timestamp,
60};
61use serde::{Deserialize, Serialize};
62
63#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
65pub struct Message {
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub activity: Option<MessageActivity>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub application: Option<MessageApplication>,
72 #[serde(skip_serializing_if = "Option::is_none")]
78 pub application_id: Option<Id<ApplicationMarker>>,
79 pub attachments: Vec<Attachment>,
92 pub author: User,
94 pub call: Option<MessageCall>,
96 pub channel_id: Id<ChannelMarker>,
98 #[serde(default, skip_serializing_if = "Vec::is_empty")]
111 pub components: Vec<Component>,
112 pub content: String,
125 pub edited_timestamp: Option<Timestamp>,
127 pub embeds: Vec<Embed>,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub flags: Option<MessageFlags>,
143 #[serde(skip_serializing_if = "Option::is_none")]
147 pub guild_id: Option<Id<GuildMarker>>,
148 pub id: Id<MessageMarker>,
150 #[deprecated(note = "use interaction_metadata instead")]
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub interaction: Option<MessageInteraction>,
154 #[serde(skip_serializing_if = "Option::is_none")]
157 pub interaction_metadata: Option<Box<InteractionMetadata>>,
158 #[serde(rename = "type")]
160 pub kind: MessageType,
161 #[serde(skip_serializing_if = "Option::is_none")]
165 pub member: Option<PartialMember>,
166 #[serde(default, skip_serializing_if = "Vec::is_empty")]
171 pub mention_channels: Vec<ChannelMention>,
172 pub mention_everyone: bool,
174 pub mention_roles: Vec<Id<RoleMarker>>,
178 pub mentions: Vec<Mention>,
180 #[serde(default, skip_serializing_if = "Vec::is_empty")]
183 pub message_snapshots: Vec<MessageSnapshot>,
184 pub pinned: bool,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub poll: Option<Poll>,
189 #[serde(default, skip_serializing_if = "Vec::is_empty")]
191 pub reactions: Vec<Reaction>,
192 #[serde(rename = "message_reference", skip_serializing_if = "Option::is_none")]
194 pub reference: Option<MessageReference>,
195 #[serde(skip_serializing_if = "Option::is_none")]
199 pub referenced_message: Option<Box<Message>>,
200 #[serde(skip_serializing_if = "Option::is_none")]
207 pub role_subscription_data: Option<RoleSubscriptionData>,
208 #[serde(default)]
210 pub sticker_items: Vec<MessageSticker>,
211 pub timestamp: Timestamp,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub thread: Option<Channel>,
216 pub tts: bool,
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub webhook_id: Option<Id<WebhookMarker>>,
221}
222
223#[cfg(test)]
224mod tests {
225 use super::{
226 EmojiReactionType, Message, MessageActivity, MessageActivityType, MessageApplication,
227 MessageCall, MessageFlags, MessageReference, MessageType, Reaction,
228 reaction::ReactionCountDetails,
229 reference_type::MessageReferenceType,
230 sticker::{MessageSticker, StickerFormatType},
231 };
232 use crate::{
233 channel::{ChannelMention, ChannelType},
234 guild::{MemberFlags, PartialMember},
235 id::Id,
236 test::image_hash,
237 user::User,
238 util::{Timestamp, datetime::TimestampParseError},
239 };
240 use serde_test::Token;
241 use std::str::FromStr;
242
243 #[allow(clippy::too_many_lines, deprecated)]
244 #[test]
245 fn message_deserialization() {
246 let joined_at = Some(Timestamp::from_str("2020-01-01T00:00:00.000000+00:00").unwrap());
247 let timestamp = Timestamp::from_micros(1_580_608_922_020_000).expect("non zero");
248 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
249
250 let value = Message {
251 activity: None,
252 application: None,
253 application_id: None,
254 attachments: Vec::new(),
255 author: User {
256 accent_color: None,
257 avatar: Some(image_hash::AVATAR),
258 avatar_decoration: None,
259 avatar_decoration_data: None,
260 banner: None,
261 bot: false,
262 discriminator: 1,
263 email: None,
264 flags: None,
265 global_name: Some("test".to_owned()),
266 id: Id::new(3),
267 locale: None,
268 mfa_enabled: None,
269 name: "test".to_owned(),
270 premium_type: None,
271 primary_guild: None,
272 public_flags: None,
273 system: None,
274 verified: None,
275 },
276 call: Some(MessageCall {
277 ended_timestamp: None,
278 participants: Vec::new(),
279 }),
280 channel_id: Id::new(2),
281 components: Vec::new(),
282 content: "ping".to_owned(),
283 edited_timestamp: None,
284 embeds: Vec::new(),
285 flags: Some(MessageFlags::empty()),
286 guild_id: Some(Id::new(1)),
287 id: Id::new(4),
288 interaction: None,
289 kind: MessageType::Regular,
290 member: Some(PartialMember {
291 avatar: None,
292 avatar_decoration_data: None,
293 banner: None,
294 communication_disabled_until: None,
295 deaf: false,
296 flags,
297 joined_at,
298 mute: false,
299 nick: Some("member nick".to_owned()),
300 permissions: None,
301 premium_since: None,
302 roles: Vec::new(),
303 user: None,
304 }),
305 mention_channels: Vec::new(),
306 mention_everyone: false,
307 mention_roles: Vec::new(),
308 mentions: Vec::new(),
309 message_snapshots: Vec::new(),
310 pinned: false,
311 poll: None,
312 reactions: Vec::new(),
313 reference: None,
314 role_subscription_data: None,
315 sticker_items: vec![MessageSticker {
316 format_type: StickerFormatType::Png,
317 id: Id::new(1),
318 name: "sticker name".to_owned(),
319 }],
320 referenced_message: None,
321 timestamp,
322 thread: None,
323 tts: false,
324 webhook_id: None,
325 interaction_metadata: None,
326 };
327
328 serde_test::assert_tokens(
329 &value,
330 &[
331 Token::Struct {
332 name: "Message",
333 len: 19,
334 },
335 Token::Str("attachments"),
336 Token::Seq { len: Some(0) },
337 Token::SeqEnd,
338 Token::Str("author"),
339 Token::Struct {
340 name: "User",
341 len: 10,
342 },
343 Token::Str("accent_color"),
344 Token::None,
345 Token::Str("avatar"),
346 Token::Some,
347 Token::Str(image_hash::AVATAR_INPUT),
348 Token::Str("avatar_decoration"),
349 Token::None,
350 Token::Str("avatar_decoration_data"),
351 Token::None,
352 Token::Str("banner"),
353 Token::None,
354 Token::Str("bot"),
355 Token::Bool(false),
356 Token::Str("discriminator"),
357 Token::Str("0001"),
358 Token::Str("global_name"),
359 Token::Some,
360 Token::Str("test"),
361 Token::Str("id"),
362 Token::NewtypeStruct { name: "Id" },
363 Token::Str("3"),
364 Token::Str("username"),
365 Token::Str("test"),
366 Token::StructEnd,
367 Token::Str("call"),
368 Token::Some,
369 Token::Struct {
370 name: "MessageCall",
371 len: 2,
372 },
373 Token::Str("ended_timestamp"),
374 Token::None,
375 Token::Str("participants"),
376 Token::Seq { len: Some(0) },
377 Token::SeqEnd,
378 Token::StructEnd,
379 Token::Str("channel_id"),
380 Token::NewtypeStruct { name: "Id" },
381 Token::Str("2"),
382 Token::Str("content"),
383 Token::Str("ping"),
384 Token::Str("edited_timestamp"),
385 Token::None,
386 Token::Str("embeds"),
387 Token::Seq { len: Some(0) },
388 Token::SeqEnd,
389 Token::Str("flags"),
390 Token::Some,
391 Token::U64(0),
392 Token::Str("guild_id"),
393 Token::Some,
394 Token::NewtypeStruct { name: "Id" },
395 Token::Str("1"),
396 Token::Str("id"),
397 Token::NewtypeStruct { name: "Id" },
398 Token::Str("4"),
399 Token::Str("type"),
400 Token::U8(0),
401 Token::Str("member"),
402 Token::Some,
403 Token::Struct {
404 name: "PartialMember",
405 len: 8,
406 },
407 Token::Str("communication_disabled_until"),
408 Token::None,
409 Token::Str("deaf"),
410 Token::Bool(false),
411 Token::Str("flags"),
412 Token::U64(flags.bits()),
413 Token::Str("joined_at"),
414 Token::Some,
415 Token::Str("2020-01-01T00:00:00.000000+00:00"),
416 Token::Str("mute"),
417 Token::Bool(false),
418 Token::Str("nick"),
419 Token::Some,
420 Token::Str("member nick"),
421 Token::Str("roles"),
422 Token::Seq { len: Some(0) },
423 Token::SeqEnd,
424 Token::Str("user"),
425 Token::None,
426 Token::StructEnd,
427 Token::Str("mention_everyone"),
428 Token::Bool(false),
429 Token::Str("mention_roles"),
430 Token::Seq { len: Some(0) },
431 Token::SeqEnd,
432 Token::Str("mentions"),
433 Token::Seq { len: Some(0) },
434 Token::SeqEnd,
435 Token::Str("pinned"),
436 Token::Bool(false),
437 Token::Str("sticker_items"),
438 Token::Seq { len: Some(1) },
439 Token::Struct {
440 name: "MessageSticker",
441 len: 3,
442 },
443 Token::Str("format_type"),
444 Token::U8(1),
445 Token::Str("id"),
446 Token::NewtypeStruct { name: "Id" },
447 Token::Str("1"),
448 Token::Str("name"),
449 Token::Str("sticker name"),
450 Token::StructEnd,
451 Token::SeqEnd,
452 Token::Str("timestamp"),
453 Token::Str("2020-02-02T02:02:02.020000+00:00"),
454 Token::Str("tts"),
455 Token::Bool(false),
456 Token::StructEnd,
457 ],
458 );
459 }
460
461 #[allow(clippy::too_many_lines, deprecated)]
462 #[test]
463 fn message_deserialization_complete() -> Result<(), TimestampParseError> {
464 let edited_timestamp = Timestamp::from_str("2021-08-10T12:41:51.602000+00:00")?;
465 let joined_at = Some(Timestamp::from_str("2020-01-01T00:00:00.000000+00:00")?);
466 let timestamp = Timestamp::from_micros(1_580_608_922_020_000).expect("non zero");
467 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
468
469 let value = Message {
470 activity: Some(MessageActivity {
471 kind: MessageActivityType::Join,
472 party_id: None,
473 }),
474 application: Some(MessageApplication {
475 cover_image: Some(image_hash::COVER),
476 description: "a description".to_owned(),
477 icon: Some(image_hash::ICON),
478 id: Id::new(1),
479 name: "application".to_owned(),
480 }),
481 application_id: Some(Id::new(1)),
482 attachments: Vec::new(),
483 author: User {
484 accent_color: None,
485 avatar: Some(image_hash::AVATAR),
486 avatar_decoration: None,
487 avatar_decoration_data: None,
488 banner: None,
489 bot: false,
490 discriminator: 1,
491 email: None,
492 flags: None,
493 global_name: Some("test".to_owned()),
494 id: Id::new(3),
495 locale: None,
496 mfa_enabled: None,
497 name: "test".to_owned(),
498 premium_type: None,
499 primary_guild: None,
500 public_flags: None,
501 system: None,
502 verified: None,
503 },
504 call: None,
505 channel_id: Id::new(2),
506 components: Vec::new(),
507 content: "ping".to_owned(),
508 edited_timestamp: Some(edited_timestamp),
509 embeds: Vec::new(),
510 flags: Some(MessageFlags::empty()),
511 guild_id: Some(Id::new(1)),
512 id: Id::new(4),
513 interaction: None,
514 kind: MessageType::Regular,
515 member: Some(PartialMember {
516 avatar: None,
517 avatar_decoration_data: None,
518 banner: None,
519 communication_disabled_until: None,
520 deaf: false,
521 flags,
522 joined_at,
523 mute: false,
524 nick: Some("member nick".to_owned()),
525 permissions: None,
526 premium_since: None,
527 roles: Vec::new(),
528 user: None,
529 }),
530 mention_channels: vec![ChannelMention {
531 guild_id: Id::new(1),
532 id: Id::new(2),
533 kind: ChannelType::GuildText,
534 name: "channel".to_owned(),
535 }],
536 mention_everyone: false,
537 mention_roles: Vec::new(),
538 mentions: Vec::new(),
539 message_snapshots: Vec::new(),
540 pinned: false,
541 poll: None,
542 reactions: vec![Reaction {
543 burst_colors: Vec::new(),
544 count: 7,
545 count_details: ReactionCountDetails {
546 burst: 0,
547 normal: 7,
548 },
549 emoji: EmojiReactionType::Unicode {
550 name: "a".to_owned(),
551 },
552 me: true,
553 me_burst: false,
554 }],
555 reference: Some(MessageReference {
556 channel_id: Some(Id::new(1)),
557 guild_id: None,
558 kind: MessageReferenceType::Default,
559 message_id: None,
560 fail_if_not_exists: None,
561 }),
562 role_subscription_data: None,
563 sticker_items: vec![MessageSticker {
564 format_type: StickerFormatType::Png,
565 id: Id::new(1),
566 name: "sticker name".to_owned(),
567 }],
568 referenced_message: None,
569 timestamp,
570 thread: None,
571 tts: false,
572 webhook_id: Some(Id::new(1)),
573 interaction_metadata: None,
574 };
575
576 serde_test::assert_tokens(
577 &value,
578 &[
579 Token::Struct {
580 name: "Message",
581 len: 26,
582 },
583 Token::Str("activity"),
584 Token::Some,
585 Token::Struct {
586 name: "MessageActivity",
587 len: 1,
588 },
589 Token::Str("type"),
590 Token::U8(1),
591 Token::StructEnd,
592 Token::Str("application"),
593 Token::Some,
594 Token::Struct {
595 name: "MessageApplication",
596 len: 5,
597 },
598 Token::Str("cover_image"),
599 Token::Some,
600 Token::Str(image_hash::COVER_INPUT),
601 Token::Str("description"),
602 Token::Str("a description"),
603 Token::Str("icon"),
604 Token::Some,
605 Token::Str(image_hash::ICON_INPUT),
606 Token::Str("id"),
607 Token::NewtypeStruct { name: "Id" },
608 Token::Str("1"),
609 Token::Str("name"),
610 Token::Str("application"),
611 Token::StructEnd,
612 Token::Str("application_id"),
613 Token::Some,
614 Token::NewtypeStruct { name: "Id" },
615 Token::Str("1"),
616 Token::Str("attachments"),
617 Token::Seq { len: Some(0) },
618 Token::SeqEnd,
619 Token::Str("author"),
620 Token::Struct {
621 name: "User",
622 len: 10,
623 },
624 Token::Str("accent_color"),
625 Token::None,
626 Token::Str("avatar"),
627 Token::Some,
628 Token::Str(image_hash::AVATAR_INPUT),
629 Token::Str("avatar_decoration"),
630 Token::None,
631 Token::Str("avatar_decoration_data"),
632 Token::None,
633 Token::Str("banner"),
634 Token::None,
635 Token::Str("bot"),
636 Token::Bool(false),
637 Token::Str("discriminator"),
638 Token::Str("0001"),
639 Token::Str("global_name"),
640 Token::Some,
641 Token::Str("test"),
642 Token::Str("id"),
643 Token::NewtypeStruct { name: "Id" },
644 Token::Str("3"),
645 Token::Str("username"),
646 Token::Str("test"),
647 Token::StructEnd,
648 Token::Str("call"),
649 Token::None,
650 Token::Str("channel_id"),
651 Token::NewtypeStruct { name: "Id" },
652 Token::Str("2"),
653 Token::Str("content"),
654 Token::Str("ping"),
655 Token::Str("edited_timestamp"),
656 Token::Some,
657 Token::Str("2021-08-10T12:41:51.602000+00:00"),
658 Token::Str("embeds"),
659 Token::Seq { len: Some(0) },
660 Token::SeqEnd,
661 Token::Str("flags"),
662 Token::Some,
663 Token::U64(0),
664 Token::Str("guild_id"),
665 Token::Some,
666 Token::NewtypeStruct { name: "Id" },
667 Token::Str("1"),
668 Token::Str("id"),
669 Token::NewtypeStruct { name: "Id" },
670 Token::Str("4"),
671 Token::Str("type"),
672 Token::U8(0),
673 Token::Str("member"),
674 Token::Some,
675 Token::Struct {
676 name: "PartialMember",
677 len: 8,
678 },
679 Token::Str("communication_disabled_until"),
680 Token::None,
681 Token::Str("deaf"),
682 Token::Bool(false),
683 Token::Str("flags"),
684 Token::U64(flags.bits()),
685 Token::Str("joined_at"),
686 Token::Some,
687 Token::Str("2020-01-01T00:00:00.000000+00:00"),
688 Token::Str("mute"),
689 Token::Bool(false),
690 Token::Str("nick"),
691 Token::Some,
692 Token::Str("member nick"),
693 Token::Str("roles"),
694 Token::Seq { len: Some(0) },
695 Token::SeqEnd,
696 Token::Str("user"),
697 Token::None,
698 Token::StructEnd,
699 Token::Str("mention_channels"),
700 Token::Seq { len: Some(1) },
701 Token::Struct {
702 name: "ChannelMention",
703 len: 4,
704 },
705 Token::Str("guild_id"),
706 Token::NewtypeStruct { name: "Id" },
707 Token::Str("1"),
708 Token::Str("id"),
709 Token::NewtypeStruct { name: "Id" },
710 Token::Str("2"),
711 Token::Str("type"),
712 Token::U8(0),
713 Token::Str("name"),
714 Token::Str("channel"),
715 Token::StructEnd,
716 Token::SeqEnd,
717 Token::Str("mention_everyone"),
718 Token::Bool(false),
719 Token::Str("mention_roles"),
720 Token::Seq { len: Some(0) },
721 Token::SeqEnd,
722 Token::Str("mentions"),
723 Token::Seq { len: Some(0) },
724 Token::SeqEnd,
725 Token::Str("pinned"),
726 Token::Bool(false),
727 Token::Str("reactions"),
728 Token::Seq { len: Some(1) },
729 Token::Struct {
730 name: "Reaction",
731 len: 6,
732 },
733 Token::Str("burst_colors"),
734 Token::Seq { len: Some(0) },
735 Token::SeqEnd,
736 Token::Str("count"),
737 Token::U64(7),
738 Token::Str("count_details"),
739 Token::Struct {
740 name: "ReactionCountDetails",
741 len: 2,
742 },
743 Token::Str("burst"),
744 Token::U64(0),
745 Token::Str("normal"),
746 Token::U64(7),
747 Token::StructEnd,
748 Token::Str("emoji"),
749 Token::Struct {
750 name: "EmojiReactionType",
751 len: 1,
752 },
753 Token::Str("name"),
754 Token::Str("a"),
755 Token::StructEnd,
756 Token::Str("me"),
757 Token::Bool(true),
758 Token::Str("me_burst"),
759 Token::Bool(false),
760 Token::StructEnd,
761 Token::SeqEnd,
762 Token::Str("message_reference"),
763 Token::Some,
764 Token::Struct {
765 name: "MessageReference",
766 len: 2,
767 },
768 Token::Str("channel_id"),
769 Token::Some,
770 Token::NewtypeStruct { name: "Id" },
771 Token::Str("1"),
772 Token::Str("type"),
773 Token::U8(0),
774 Token::StructEnd,
775 Token::Str("sticker_items"),
776 Token::Seq { len: Some(1) },
777 Token::Struct {
778 name: "MessageSticker",
779 len: 3,
780 },
781 Token::Str("format_type"),
782 Token::U8(1),
783 Token::Str("id"),
784 Token::NewtypeStruct { name: "Id" },
785 Token::Str("1"),
786 Token::Str("name"),
787 Token::Str("sticker name"),
788 Token::StructEnd,
789 Token::SeqEnd,
790 Token::Str("timestamp"),
791 Token::Str("2020-02-02T02:02:02.020000+00:00"),
792 Token::Str("tts"),
793 Token::Bool(false),
794 Token::Str("webhook_id"),
795 Token::Some,
796 Token::NewtypeStruct { name: "Id" },
797 Token::Str("1"),
798 Token::StructEnd,
799 ],
800 );
801
802 Ok(())
803 }
804}