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