1use std::collections::HashMap;
2
3use serde::de::{Deserializer, Error as DeError};
4use serde::ser::{Error as _, Serializer};
5use serde::{Deserialize, Serialize};
6
7use super::{AuthorizingIntegrationOwners, InteractionContext};
8#[cfg(feature = "model")]
9use crate::builder::{
10 Builder,
11 CreateInteractionResponse,
12 CreateInteractionResponseFollowup,
13 CreateInteractionResponseMessage,
14 EditInteractionResponse,
15};
16#[cfg(feature = "collector")]
17use crate::client::Context;
18#[cfg(feature = "model")]
19use crate::http::{CacheHttp, Http};
20use crate::internal::prelude::*;
21use crate::json::{self, JsonError};
22use crate::model::application::{CommandOptionType, CommandType};
23use crate::model::channel::{Attachment, Message, PartialChannel};
24use crate::model::guild::{Member, PartialMember, Role};
25use crate::model::id::{
26 ApplicationId,
27 AttachmentId,
28 ChannelId,
29 CommandId,
30 GenericId,
31 GuildId,
32 InteractionId,
33 MessageId,
34 RoleId,
35 TargetId,
36 UserId,
37};
38use crate::model::monetization::Entitlement;
39use crate::model::user::User;
40use crate::model::Permissions;
41#[cfg(all(feature = "collector", feature = "utils"))]
42use crate::utils::{CreateQuickModal, QuickModalResponse};
43
44#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
48#[derive(Clone, Debug, Deserialize, Serialize)]
49#[serde(remote = "Self")]
50#[non_exhaustive]
51pub struct CommandInteraction {
52 pub id: InteractionId,
54 pub application_id: ApplicationId,
56 pub data: CommandData,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub guild_id: Option<GuildId>,
61 pub channel: Option<PartialChannel>,
63 pub channel_id: ChannelId,
65 #[serde(skip_serializing_if = "Option::is_none")]
69 pub member: Option<Box<Member>>,
70 #[serde(default)]
72 pub user: User,
73 pub token: String,
75 pub version: u8,
77 pub app_permissions: Option<Permissions>,
80 pub locale: String,
82 pub guild_locale: Option<String>,
84 pub entitlements: Vec<Entitlement>,
86 #[serde(default)]
88 pub authorizing_integration_owners: AuthorizingIntegrationOwners,
89 pub context: Option<InteractionContext>,
91}
92
93#[cfg(feature = "model")]
94impl CommandInteraction {
95 pub async fn get_response(&self, http: impl AsRef<Http>) -> Result<Message> {
101 http.as_ref().get_original_interaction_response(&self.token).await
102 }
103
104 pub async fn create_response(
114 &self,
115 cache_http: impl CacheHttp,
116 builder: CreateInteractionResponse,
117 ) -> Result<()> {
118 builder.execute(cache_http, (self.id, &self.token)).await
119 }
120
121 pub async fn edit_response(
131 &self,
132 cache_http: impl CacheHttp,
133 builder: EditInteractionResponse,
134 ) -> Result<Message> {
135 builder.execute(cache_http, &self.token).await
136 }
137
138 pub async fn delete_response(&self, http: impl AsRef<Http>) -> Result<()> {
147 http.as_ref().delete_original_interaction_response(&self.token).await
148 }
149
150 pub async fn create_followup(
160 &self,
161 cache_http: impl CacheHttp,
162 builder: CreateInteractionResponseFollowup,
163 ) -> Result<Message> {
164 builder.execute(cache_http, (None, &self.token)).await
165 }
166
167 pub async fn edit_followup(
177 &self,
178 cache_http: impl CacheHttp,
179 message_id: impl Into<MessageId>,
180 builder: CreateInteractionResponseFollowup,
181 ) -> Result<Message> {
182 builder.execute(cache_http, (Some(message_id.into()), &self.token)).await
183 }
184
185 pub async fn delete_followup<M: Into<MessageId>>(
192 &self,
193 http: impl AsRef<Http>,
194 message_id: M,
195 ) -> Result<()> {
196 http.as_ref().delete_followup_message(&self.token, message_id.into()).await
197 }
198
199 pub async fn get_followup<M: Into<MessageId>>(
206 &self,
207 http: impl AsRef<Http>,
208 message_id: M,
209 ) -> Result<Message> {
210 http.as_ref().get_followup_message(&self.token, message_id.into()).await
211 }
212
213 pub async fn defer(&self, cache_http: impl CacheHttp) -> Result<()> {
220 let builder = CreateInteractionResponse::Defer(CreateInteractionResponseMessage::default());
221 self.create_response(cache_http, builder).await
222 }
223
224 pub async fn defer_ephemeral(&self, cache_http: impl CacheHttp) -> Result<()> {
231 let builder = CreateInteractionResponse::Defer(
232 CreateInteractionResponseMessage::new().ephemeral(true),
233 );
234 self.create_response(cache_http, builder).await
235 }
236
237 #[cfg(all(feature = "collector", feature = "utils"))]
243 pub async fn quick_modal(
244 &self,
245 ctx: &Context,
246 builder: CreateQuickModal,
247 ) -> Result<Option<QuickModalResponse>> {
248 builder.execute(ctx, self.id, &self.token).await
249 }
250}
251
252impl<'de> Deserialize<'de> for CommandInteraction {
254 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
255 let mut interaction = Self::deserialize(deserializer)?;
257 if let Some(guild_id) = interaction.guild_id {
258 if let Some(member) = &mut interaction.member {
259 member.guild_id = guild_id;
260 interaction.user = member.user.clone();
262 }
263 interaction.data.resolved.roles.values_mut().for_each(|r| r.guild_id = guild_id);
264 }
265 Ok(interaction)
266 }
267}
268
269impl Serialize for CommandInteraction {
270 fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
271 Self::serialize(self, serializer)
273 }
274}
275
276#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
280#[derive(Clone, Debug, Deserialize, Serialize)]
281#[non_exhaustive]
282pub struct CommandData {
283 pub id: CommandId,
285 pub name: String,
287 #[serde(rename = "type")]
289 pub kind: CommandType,
290 #[serde(default)]
292 pub resolved: CommandDataResolved,
293 #[serde(default)]
294 pub options: Vec<CommandDataOption>,
295 #[serde(skip_serializing_if = "Option::is_none")]
297 pub guild_id: Option<GuildId>,
298 pub target_id: Option<TargetId>,
307}
308
309impl CommandData {
310 #[must_use]
312 pub fn autocomplete(&self) -> Option<AutocompleteOption<'_>> {
313 fn find_option(opts: &[CommandDataOption]) -> Option<AutocompleteOption<'_>> {
314 for opt in opts {
315 match &opt.value {
316 CommandDataOptionValue::SubCommand(opts)
317 | CommandDataOptionValue::SubCommandGroup(opts) => {
318 return find_option(opts);
319 },
320 CommandDataOptionValue::Autocomplete {
321 kind,
322 value,
323 } => {
324 return Some(AutocompleteOption {
325 name: &opt.name,
326 kind: *kind,
327 value,
328 });
329 },
330 _ => {},
331 }
332 }
333 None
334 }
335 find_option(&self.options)
336 }
337
338 #[must_use]
340 pub fn options(&self) -> Vec<ResolvedOption<'_>> {
341 fn resolve_options<'a>(
342 opts: &'a [CommandDataOption],
343 resolved: &'a CommandDataResolved,
344 ) -> Vec<ResolvedOption<'a>> {
345 let mut options = Vec::new();
346 for opt in opts {
347 let value = match &opt.value {
348 CommandDataOptionValue::SubCommand(opts) => {
349 ResolvedValue::SubCommand(resolve_options(opts, resolved))
350 },
351 CommandDataOptionValue::SubCommandGroup(opts) => {
352 ResolvedValue::SubCommandGroup(resolve_options(opts, resolved))
353 },
354 CommandDataOptionValue::Autocomplete {
355 kind,
356 value,
357 } => ResolvedValue::Autocomplete {
358 kind: *kind,
359 value,
360 },
361 CommandDataOptionValue::Boolean(v) => ResolvedValue::Boolean(*v),
362 CommandDataOptionValue::Integer(v) => ResolvedValue::Integer(*v),
363 CommandDataOptionValue::Number(v) => ResolvedValue::Number(*v),
364 CommandDataOptionValue::String(v) => ResolvedValue::String(v),
365 CommandDataOptionValue::Attachment(id) => resolved.attachments.get(id).map_or(
366 ResolvedValue::Unresolved(Unresolved::Attachment(*id)),
367 ResolvedValue::Attachment,
368 ),
369 CommandDataOptionValue::Channel(id) => resolved.channels.get(id).map_or(
370 ResolvedValue::Unresolved(Unresolved::Channel(*id)),
371 ResolvedValue::Channel,
372 ),
373 CommandDataOptionValue::Mentionable(id) => {
374 let user_id = UserId::new(id.get());
375 let value = if let Some(user) = resolved.users.get(&user_id) {
376 Some(ResolvedValue::User(user, resolved.members.get(&user_id)))
377 } else {
378 resolved.roles.get(&RoleId::new(id.get())).map(ResolvedValue::Role)
379 };
380 value.unwrap_or(ResolvedValue::Unresolved(Unresolved::Mentionable(*id)))
381 },
382 CommandDataOptionValue::User(id) => resolved
383 .users
384 .get(id)
385 .map(|u| ResolvedValue::User(u, resolved.members.get(id)))
386 .unwrap_or(ResolvedValue::Unresolved(Unresolved::User(*id))),
387 CommandDataOptionValue::Role(id) => resolved.roles.get(id).map_or(
388 ResolvedValue::Unresolved(Unresolved::RoleId(*id)),
389 ResolvedValue::Role,
390 ),
391 CommandDataOptionValue::Unknown(unknown) => {
392 ResolvedValue::Unresolved(Unresolved::Unknown(*unknown))
393 },
394 };
395
396 options.push(ResolvedOption {
397 name: &opt.name,
398 value,
399 });
400 }
401 options
402 }
403
404 resolve_options(&self.options, &self.resolved)
405 }
406
407 #[must_use]
411 pub fn target(&self) -> Option<ResolvedTarget<'_>> {
412 match (self.kind, self.target_id) {
413 (CommandType::User, Some(id)) => {
414 let user_id = id.to_user_id();
415
416 let user = self.resolved.users.get(&user_id)?;
417 let member = self.resolved.members.get(&user_id);
418
419 Some(ResolvedTarget::User(user, member))
420 },
421 (CommandType::Message, Some(id)) => {
422 let message_id = id.to_message_id();
423 let message = self.resolved.messages.get(&message_id)?;
424
425 Some(ResolvedTarget::Message(message))
426 },
427 _ => None,
428 }
429 }
430}
431
432#[derive(Clone, Debug)]
434#[non_exhaustive]
435pub struct AutocompleteOption<'a> {
436 pub name: &'a str,
437 pub kind: CommandOptionType,
438 pub value: &'a str,
439}
440
441#[derive(Clone, Debug)]
442#[non_exhaustive]
443pub struct ResolvedOption<'a> {
444 pub name: &'a str,
445 pub value: ResolvedValue<'a>,
446}
447
448#[derive(Clone, Debug)]
450#[non_exhaustive]
451pub enum ResolvedValue<'a> {
452 Autocomplete { kind: CommandOptionType, value: &'a str },
453 Boolean(bool),
454 Integer(i64),
455 Number(f64),
456 String(&'a str),
457 SubCommand(Vec<ResolvedOption<'a>>),
458 SubCommandGroup(Vec<ResolvedOption<'a>>),
459 Attachment(&'a Attachment),
460 Channel(&'a PartialChannel),
461 Role(&'a Role),
462 User(&'a User, Option<&'a PartialMember>),
463 Unresolved(Unresolved),
464}
465
466#[derive(Clone, Debug)]
468#[non_exhaustive]
469pub enum Unresolved {
470 Attachment(AttachmentId),
471 Channel(ChannelId),
472 Mentionable(GenericId),
473 RoleId(RoleId),
474 User(UserId),
475 Unknown(u8),
477}
478
479#[derive(Clone, Debug)]
481#[non_exhaustive]
482pub enum ResolvedTarget<'a> {
483 User(&'a User, Option<&'a PartialMember>),
484 Message(&'a Message),
485}
486
487#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
492#[derive(Clone, Debug, Default, Deserialize, Serialize)]
493#[non_exhaustive]
494pub struct CommandDataResolved {
495 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
497 pub users: HashMap<UserId, User>,
498 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
500 pub members: HashMap<UserId, PartialMember>,
501 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
503 pub roles: HashMap<RoleId, Role>,
504 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
506 pub channels: HashMap<ChannelId, PartialChannel>,
507 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
509 pub messages: HashMap<MessageId, Message>,
510 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
512 pub attachments: HashMap<AttachmentId, Attachment>,
513}
514
515#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
525#[derive(Clone, Debug, PartialEq)]
526#[non_exhaustive]
527pub struct CommandDataOption {
528 pub name: String,
530 pub value: CommandDataOptionValue,
532}
533
534impl CommandDataOption {
535 #[must_use]
536 pub fn kind(&self) -> CommandOptionType {
537 self.value.kind()
538 }
539}
540
541#[derive(Deserialize, Serialize)]
542struct RawCommandDataOption {
543 name: String,
544 #[serde(rename = "type")]
545 kind: CommandOptionType,
546 #[serde(skip_serializing_if = "Option::is_none")]
547 value: Option<json::Value>,
548 #[serde(skip_serializing_if = "Option::is_none")]
549 options: Option<Vec<RawCommandDataOption>>,
550 #[serde(skip_serializing_if = "Option::is_none")]
551 focused: Option<bool>,
552}
553
554fn option_from_raw(raw: RawCommandDataOption) -> Result<CommandDataOption> {
555 macro_rules! value {
556 () => {{
557 json::from_value(
558 raw.value.ok_or_else::<JsonError, _>(|| DeError::missing_field("value"))?,
559 )?
560 }};
561 }
562
563 let value = match raw.kind {
564 _ if raw.focused == Some(true) => CommandDataOptionValue::Autocomplete {
565 kind: raw.kind,
566 value: value!(),
567 },
568 CommandOptionType::Boolean => CommandDataOptionValue::Boolean(value!()),
569 CommandOptionType::Integer => CommandDataOptionValue::Integer(value!()),
570 CommandOptionType::Number => CommandDataOptionValue::Number(value!()),
571 CommandOptionType::String => CommandDataOptionValue::String(value!()),
572 CommandOptionType::SubCommand => {
573 let options =
574 raw.options.ok_or_else::<JsonError, _>(|| DeError::missing_field("options"))?;
575 let options = options.into_iter().map(option_from_raw).collect::<Result<_>>()?;
576 CommandDataOptionValue::SubCommand(options)
577 },
578 CommandOptionType::SubCommandGroup => {
579 let options =
580 raw.options.ok_or_else::<JsonError, _>(|| DeError::missing_field("options"))?;
581 let options = options.into_iter().map(option_from_raw).collect::<Result<_>>()?;
582 CommandDataOptionValue::SubCommandGroup(options)
583 },
584 CommandOptionType::Attachment => CommandDataOptionValue::Attachment(value!()),
585 CommandOptionType::Channel => CommandDataOptionValue::Channel(value!()),
586 CommandOptionType::Mentionable => CommandDataOptionValue::Mentionable(value!()),
587 CommandOptionType::Role => CommandDataOptionValue::Role(value!()),
588 CommandOptionType::User => CommandDataOptionValue::User(value!()),
589 CommandOptionType::Unknown(unknown) => CommandDataOptionValue::Unknown(unknown),
590 };
591
592 Ok(CommandDataOption {
593 name: raw.name,
594 value,
595 })
596}
597
598fn option_to_raw(option: &CommandDataOption) -> Result<RawCommandDataOption> {
599 let mut raw = RawCommandDataOption {
600 name: option.name.clone(),
601 kind: option.kind(),
602 value: None,
603 options: None,
604 focused: None,
605 };
606
607 match &option.value {
608 CommandDataOptionValue::Autocomplete {
609 kind: _,
610 value,
611 } => {
612 raw.value = Some(json::to_value(value)?);
613 raw.focused = Some(true);
614 },
615 CommandDataOptionValue::Boolean(v) => raw.value = Some(json::to_value(v)?),
616 CommandDataOptionValue::Integer(v) => raw.value = Some(json::to_value(v)?),
617 CommandDataOptionValue::Number(v) => raw.value = Some(json::to_value(v)?),
618 CommandDataOptionValue::String(v) => raw.value = Some(json::to_value(v)?),
619 CommandDataOptionValue::SubCommand(o) | CommandDataOptionValue::SubCommandGroup(o) => {
620 raw.options = Some(o.iter().map(option_to_raw).collect::<Result<_>>()?);
621 },
622 CommandDataOptionValue::Attachment(v) => raw.value = Some(json::to_value(v)?),
623 CommandDataOptionValue::Channel(v) => raw.value = Some(json::to_value(v)?),
624 CommandDataOptionValue::Mentionable(v) => raw.value = Some(json::to_value(v)?),
625 CommandDataOptionValue::Role(v) => raw.value = Some(json::to_value(v)?),
626 CommandDataOptionValue::User(v) => raw.value = Some(json::to_value(v)?),
627 CommandDataOptionValue::Unknown(_) => {},
628 }
629
630 Ok(raw)
631}
632
633impl<'de> Deserialize<'de> for CommandDataOption {
635 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
636 option_from_raw(RawCommandDataOption::deserialize(deserializer)?).map_err(D::Error::custom)
637 }
638}
639
640impl Serialize for CommandDataOption {
641 fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
642 option_to_raw(self).map_err(S::Error::custom)?.serialize(serializer)
643 }
644}
645
646#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
650#[derive(Clone, Debug, PartialEq)]
651#[non_exhaustive]
652pub enum CommandDataOptionValue {
653 Autocomplete { kind: CommandOptionType, value: String },
654 Boolean(bool),
655 Integer(i64),
656 Number(f64),
657 String(String),
658 SubCommand(Vec<CommandDataOption>),
659 SubCommandGroup(Vec<CommandDataOption>),
660 Attachment(AttachmentId),
661 Channel(ChannelId),
662 Mentionable(GenericId),
663 Role(RoleId),
664 User(UserId),
665 Unknown(u8),
666}
667
668impl CommandDataOptionValue {
669 #[must_use]
670 pub fn kind(&self) -> CommandOptionType {
671 match self {
672 Self::Autocomplete {
673 kind, ..
674 } => *kind,
675 Self::Boolean(_) => CommandOptionType::Boolean,
676 Self::Integer(_) => CommandOptionType::Integer,
677 Self::Number(_) => CommandOptionType::Number,
678 Self::String(_) => CommandOptionType::String,
679 Self::SubCommand(_) => CommandOptionType::SubCommand,
680 Self::SubCommandGroup(_) => CommandOptionType::SubCommandGroup,
681 Self::Attachment(_) => CommandOptionType::Attachment,
682 Self::Channel(_) => CommandOptionType::Channel,
683 Self::Mentionable(_) => CommandOptionType::Mentionable,
684 Self::Role(_) => CommandOptionType::Role,
685 Self::User(_) => CommandOptionType::User,
686 Self::Unknown(unknown) => CommandOptionType::Unknown(*unknown),
687 }
688 }
689
690 #[must_use]
692 pub fn as_bool(&self) -> Option<bool> {
693 match *self {
694 Self::Boolean(b) => Some(b),
695 _ => None,
696 }
697 }
698
699 #[must_use]
701 pub fn as_i64(&self) -> Option<i64> {
702 match *self {
703 Self::Integer(v) => Some(v),
704 _ => None,
705 }
706 }
707
708 #[must_use]
710 pub fn as_f64(&self) -> Option<f64> {
711 match *self {
712 Self::Number(v) => Some(v),
713 _ => None,
714 }
715 }
716
717 #[must_use]
719 pub fn as_str(&self) -> Option<&str> {
720 match self {
721 Self::String(s) => Some(s),
722 Self::Autocomplete {
723 value, ..
724 } => Some(value),
725 _ => None,
726 }
727 }
728
729 #[must_use]
731 pub fn as_attachment_id(&self) -> Option<AttachmentId> {
732 match self {
733 Self::Attachment(id) => Some(*id),
734 _ => None,
735 }
736 }
737
738 #[must_use]
740 pub fn as_channel_id(&self) -> Option<ChannelId> {
741 match self {
742 Self::Channel(id) => Some(*id),
743 _ => None,
744 }
745 }
746
747 #[must_use]
749 pub fn as_mentionable(&self) -> Option<GenericId> {
750 match self {
751 Self::Mentionable(id) => Some(*id),
752 _ => None,
753 }
754 }
755
756 #[must_use]
758 pub fn as_user_id(&self) -> Option<UserId> {
759 match self {
760 Self::User(id) => Some(*id),
761 _ => None,
762 }
763 }
764
765 #[must_use]
767 pub fn as_role_id(&self) -> Option<RoleId> {
768 match self {
769 Self::Role(id) => Some(*id),
770 _ => None,
771 }
772 }
773}
774
775impl TargetId {
776 #[must_use]
778 pub fn to_user_id(self) -> UserId {
779 self.get().into()
780 }
781
782 #[must_use]
784 pub fn to_message_id(self) -> MessageId {
785 self.get().into()
786 }
787}
788
789impl From<MessageId> for TargetId {
790 fn from(id: MessageId) -> Self {
791 Self::new(id.into())
792 }
793}
794
795impl From<UserId> for TargetId {
796 fn from(id: UserId) -> Self {
797 Self::new(id.into())
798 }
799}
800
801impl From<TargetId> for MessageId {
802 fn from(id: TargetId) -> Self {
803 Self::new(id.into())
804 }
805}
806
807impl From<TargetId> for UserId {
808 fn from(id: TargetId) -> Self {
809 Self::new(id.into())
810 }
811}
812
813#[cfg(test)]
814mod tests {
815 use super::*;
816 use crate::json::{assert_json, json};
817
818 #[test]
819 fn nested_options() {
820 let value = CommandDataOption {
821 name: "subcommand_group".into(),
822 value: CommandDataOptionValue::SubCommandGroup(vec![CommandDataOption {
823 name: "subcommand".into(),
824 value: CommandDataOptionValue::SubCommand(vec![CommandDataOption {
825 name: "channel".into(),
826 value: CommandDataOptionValue::Channel(ChannelId::new(3)),
827 }]),
828 }]),
829 };
830
831 assert_json(
832 &value,
833 json!({
834 "name": "subcommand_group",
835 "type": 2,
836 "options": [{
837 "name": "subcommand",
838 "type": 1,
839 "options": [{"name": "channel", "type": 7, "value": "3"}],
840 }]
841 }),
842 );
843 }
844
845 #[test]
846 fn mixed_options() {
847 let value = vec![
848 CommandDataOption {
849 name: "boolean".into(),
850 value: CommandDataOptionValue::Boolean(true),
851 },
852 CommandDataOption {
853 name: "integer".into(),
854 value: CommandDataOptionValue::Integer(1),
855 },
856 CommandDataOption {
857 name: "number".into(),
858 value: CommandDataOptionValue::Number(2.0),
859 },
860 CommandDataOption {
861 name: "string".into(),
862 value: CommandDataOptionValue::String("foobar".into()),
863 },
864 CommandDataOption {
865 name: "empty_subcommand".into(),
866 value: CommandDataOptionValue::SubCommand(vec![]),
867 },
868 CommandDataOption {
869 name: "autocomplete".into(),
870 value: CommandDataOptionValue::Autocomplete {
871 kind: CommandOptionType::Integer,
872 value: "not an integer".into(),
873 },
874 },
875 ];
876
877 assert_json(
878 &value,
879 json!([
880 {"name": "boolean", "type": 5, "value": true},
881 {"name": "integer", "type": 4, "value": 1},
882 {"name": "number", "type": 10, "value": 2.0},
883 {"name": "string", "type": 3, "value": "foobar"},
884 {"name": "empty_subcommand", "type": 1, "options": []},
885 {"name": "autocomplete", "type": 4, "value": "not an integer", "focused": true},
886 ]),
887 );
888 }
889}