1pub mod application_command;
8pub mod message_component;
9pub mod modal;
10
11mod context_type;
12mod interaction_type;
13mod metadata;
14mod resolved;
15
16pub use self::{
17 context_type::InteractionContextType,
18 interaction_type::InteractionType,
19 metadata::InteractionMetadata,
20 resolved::{InteractionChannel, InteractionDataResolved, InteractionMember},
21};
22
23use self::{
24 application_command::CommandData, message_component::MessageComponentInteractionData,
25 modal::ModalInteractionData,
26};
27use crate::{
28 channel::{Channel, Message},
29 guild::{GuildFeature, PartialMember, Permissions},
30 id::{
31 AnonymizableId, Id,
32 marker::{ApplicationMarker, ChannelMarker, GuildMarker, InteractionMarker, UserMarker},
33 },
34 oauth::ApplicationIntegrationMap,
35 user::User,
36};
37use serde::{
38 Deserialize, Deserializer, Serialize,
39 de::{Error as DeError, IgnoredAny, MapAccess, Visitor},
40};
41use serde_value::{DeserializerError, Value};
42use std::fmt::{Formatter, Result as FmtResult};
43
44use super::monetization::Entitlement;
45
46#[derive(Clone, Debug, PartialEq, Serialize)]
52pub struct Interaction {
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub app_permissions: Option<Permissions>,
56 pub application_id: Id<ApplicationMarker>,
58 pub authorizing_integration_owners:
61 ApplicationIntegrationMap<AnonymizableId<GuildMarker>, Id<UserMarker>>,
62 #[serde(skip_serializing_if = "Option::is_none")]
68 pub channel: Option<Channel>,
69 #[serde(skip_serializing_if = "Option::is_none")]
75 #[deprecated(
76 note = "channel_id is deprecated in the discord API and will no be sent in the future, users should use the channel field instead."
77 )]
78 pub channel_id: Option<Id<ChannelMarker>>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub context: Option<InteractionContextType>,
82 #[serde(skip_serializing_if = "Option::is_none")]
93 pub data: Option<InteractionData>,
94 pub entitlements: Vec<Entitlement>,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub guild: Option<InteractionPartialGuild>,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub guild_id: Option<Id<GuildMarker>>,
102 #[serde(skip_serializing_if = "Option::is_none")]
106 pub guild_locale: Option<String>,
107 pub id: Id<InteractionMarker>,
109 #[serde(rename = "type")]
111 pub kind: InteractionType,
112 #[serde(skip_serializing_if = "Option::is_none")]
118 pub locale: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
123 pub member: Option<PartialMember>,
124 #[serde(skip_serializing_if = "Option::is_none")]
130 pub message: Option<Message>,
131 pub token: String,
133 #[serde(skip_serializing_if = "Option::is_none")]
137 pub user: Option<User>,
138}
139
140impl Interaction {
141 pub const fn author_id(&self) -> Option<Id<UserMarker>> {
150 if let Some(user) = self.author() {
151 Some(user.id)
152 } else {
153 None
154 }
155 }
156
157 pub const fn author(&self) -> Option<&User> {
166 match self.member.as_ref() {
167 Some(member) if member.user.is_some() => member.user.as_ref(),
168 _ => self.user.as_ref(),
169 }
170 }
171
172 pub const fn is_dm(&self) -> bool {
174 self.user.is_some()
175 }
176
177 pub const fn is_guild(&self) -> bool {
179 self.member.is_some()
180 }
181}
182
183impl<'de> Deserialize<'de> for Interaction {
184 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
185 deserializer.deserialize_map(InteractionVisitor)
186 }
187}
188
189#[derive(Debug, Deserialize)]
190#[serde(field_identifier, rename_all = "snake_case")]
191enum InteractionField {
192 AppPermissions,
193 ApplicationId,
194 Context,
195 Channel,
196 ChannelId,
197 Data,
198 Entitlements,
199 Guild,
200 GuildId,
201 GuildLocale,
202 Id,
203 Locale,
204 Member,
205 Message,
206 Token,
207 Type,
208 User,
209 Version,
210 AuthorizingIntegrationOwners,
211}
212
213struct InteractionVisitor;
214
215impl<'de> Visitor<'de> for InteractionVisitor {
216 type Value = Interaction;
217
218 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
219 f.write_str("enum Interaction")
220 }
221
222 #[allow(clippy::too_many_lines, deprecated)]
223 fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
224 let mut app_permissions: Option<Permissions> = None;
225 let mut application_id: Option<Id<ApplicationMarker>> = None;
226 let mut channel: Option<Channel> = None;
227 let mut channel_id: Option<Id<ChannelMarker>> = None;
228 let mut context: Option<InteractionContextType> = None;
229 let mut data: Option<Value> = None;
230 let mut entitlements: Option<Vec<Entitlement>> = None;
231 let mut guild: Option<InteractionPartialGuild> = None;
232 let mut guild_id: Option<Id<GuildMarker>> = None;
233 let mut guild_locale: Option<String> = None;
234 let mut id: Option<Id<InteractionMarker>> = None;
235 let mut kind: Option<InteractionType> = None;
236 let mut locale: Option<String> = None;
237 let mut member: Option<PartialMember> = None;
238 let mut message: Option<Message> = None;
239 let mut token: Option<String> = None;
240 let mut user: Option<User> = None;
241 let mut authorizing_integration_owners: Option<
242 ApplicationIntegrationMap<AnonymizableId<GuildMarker>, Id<UserMarker>>,
243 > = None;
244
245 loop {
246 let key = match map.next_key() {
247 Ok(Some(key)) => key,
248 Ok(None) => break,
249 Err(_) => {
250 map.next_value::<IgnoredAny>()?;
251
252 continue;
253 }
254 };
255
256 match key {
257 InteractionField::AppPermissions => {
258 if app_permissions.is_some() {
259 return Err(DeError::duplicate_field("app_permissions"));
260 }
261
262 app_permissions = map.next_value()?;
263 }
264 InteractionField::ApplicationId => {
265 if application_id.is_some() {
266 return Err(DeError::duplicate_field("application_id"));
267 }
268
269 application_id = Some(map.next_value()?);
270 }
271 InteractionField::Context => {
272 if context.is_some() {
273 return Err(DeError::duplicate_field("context"));
274 }
275
276 context = map.next_value()?;
277 }
278 InteractionField::Channel => {
279 if channel.is_some() {
280 return Err(DeError::duplicate_field("channel"));
281 }
282
283 channel = map.next_value()?;
284 }
285 InteractionField::ChannelId => {
286 if channel_id.is_some() {
287 return Err(DeError::duplicate_field("channel_id"));
288 }
289
290 channel_id = map.next_value()?;
291 }
292 InteractionField::Data => {
293 if data.is_some() {
294 return Err(DeError::duplicate_field("data"));
295 }
296
297 data = map.next_value()?;
298 }
299 InteractionField::Entitlements => {
300 if entitlements.is_some() {
301 return Err(DeError::duplicate_field("entitlements"));
302 }
303
304 entitlements = map.next_value()?;
305 }
306 InteractionField::Guild => {
307 if guild.is_some() {
308 return Err(DeError::duplicate_field("guild"));
309 }
310
311 guild = map.next_value()?;
312 }
313 InteractionField::GuildId => {
314 if guild_id.is_some() {
315 return Err(DeError::duplicate_field("guild_id"));
316 }
317
318 guild_id = map.next_value()?;
319 }
320 InteractionField::GuildLocale => {
321 if guild_locale.is_some() {
322 return Err(DeError::duplicate_field("guild_locale"));
323 }
324
325 guild_locale = map.next_value()?;
326 }
327 InteractionField::Id => {
328 if id.is_some() {
329 return Err(DeError::duplicate_field("id"));
330 }
331
332 id = Some(map.next_value()?);
333 }
334 InteractionField::Locale => {
335 if locale.is_some() {
336 return Err(DeError::duplicate_field("locale"));
337 }
338
339 locale = map.next_value()?;
340 }
341 InteractionField::Member => {
342 if member.is_some() {
343 return Err(DeError::duplicate_field("member"));
344 }
345
346 member = map.next_value()?;
347 }
348 InteractionField::Message => {
349 if message.is_some() {
350 return Err(DeError::duplicate_field("message"));
351 }
352
353 message = map.next_value()?;
354 }
355 InteractionField::Token => {
356 if token.is_some() {
357 return Err(DeError::duplicate_field("token"));
358 }
359
360 token = Some(map.next_value()?);
361 }
362 InteractionField::Type => {
363 if kind.is_some() {
364 return Err(DeError::duplicate_field("kind"));
365 }
366
367 kind = Some(map.next_value()?);
368 }
369 InteractionField::User => {
370 if user.is_some() {
371 return Err(DeError::duplicate_field("user"));
372 }
373
374 user = map.next_value()?;
375 }
376 InteractionField::Version => {
377 map.next_value::<IgnoredAny>()?;
379 }
380 InteractionField::AuthorizingIntegrationOwners => {
381 if authorizing_integration_owners.is_some() {
382 return Err(DeError::duplicate_field("authorizing_integration_owners"));
383 }
384
385 authorizing_integration_owners = map.next_value()?;
386 }
387 }
388 }
389
390 let application_id =
391 application_id.ok_or_else(|| DeError::missing_field("application_id"))?;
392 let authorizing_integration_owners = authorizing_integration_owners
393 .ok_or_else(|| DeError::missing_field("authorizing_integration_owners"))?;
394 let id = id.ok_or_else(|| DeError::missing_field("id"))?;
395 let token = token.ok_or_else(|| DeError::missing_field("token"))?;
396 let kind = kind.ok_or_else(|| DeError::missing_field("kind"))?;
397
398 let data = match kind {
399 InteractionType::Ping => None,
400 InteractionType::ApplicationCommand => {
401 let data = data
402 .ok_or_else(|| DeError::missing_field("data"))?
403 .deserialize_into()
404 .map_err(DeserializerError::into_error)?;
405
406 Some(InteractionData::ApplicationCommand(data))
407 }
408 InteractionType::MessageComponent => {
409 let data = data
410 .ok_or_else(|| DeError::missing_field("data"))?
411 .deserialize_into()
412 .map_err(DeserializerError::into_error)?;
413
414 Some(InteractionData::MessageComponent(data))
415 }
416 InteractionType::ApplicationCommandAutocomplete => {
417 let data = data
418 .ok_or_else(|| DeError::missing_field("data"))?
419 .deserialize_into()
420 .map_err(DeserializerError::into_error)?;
421
422 Some(InteractionData::ApplicationCommand(data))
423 }
424 InteractionType::ModalSubmit => {
425 let data = data
426 .ok_or_else(|| DeError::missing_field("data"))?
427 .deserialize_into()
428 .map_err(DeserializerError::into_error)?;
429
430 Some(InteractionData::ModalSubmit(data))
431 }
432 };
433
434 let entitlements = entitlements.unwrap_or_default();
435
436 Ok(Self::Value {
437 app_permissions,
438 application_id,
439 authorizing_integration_owners,
440 channel,
441 channel_id,
442 context,
443 data,
444 entitlements,
445 guild,
446 guild_id,
447 guild_locale,
448 id,
449 kind,
450 locale,
451 member,
452 message,
453 token,
454 user,
455 })
456 }
457}
458
459#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
461#[non_exhaustive]
462#[serde(untagged)]
463pub enum InteractionData {
464 ApplicationCommand(Box<CommandData>),
470 MessageComponent(Box<MessageComponentInteractionData>),
474 ModalSubmit(Box<ModalInteractionData>),
478}
479
480#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, Hash)]
488pub struct InteractionPartialGuild {
489 pub id: Option<Id<GuildMarker>>,
491 pub features: Option<Vec<GuildFeature>>,
493 pub locale: Option<String>,
494}
495
496#[cfg(test)]
497mod tests {
498 use super::{
499 Interaction, InteractionData, InteractionDataResolved, InteractionMember, InteractionType,
500 application_command::{CommandData, CommandDataOption, CommandOptionValue},
501 };
502 use crate::{
503 application::{
504 command::{CommandOptionType, CommandType},
505 monetization::{EntitlementType, entitlement::Entitlement},
506 },
507 channel::Channel,
508 guild::{MemberFlags, PartialMember, Permissions},
509 id::Id,
510 oauth::ApplicationIntegrationMap,
511 test::image_hash,
512 user::User,
513 util::datetime::{Timestamp, TimestampParseError},
514 };
515 use serde_test::Token;
516 use std::{collections::HashMap, str::FromStr};
517
518 #[test]
519 #[allow(clippy::too_many_lines, deprecated)]
520 fn test_interaction_full() -> Result<(), TimestampParseError> {
521 let joined_at = Some(Timestamp::from_str("2020-01-01T00:00:00.000000+00:00")?);
522 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
523
524 let value = Interaction {
525 app_permissions: Some(Permissions::SEND_MESSAGES),
526 application_id: Id::new(100),
527 authorizing_integration_owners: ApplicationIntegrationMap {
528 guild: None,
529 user: None,
530 },
531 channel: Some(Channel {
532 bitrate: None,
533 guild_id: None,
534 id: Id::new(400),
535 kind: crate::channel::ChannelType::GuildText,
536 last_message_id: None,
537 last_pin_timestamp: None,
538 name: None,
539 nsfw: None,
540 owner_id: None,
541 parent_id: None,
542 permission_overwrites: None,
543 position: None,
544 rate_limit_per_user: None,
545 recipients: None,
546 rtc_region: None,
547 topic: None,
548 user_limit: None,
549 application_id: None,
550 applied_tags: None,
551 available_tags: None,
552 default_auto_archive_duration: None,
553 default_forum_layout: None,
554 default_reaction_emoji: None,
555 default_sort_order: None,
556 default_thread_rate_limit_per_user: None,
557 flags: None,
558 icon: None,
559 invitable: None,
560 managed: None,
561 member: None,
562 member_count: None,
563 message_count: None,
564 newly_created: None,
565 thread_metadata: None,
566 video_quality_mode: None,
567 }),
568 channel_id: Some(Id::new(200)),
569 context: None,
570 data: Some(InteractionData::ApplicationCommand(Box::new(CommandData {
571 guild_id: None,
572 id: Id::new(300),
573 name: "command name".into(),
574 kind: CommandType::ChatInput,
575 options: Vec::from([CommandDataOption {
576 name: "member".into(),
577 value: CommandOptionValue::User(Id::new(600)),
578 }]),
579 resolved: Some(InteractionDataResolved {
580 attachments: HashMap::new(),
581 channels: HashMap::new(),
582 members: IntoIterator::into_iter([(
583 Id::new(600),
584 InteractionMember {
585 avatar: None,
586 avatar_decoration_data: None,
587 banner: None,
588 communication_disabled_until: None,
589 flags,
590 joined_at,
591 nick: Some("nickname".into()),
592 pending: false,
593 permissions: Permissions::empty(),
594 premium_since: None,
595 roles: Vec::new(),
596 },
597 )])
598 .collect(),
599 messages: HashMap::new(),
600 roles: HashMap::new(),
601 users: IntoIterator::into_iter([(
602 Id::new(600),
603 User {
604 accent_color: None,
605 avatar: Some(image_hash::AVATAR),
606 avatar_decoration: None,
607 avatar_decoration_data: None,
608 banner: None,
609 bot: false,
610 discriminator: 1111,
611 email: None,
612 flags: None,
613 global_name: Some("test".into()),
614 id: Id::new(600),
615 locale: None,
616 mfa_enabled: None,
617 name: "username".into(),
618 premium_type: None,
619 primary_guild: None,
620 public_flags: None,
621 system: None,
622 verified: None,
623 },
624 )])
625 .collect(),
626 }),
627 target_id: None,
628 }))),
629 entitlements: vec![Entitlement {
630 application_id: Id::new(100),
631 consumed: Some(false),
632 deleted: false,
633 ends_at: None,
634 guild_id: None,
635 id: Id::new(200),
636 kind: EntitlementType::ApplicationSubscription,
637 sku_id: Id::new(300),
638 starts_at: None,
639 user_id: None,
640 }],
641 guild: None,
642 guild_id: Some(Id::new(400)),
643 guild_locale: Some("de".to_owned()),
644 id: Id::new(500),
645 kind: InteractionType::ApplicationCommand,
646 locale: Some("en-GB".to_owned()),
647 member: Some(PartialMember {
648 avatar: None,
649 avatar_decoration_data: None,
650 banner: None,
651 communication_disabled_until: None,
652 deaf: false,
653 flags,
654 joined_at,
655 mute: false,
656 nick: Some("nickname".into()),
657 permissions: Some(Permissions::empty()),
658 premium_since: None,
659 roles: Vec::new(),
660 user: Some(User {
661 accent_color: None,
662 avatar: Some(image_hash::AVATAR),
663 avatar_decoration: None,
664 avatar_decoration_data: None,
665 banner: None,
666 bot: false,
667 discriminator: 1111,
668 email: None,
669 flags: None,
670 global_name: Some("test".into()),
671 id: Id::new(600),
672 locale: None,
673 mfa_enabled: None,
674 name: "username".into(),
675 premium_type: None,
676 primary_guild: None,
677 public_flags: None,
678 system: None,
679 verified: None,
680 }),
681 }),
682 message: None,
683 token: "interaction token".into(),
684 user: None,
685 };
686
687 serde_test::assert_ser_tokens(
689 &value,
690 &[
691 Token::Struct {
692 name: "Interaction",
693 len: 14,
694 },
695 Token::Str("app_permissions"),
696 Token::Some,
697 Token::Str("2048"),
698 Token::Str("application_id"),
699 Token::NewtypeStruct { name: "Id" },
700 Token::Str("100"),
701 Token::Str("authorizing_integration_owners"),
702 Token::Struct {
703 name: "ApplicationIntegrationMap",
704 len: 0,
705 },
706 Token::StructEnd,
707 Token::Str("channel"),
708 Token::Some,
709 Token::Struct {
710 name: "Channel",
711 len: 2,
712 },
713 Token::Str("id"),
714 Token::NewtypeStruct { name: "Id" },
715 Token::Str("400"),
716 Token::Str("type"),
717 Token::U8(0),
718 Token::StructEnd,
719 Token::Str("channel_id"),
720 Token::Some,
721 Token::NewtypeStruct { name: "Id" },
722 Token::Str("200"),
723 Token::Str("data"),
724 Token::Some,
725 Token::Struct {
726 name: "CommandData",
727 len: 5,
728 },
729 Token::Str("id"),
730 Token::NewtypeStruct { name: "Id" },
731 Token::Str("300"),
732 Token::Str("name"),
733 Token::Str("command name"),
734 Token::Str("type"),
735 Token::U8(1),
736 Token::Str("options"),
737 Token::Seq { len: Some(1) },
738 Token::Struct {
739 name: "CommandDataOption",
740 len: 3,
741 },
742 Token::Str("name"),
743 Token::Str("member"),
744 Token::Str("type"),
745 Token::U8(CommandOptionType::User as u8),
746 Token::Str("value"),
747 Token::NewtypeStruct { name: "Id" },
748 Token::Str("600"),
749 Token::StructEnd,
750 Token::SeqEnd,
751 Token::Str("resolved"),
752 Token::Some,
753 Token::Struct {
754 name: "InteractionDataResolved",
755 len: 2,
756 },
757 Token::Str("members"),
758 Token::Map { len: Some(1) },
759 Token::NewtypeStruct { name: "Id" },
760 Token::Str("600"),
761 Token::Struct {
762 name: "InteractionMember",
763 len: 7,
764 },
765 Token::Str("communication_disabled_until"),
766 Token::None,
767 Token::Str("flags"),
768 Token::U64(flags.bits()),
769 Token::Str("joined_at"),
770 Token::Some,
771 Token::Str("2020-01-01T00:00:00.000000+00:00"),
772 Token::Str("nick"),
773 Token::Some,
774 Token::Str("nickname"),
775 Token::Str("pending"),
776 Token::Bool(false),
777 Token::Str("permissions"),
778 Token::Str("0"),
779 Token::Str("roles"),
780 Token::Seq { len: Some(0) },
781 Token::SeqEnd,
782 Token::StructEnd,
783 Token::MapEnd,
784 Token::Str("users"),
785 Token::Map { len: Some(1) },
786 Token::NewtypeStruct { name: "Id" },
787 Token::Str("600"),
788 Token::Struct {
789 name: "User",
790 len: 10,
791 },
792 Token::Str("accent_color"),
793 Token::None,
794 Token::Str("avatar"),
795 Token::Some,
796 Token::Str(image_hash::AVATAR_INPUT),
797 Token::Str("avatar_decoration"),
798 Token::None,
799 Token::Str("avatar_decoration_data"),
800 Token::None,
801 Token::Str("banner"),
802 Token::None,
803 Token::Str("bot"),
804 Token::Bool(false),
805 Token::Str("discriminator"),
806 Token::Str("1111"),
807 Token::Str("global_name"),
808 Token::Some,
809 Token::Str("test"),
810 Token::Str("id"),
811 Token::NewtypeStruct { name: "Id" },
812 Token::Str("600"),
813 Token::Str("username"),
814 Token::Str("username"),
815 Token::StructEnd,
816 Token::MapEnd,
817 Token::StructEnd,
818 Token::StructEnd,
819 Token::Str("entitlements"),
820 Token::Seq { len: Some(1) },
821 Token::Struct {
822 name: "Entitlement",
823 len: 6,
824 },
825 Token::Str("application_id"),
826 Token::NewtypeStruct { name: "Id" },
827 Token::Str("100"),
828 Token::Str("consumed"),
829 Token::Some,
830 Token::Bool(false),
831 Token::Str("deleted"),
832 Token::Bool(false),
833 Token::Str("id"),
834 Token::NewtypeStruct { name: "Id" },
835 Token::Str("200"),
836 Token::Str("type"),
837 Token::U8(8),
838 Token::Str("sku_id"),
839 Token::NewtypeStruct { name: "Id" },
840 Token::Str("300"),
841 Token::StructEnd,
842 Token::SeqEnd,
843 Token::Str("guild_id"),
844 Token::Some,
845 Token::NewtypeStruct { name: "Id" },
846 Token::Str("400"),
847 Token::Str("guild_locale"),
848 Token::Some,
849 Token::String("de"),
850 Token::Str("id"),
851 Token::NewtypeStruct { name: "Id" },
852 Token::Str("500"),
853 Token::Str("type"),
854 Token::U8(InteractionType::ApplicationCommand as u8),
855 Token::Str("locale"),
856 Token::Some,
857 Token::Str("en-GB"),
858 Token::Str("member"),
859 Token::Some,
860 Token::Struct {
861 name: "PartialMember",
862 len: 9,
863 },
864 Token::Str("communication_disabled_until"),
865 Token::None,
866 Token::Str("deaf"),
867 Token::Bool(false),
868 Token::Str("flags"),
869 Token::U64(flags.bits()),
870 Token::Str("joined_at"),
871 Token::Some,
872 Token::Str("2020-01-01T00:00:00.000000+00:00"),
873 Token::Str("mute"),
874 Token::Bool(false),
875 Token::Str("nick"),
876 Token::Some,
877 Token::Str("nickname"),
878 Token::Str("permissions"),
879 Token::Some,
880 Token::Str("0"),
881 Token::Str("roles"),
882 Token::Seq { len: Some(0) },
883 Token::SeqEnd,
884 Token::Str("user"),
885 Token::Some,
886 Token::Struct {
887 name: "User",
888 len: 10,
889 },
890 Token::Str("accent_color"),
891 Token::None,
892 Token::Str("avatar"),
893 Token::Some,
894 Token::Str(image_hash::AVATAR_INPUT),
895 Token::Str("avatar_decoration"),
896 Token::None,
897 Token::Str("avatar_decoration_data"),
898 Token::None,
899 Token::Str("banner"),
900 Token::None,
901 Token::Str("bot"),
902 Token::Bool(false),
903 Token::Str("discriminator"),
904 Token::Str("1111"),
905 Token::Str("global_name"),
906 Token::Some,
907 Token::Str("test"),
908 Token::Str("id"),
909 Token::NewtypeStruct { name: "Id" },
910 Token::Str("600"),
911 Token::Str("username"),
912 Token::Str("username"),
913 Token::StructEnd,
914 Token::StructEnd,
915 Token::Str("token"),
916 Token::Str("interaction token"),
917 Token::StructEnd,
918 ],
919 );
920
921 Ok(())
922 }
923}