You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

06-subcommands.md 6.6 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. # Subcommands
  2. Subcommands allow you to have multiple commands available in a single command. They can be useful for representing sub options for a command. For example: A settings command. Let's first look at some limitations with subcommands set by discord.
  3. - An app can have up to 25 subcommand groups on a top-level command
  4. - An app can have up to 25 subcommands within a subcommand group
  5. - commands can have up to 25 `options`
  6. - options can have up to 25 `choices`
  7. ```
  8. VALID
  9. command
  10. |
  11. |__ subcommand
  12. |
  13. |__ subcommand
  14. ----
  15. command
  16. |
  17. |__ subcommand-group
  18. |
  19. |__ subcommand
  20. |
  21. |__ subcommand-group
  22. |
  23. |__ subcommand
  24. -------
  25. INVALID
  26. command
  27. |
  28. |__ subcommand-group
  29. |
  30. |__ subcommand-group
  31. |
  32. |__ subcommand-group
  33. |
  34. |__ subcommand-group
  35. ----
  36. INVALID
  37. command
  38. |
  39. |__ subcommand
  40. |
  41. |__ subcommand-group
  42. |
  43. |__ subcommand
  44. |
  45. |__ subcommand-group
  46. ```
  47. Let's write a settings command that can change 3 fields in our bot.
  48. ```cs
  49. public string FieldA { get; set; } = "test";
  50. public int FieldB { get; set; } = 10;
  51. public bool FieldC { get; set; } = true;
  52. public async Task Client_Ready()
  53. {
  54. ulong guildId = 848176216011046962;
  55. var guildCommand = new SlashCommandBuilder()
  56. .WithName("settings")
  57. .WithDescription("Changes some settings within the bot.")
  58. .AddOption(new SlashCommandOptionBuilder()
  59. .WithName("field-a")
  60. .WithDescription("Gets or sets the field A")
  61. .WithType(ApplicationCommandOptionType.SubCommandGroup)
  62. .AddOption(new SlashCommandOptionBuilder()
  63. .WithName("set")
  64. .WithDescription("Sets the field A")
  65. .WithType(ApplicationCommandOptionType.SubCommand)
  66. .AddOption("value", ApplicationCommandOptionType.String, "the value to set the field ", required: true)
  67. ).AddOption(new SlashCommandOptionBuilder()
  68. .WithName("get")
  69. .WithDescription("Gets the value of field A.")
  70. .WithType(ApplicationCommandOptionType.SubCommand)
  71. )
  72. ).AddOption(new SlashCommandOptionBuilder()
  73. .WithName("field-b")
  74. .WithDescription("Gets or sets the field B")
  75. .WithType(ApplicationCommandOptionType.SubCommandGroup)
  76. .AddOption(new SlashCommandOptionBuilder()
  77. .WithName("set")
  78. .WithDescription("Sets the field B")
  79. .WithType(ApplicationCommandOptionType.SubCommand)
  80. .AddOption("value", ApplicationCommandOptionType.Integer, "the value to set the fie to.", required: true)
  81. ).AddOption(new SlashCommandOptionBuilder()
  82. .WithName("get")
  83. .WithDescription("Gets the value of field B.")
  84. .WithType(ApplicationCommandOptionType.SubCommand)
  85. )
  86. ).AddOption(new SlashCommandOptionBuilder()
  87. .WithName("field-c")
  88. .WithDescription("Gets or sets the field C")
  89. .WithType(ApplicationCommandOptionType.SubCommandGroup)
  90. .AddOption(new SlashCommandOptionBuilder()
  91. .WithName("set")
  92. .WithDescription("Sets the field C")
  93. .WithType(ApplicationCommandOptionType.SubCommand)
  94. .AddOption("value", ApplicationCommandOptionType.Boolean, "the value to set the fie to.", required: true)
  95. ).AddOption(new SlashCommandOptionBuilder()
  96. .WithName("get")
  97. .WithDescription("Gets the value of field C.")
  98. .WithType(ApplicationCommandOptionType.SubCommand)
  99. )
  100. );
  101. try
  102. {
  103. await client.Rest.CreateGuildCommand(guildCommand.Build(), guildId);
  104. }
  105. catch(ApplicationCommandException exception)
  106. {
  107. var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented);
  108. Console.WriteLine(json);
  109. }
  110. }
  111. ```
  112. All that code generates a command that looks like this:
  113. ![settings](images/settings1.png)
  114. Now that we have our command made, we need to handle the multiple options with this command. So lets add this into our handler:
  115. ```cs
  116. private async Task Client_InteractionCreated(SocketInteraction arg)
  117. {
  118. if(arg is SocketSlashCommand command)
  119. {
  120. // Let's add a switch statement for the command name so we can handle multiple commands in one event.
  121. switch(command.Data.Name)
  122. {
  123. case "list-roles":
  124. await HandleListRoleCommand(command);
  125. break;
  126. case "settings":
  127. await HandleSettingsCommand(command);
  128. break;
  129. }
  130. }
  131. }
  132. private async Task HandleSettingsCommand(SocketSlashCommand command)
  133. {
  134. // First lets extract our variables
  135. var fieldName = command.Data.Options.First().Name;
  136. var getOrSet = command.Data.Options.First().Options.First().Name;
  137. // Since there is no value on a get command, we use the ? operator because "Options" can be null.
  138. var value = command.Data.Options.First().Options.First().Options?.FirstOrDefault().Value;
  139. switch (fieldName)
  140. {
  141. case "field-a":
  142. {
  143. if(getOrSet == "get")
  144. {
  145. await command.RespondAsync($"The value of `field-a` is `{FieldA}`");
  146. }
  147. else if (getOrSet == "set")
  148. {
  149. this.FieldA = (string)value;
  150. await command.RespondAsync($"`field-a` has been set to `{FieldA}`");
  151. }
  152. }
  153. break;
  154. case "field-b":
  155. {
  156. if (getOrSet == "get")
  157. {
  158. await command.RespondAsync($"The value of `field-b` is `{FieldB}`");
  159. }
  160. else if (getOrSet == "set")
  161. {
  162. this.FieldB = (int)value;
  163. await command.RespondAsync($"`field-b` has been set to `{FieldB}`");
  164. }
  165. }
  166. break;
  167. case "field-c":
  168. {
  169. if (getOrSet == "get")
  170. {
  171. await command.RespondAsync($"The value of `field-c` is `{FieldC}`");
  172. }
  173. else if (getOrSet == "set")
  174. {
  175. this.FieldC = (bool)value;
  176. await command.RespondAsync($"`field-c` has been set to `{FieldC}`");
  177. }
  178. }
  179. break;
  180. }
  181. }
  182. ```
  183. Now, let's try this out! Running the 3 get commands seems to get the default values we set.
  184. ![settings get](images/settings2.png)
  185. Now let's try changing each to a different value.
  186. ![settings set](images/settings3.png)
  187. That has worked! Next, let't look at choices in commands.