diff --git a/src/Discord.Net.Core/Discord.Net.Core.xml b/src/Discord.Net.Core/Discord.Net.Core.xml
index f60639030..a4b16057d 100644
--- a/src/Discord.Net.Core/Discord.Net.Core.xml
+++ b/src/Discord.Net.Core/Discord.Net.Core.xml
@@ -4132,6 +4132,11 @@
read-only property, always 1.
+
+
+ Represents an interface used to specify classes that they are a vaild dataype of a class.
+
+
The response type for an .
@@ -4277,6 +4282,16 @@
Represents a builder for creating a .
+
+
+ The max length of a .
+
+
+
+
+ The max length of a .
+
+
The max amount of rows a message can have.
@@ -4287,6 +4302,36 @@
Gets or sets the Action Rows for this Component Builder.
+
+
+ Adds a to the first row, if the first row cannot
+ accept the component then it will add it to a row that can
+
+ The label of the menu.
+ The custom id of the menu.
+ The options of the menu.
+ The placeholder of the menu.
+ The min values of the placeholder.
+ The max values of the placeholder.
+ The row to add the menu to.
+
+
+
+
+ Adds a to the first row, if the first row cannot
+ accept the component then it will add it to a row that can
+
+ The menu to add
+ The current builder.
+
+
+
+ Adds a to the current builder at the specific row.
+
+ The menu to add.
+ The row to attempt to add this component on.
+ The current builder.
+
Adds a button to the specified row.
@@ -4336,14 +4381,14 @@
Gets or sets the components inside this row.
-
+
Adds a list of components to the current row.
The list of components to add.
The current builder.
-
+
Adds a component at the end of the current row.
@@ -4363,16 +4408,6 @@
Represents a class used to build 's.
-
-
- The max length of a .
-
-
-
-
- The max length of a .
-
-
Gets or sets the label of the current button.
@@ -4493,6 +4528,226 @@
A button cannot contain a URL and a CustomId.A button must have an Emote or a label.
+
+
+ Represents a class used to build 's.
+
+
+
+
+ The max length of a .
+
+
+
+
+ The maximum number of values for the and properties.
+
+
+
+
+ The maximum number of options a can have.
+
+
+
+
+ Gets or sets the label of the current select menu.
+
+
+
+
+ Gets or sets the custom id of the current select menu.
+
+
+
+
+ Gets or sets the placeholder text of the current select menu.
+
+
+
+
+ Gets or sets the minimum values of the current select menu.
+
+
+
+
+ Gets or sets the maximum values of the current select menu.
+
+
+
+
+ Gets or sets a collection of for this current select menu.
+
+
+
+
+ Creates a new instance of a .
+
+
+
+
+ Creates a new instance of a .
+
+ The custom id of this select menu.
+ The options for this select menu.
+
+
+
+ Sets the field label.
+
+ The value to set the field label to.
+
+ The current builder.
+
+
+
+
+ Sets the field CustomId.
+
+ The value to set the field CustomId to.
+
+ The current builder.
+
+
+
+
+ Sets the field placeholder.
+
+ The value to set the field placeholder to.
+
+ The current builder.
+
+
+
+
+ Sets the field minValues.
+
+ The value to set the field minValues to.
+
+ The current builder.
+
+
+
+
+ Sets the field maxValues.
+
+ The value to set the field maxValues to.
+
+ The current builder.
+
+
+
+
+ Sets the field options.
+
+ The value to set the field options to.
+
+ The current builder.
+
+
+
+
+ Builds a
+
+ The newly built
+
+
+
+ Represents a class used to build 's.
+
+
+
+
+ The maximum length of a .
+
+
+
+
+ Gets or sets the label of the current select menu.
+
+
+
+
+ Gets or sets the custom id of the current select menu.
+
+
+
+
+ Gets or sets this menu options description.
+
+
+
+
+ Gets or sets the emote of this option.
+
+
+
+
+ Gets or sets the whether or not this option will render selected by default.
+
+
+
+
+ Creates a new instance of a .
+
+
+
+
+ Creates a new instance of a .
+
+ The label for this option.
+ The value of this option.
+
+
+
+ Sets the field label.
+
+ The value to set the field label to.
+
+ The current builder.
+
+
+
+
+ Sets the field value.
+
+ The value to set the field value to.
+
+ The current builder.
+
+
+
+
+ Sets the field description.
+
+ The value to set the field description to.
+
+ The current builder.
+
+
+
+
+ Sets the field emote.
+
+ The value to set the field emote to.
+
+ The current builder.
+
+
+
+
+ Sets the field default.
+
+ The value to set the field default to.
+
+ The current builder.
+
+
+
+
+ Builds a .
+
+ The newly built .
+
Represents a type of a component
@@ -4508,6 +4763,11 @@
A clickable button
+
+
+ A select menu for picking from choices
+
+
The of this Message Component.
@@ -4528,6 +4788,69 @@
Returns a empty .
+
+
+ Represents a select menu component defined at
+
+
+
+
+
+
+
+ The custom id of this Select menu that will be sent with a .
+
+
+
+
+ The menus options to select from.
+
+
+
+
+ A custom placeholder text if nothing is selected, max 100 characters.
+
+
+
+
+ The minimum number of items that must be chosen; default 1, min 0, max 25
+
+
+
+
+ The maximum number of items that can be chosen; default 1, max 25
+
+
+
+
+ Represents a choice for a .
+
+
+
+
+ The user-facing name of the option, max 25 characters.
+
+
+
+
+ The dev-define value of the option, max 100 characters.
+
+
+
+
+ An additional description of the option, max 50 characters.
+
+
+
+
+ A that will be displayed with this menu option.
+
+
+
+
+ Will render this option as selected by default.
+
+
A class used to build slash commands.
diff --git a/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs b/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
index 5c409e837..466bf3e91 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
@@ -28,7 +28,7 @@ namespace Discord
///
/// Represents the data sent within this interaction.
///
- object Data { get; }
+ IDiscordInteractionData Data { get; }
///
/// A continuation token for responding to the interaction.
diff --git a/src/Discord.Net.Core/Entities/Interactions/IDiscordInteractionData.cs b/src/Discord.Net.Core/Entities/Interactions/IDiscordInteractionData.cs
new file mode 100644
index 000000000..7083810b7
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/IDiscordInteractionData.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ ///
+ /// Represents an interface used to specify classes that they are a vaild dataype of a class.
+ ///
+ public interface IDiscordInteractionData { }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/Message Components/ActionRowComponent.cs b/src/Discord.Net.Core/Entities/Interactions/Message Components/ActionRowComponent.cs
index c29ef44a2..c5bbc8d08 100644
--- a/src/Discord.Net.Core/Entities/Interactions/Message Components/ActionRowComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/Message Components/ActionRowComponent.cs
@@ -18,10 +18,10 @@ namespace Discord
///
/// The child components in this row.
///
- public IReadOnlyCollection Components { get; internal set; }
+ public IReadOnlyCollection Components { get; internal set; }
internal ActionRowComponent() { }
- internal ActionRowComponent(List components)
+ internal ActionRowComponent(List components)
{
this.Components = components;
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs
index e8b0ce19c..afd1e34bd 100644
--- a/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs
@@ -11,6 +11,16 @@ namespace Discord
///
public class ComponentBuilder
{
+ ///
+ /// The max length of a .
+ ///
+ public const int MaxLabelLength = 80;
+
+ ///
+ /// The max length of a .
+ ///
+ public const int MaxCustomIdLength = 100;
+
///
/// The max amount of rows a message can have.
///
@@ -34,6 +44,84 @@ namespace Discord
private List _actionRows { get; set; }
+ ///
+ /// Adds a to the first row, if the first row cannot
+ /// accept the component then it will add it to a row that can
+ ///
+ /// The label of the menu.
+ /// The custom id of the menu.
+ /// The options of the menu.
+ /// The placeholder of the menu.
+ /// The min values of the placeholder.
+ /// The max values of the placeholder.
+ /// The row to add the menu to.
+ ///
+ public ComponentBuilder WithSelectMenu(string label, string customId, List options,
+ string placeholder = null, int minValues = 1, int maxValues = 1, int row = 0)
+ {
+ return WithSelectMenu(new SelectMenuBuilder()
+ .WithLabel(label)
+ .WithCustomId(customId)
+ .WithOptions(options)
+ .WithPlaceholder(placeholder)
+ .WithMaxValues(maxValues)
+ .WithMinValues(minValues),
+ row);
+ }
+
+ ///
+ /// Adds a to the first row, if the first row cannot
+ /// accept the component then it will add it to a row that can
+ ///
+ /// The menu to add
+ /// The current builder.
+ public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu)
+ => WithSelectMenu(menu, 0);
+
+ ///
+ /// Adds a to the current builder at the specific row.
+ ///
+ /// The menu to add.
+ /// The row to attempt to add this component on.
+ /// The current builder.
+ public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu, int row)
+ {
+ Preconditions.LessThan(row, 5, nameof(row));
+
+ var builtMenu = menu.Build();
+
+ if (_actionRows == null)
+ {
+ _actionRows = new List();
+ _actionRows.Add(new ActionRowBuilder().WithComponent(builtMenu));
+ }
+ else
+ {
+ if (_actionRows.Count == row)
+ _actionRows.Add(new ActionRowBuilder().WithComponent(builtMenu));
+ else
+ {
+ ActionRowBuilder actionRow = null;
+ if (_actionRows.Count < row)
+ actionRow = _actionRows.ElementAt(row);
+ else
+ {
+ actionRow = new ActionRowBuilder();
+ _actionRows.Add(actionRow);
+ }
+
+ if (actionRow.CanTakeComponent(builtMenu))
+ actionRow.WithComponent(builtMenu);
+ else if (row < 5)
+ WithSelectMenu(menu, row + 1);
+ else
+ throw new ArgumentOutOfRangeException($"There is no more room to add a {nameof(builtMenu)}");
+ }
+ }
+
+ return this;
+ }
+
///
/// Adds a button to the specified row.
///
@@ -81,6 +169,8 @@ namespace Discord
/// The current builder.
public ComponentBuilder WithButton(ButtonBuilder button, int row)
{
+ Preconditions.LessThan(row, 5, nameof(row));
+
var builtButton = button.Build();
if (_actionRows == null)
@@ -94,12 +184,21 @@ namespace Discord
_actionRows.Add(new ActionRowBuilder().WithComponent(builtButton));
else
{
- if (_actionRows.Count > row)
- _actionRows[row].WithComponent(builtButton);
+ ActionRowBuilder actionRow = null;
+ if(_actionRows.Count < row)
+ actionRow = _actionRows.ElementAt(row);
else
{
- _actionRows.First().WithComponent(builtButton);
+ actionRow = new ActionRowBuilder();
+ _actionRows.Add(actionRow);
}
+
+ if (actionRow.CanTakeComponent(builtButton))
+ actionRow.WithComponent(builtButton);
+ else if (row < 5)
+ WithButton(button, row + 1);
+ else
+ throw new ArgumentOutOfRangeException($"There is no more room to add a {nameof(button)}");
}
}
@@ -132,7 +231,7 @@ namespace Discord
///
/// Gets or sets the components inside this row.
///
- public List Components
+ public List Components
{
get => _components;
set
@@ -144,14 +243,14 @@ namespace Discord
}
}
- private List _components { get; set; }
+ private List _components { get; set; }
///
/// Adds a list of components to the current row.
///
/// The list of components to add.
/// The current builder.
- public ActionRowBuilder WithComponents(List components)
+ public ActionRowBuilder WithComponents(List components)
{
this.Components = components;
return this;
@@ -162,10 +261,10 @@ namespace Discord
///
/// The component to add.
/// The current builder.
- public ActionRowBuilder WithComponent(ButtonComponent component)
+ public ActionRowBuilder WithComponent(IMessageComponent component)
{
if (this.Components == null)
- this.Components = new List();
+ this.Components = new List();
this.Components.Add(component);
@@ -188,6 +287,21 @@ namespace Discord
return new ActionRowComponent(this._components);
}
+
+ internal bool CanTakeComponent(IMessageComponent component)
+ {
+ switch (component.Type)
+ {
+ case ComponentType.ActionRow:
+ return false;
+ case ComponentType.Button:
+ return this.Components.Count < 5;
+ case ComponentType.SelectMenu:
+ return this.Components.Count == 0;
+ default:
+ return false;
+ }
+ }
}
///
@@ -195,16 +309,6 @@ namespace Discord
///
public class ButtonBuilder
{
- ///
- /// The max length of a .
- ///
- public const int MaxLabelLength = 80;
-
- ///
- /// The max length of a .
- ///
- public const int MaxCustomIdLength = 100;
-
///
/// Gets or sets the label of the current button.
///
@@ -214,8 +318,8 @@ namespace Discord
set
{
if (value != null)
- if (value.Length > MaxLabelLength)
- throw new ArgumentException(message: $"Button label must be {MaxLabelLength} characters or less!", paramName: nameof(Label));
+ if (value.Length > ComponentBuilder.MaxLabelLength)
+ throw new ArgumentException(message: $"Button label must be {ComponentBuilder.MaxLabelLength} characters or less!", paramName: nameof(Label));
_label = value;
}
@@ -230,8 +334,8 @@ namespace Discord
set
{
if (value != null)
- if (value.Length > MaxCustomIdLength)
- throw new ArgumentException(message: $"Custom Id must be {MaxCustomIdLength} characters or less!", paramName: nameof(CustomId));
+ if (value.Length > ComponentBuilder.MaxCustomIdLength)
+ throw new ArgumentException(message: $"Custom Id must be {ComponentBuilder.MaxCustomIdLength} characters or less!", paramName: nameof(CustomId));
_customId = value;
}
}
@@ -429,4 +533,385 @@ namespace Discord
return new ButtonComponent(this.Style, this.Label, this.Emote, this.CustomId, this.Url, this.Disabled);
}
}
+
+ ///
+ /// Represents a class used to build 's.
+ ///
+ public class SelectMenuBuilder
+ {
+ ///
+ /// The max length of a .
+ ///
+ public const int MaxPlaceholderLength = 100;
+
+ ///
+ /// The maximum number of values for the and properties.
+ ///
+ public const int MaxValuesCount = 25;
+
+ ///
+ /// The maximum number of options a can have.
+ ///
+ public const int MaxOptionCount = 25;
+
+ ///
+ /// Gets or sets the label of the current select menu.
+ ///
+ public string Label
+ {
+ get => _label;
+ set
+ {
+ if (value != null)
+ if (value.Length > ComponentBuilder.MaxLabelLength)
+ throw new ArgumentException(message: $"Button label must be {ComponentBuilder.MaxLabelLength} characters or less!", paramName: nameof(Label));
+
+ _label = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the custom id of the current select menu.
+ ///
+ public string CustomId
+ {
+ get => _customId;
+ set
+ {
+ if (value != null)
+ if (value.Length > ComponentBuilder.MaxCustomIdLength)
+ throw new ArgumentException(message: $"Custom Id must be {ComponentBuilder.MaxCustomIdLength} characters or less!", paramName: nameof(CustomId));
+ _customId = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the placeholder text of the current select menu.
+ ///
+ public string Placeholder
+ {
+ get => _placeholder;
+ set
+ {
+ if (value?.Length > MaxPlaceholderLength)
+ throw new ArgumentException(message: $"Placeholder must be {MaxPlaceholderLength} characters or less!", paramName: nameof(Placeholder));
+
+ _placeholder = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the minimum values of the current select menu.
+ ///
+ public int MinValues
+ {
+ get => _minvalues;
+ set
+ {
+ Preconditions.LessThan(value, MaxValuesCount, nameof(MinValues));
+ _minvalues = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum values of the current select menu.
+ ///
+ public int MaxValues
+ {
+ get => _maxvalues;
+ set
+ {
+ Preconditions.LessThan(value, MaxValuesCount, nameof(MaxValues));
+ _maxvalues = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a collection of for this current select menu.
+ ///
+ public List Options
+ {
+ get => _options;
+ set
+ {
+ if (value != null)
+ Preconditions.LessThan(value.Count, MaxOptionCount, nameof(Options));
+
+ _options = value;
+ }
+ }
+
+ private List _options;
+ private int _minvalues = 1;
+ private int _maxvalues = 1;
+ private string _placeholder;
+ private string _label;
+ private string _customId;
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ public SelectMenuBuilder() { }
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ /// The custom id of this select menu.
+ /// The options for this select menu.
+ public SelectMenuBuilder(string customId, List options)
+ {
+ this.CustomId = customId;
+ this.Options = options;
+ }
+
+ ///
+ /// Sets the field label.
+ ///
+ /// The value to set the field label to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithLabel(string label)
+ {
+ this.Label = label;
+ return this;
+ }
+
+ ///
+ /// Sets the field CustomId.
+ ///
+ /// The value to set the field CustomId to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithCustomId(string customId)
+ {
+ this.CustomId = customId;
+ return this;
+ }
+
+ ///
+ /// Sets the field placeholder.
+ ///
+ /// The value to set the field placeholder to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithPlaceholder(string placeholder)
+ {
+ this.Placeholder = placeholder;
+ return this;
+ }
+
+ ///
+ /// Sets the field minValues.
+ ///
+ /// The value to set the field minValues to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithMinValues(int minValues)
+ {
+ this.MinValues = minValues;
+ return this;
+ }
+
+ ///
+ /// Sets the field maxValues.
+ ///
+ /// The value to set the field maxValues to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithMaxValues(int maxValues)
+ {
+ this.MaxValues = maxValues;
+ return this;
+ }
+
+ ///
+ /// Sets the field options.
+ ///
+ /// The value to set the field options to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithOptions(List options)
+ {
+ this.Options = options;
+ return this;
+ }
+
+ ///
+ /// Builds a
+ ///
+ /// The newly built
+ public SelectMenu Build()
+ {
+ var opt = this.Options?.Select(x => x.Build()).ToList();
+
+ return new SelectMenu(this.CustomId, opt, this.Placeholder, this.MinValues, this.MaxValues);
+ }
+ }
+
+ ///
+ /// Represents a class used to build 's.
+ ///
+ public class SelectMenuOptionBuilder
+ {
+ ///
+ /// The maximum length of a .
+ ///
+ public const int MaxDescriptionLength = 50;
+
+ ///
+ /// Gets or sets the label of the current select menu.
+ ///
+ public string Label
+ {
+ get => _label;
+ set
+ {
+ if (value != null)
+ if (value.Length > ComponentBuilder.MaxLabelLength)
+ throw new ArgumentException(message: $"Button label must be {ComponentBuilder.MaxLabelLength} characters or less!", paramName: nameof(Label));
+
+ _label = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the custom id of the current select menu.
+ ///
+ public string Value
+ {
+ get => _value;
+ set
+ {
+ if (value != null)
+ if (value.Length > ComponentBuilder.MaxCustomIdLength)
+ throw new ArgumentException(message: $"Value must be {ComponentBuilder.MaxCustomIdLength} characters or less!", paramName: nameof(Value));
+ _value = value;
+ }
+ }
+
+ ///
+ /// Gets or sets this menu options description.
+ ///
+ public string Description
+ {
+ get => _description;
+ set
+ {
+ if (value != null)
+ Preconditions.LessThan(value.Length, MaxDescriptionLength, nameof(Description));
+
+ _description = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the emote of this option.
+ ///
+ public IEmote Emote { get; set; }
+
+ ///
+ /// Gets or sets the whether or not this option will render selected by default.
+ ///
+ public bool? Default { get; set; }
+
+ private string _label;
+ private string _value;
+ private string _description;
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ public SelectMenuOptionBuilder() { }
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ /// The label for this option.
+ /// The value of this option.
+ public SelectMenuOptionBuilder(string label, string value)
+ {
+ this.Label = label;
+ this.Value = value;
+ }
+
+ ///
+ /// Sets the field label.
+ ///
+ /// The value to set the field label to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithLabel(string label)
+ {
+ this.Label = label;
+ return this;
+ }
+
+ ///
+ /// Sets the field value.
+ ///
+ /// The value to set the field value to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithValue(string value)
+ {
+ this.Value = value;
+ return this;
+ }
+
+ ///
+ /// Sets the field description.
+ ///
+ /// The value to set the field description to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithDescription(string description)
+ {
+ this.Description = description;
+ return this;
+ }
+
+ ///
+ /// Sets the field emote.
+ ///
+ /// The value to set the field emote to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithEmote(IEmote emote)
+ {
+ this.Emote = emote;
+ return this;
+ }
+
+ ///
+ /// Sets the field default.
+ ///
+ /// The value to set the field default to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithDefault(bool defaultValue)
+ {
+ this.Default = defaultValue;
+ return this;
+ }
+
+ ///
+ /// Builds a .
+ ///
+ /// The newly built .
+ public SelectMenuOption Build()
+ {
+ return new SelectMenuOption(this.Label, this.Value, this.Description, this.Emote, this.Default);
+ }
+ }
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentType.cs b/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentType.cs
index 9b75599c9..5fb4fc092 100644
--- a/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentType.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentType.cs
@@ -19,6 +19,11 @@ namespace Discord
///
/// A clickable button
///
- Button = 2
+ Button = 2,
+
+ ///
+ /// A select menu for picking from choices
+ ///
+ SelectMenu = 3,
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/Message Components/SelectMenu.cs b/src/Discord.Net.Core/Entities/Interactions/Message Components/SelectMenu.cs
new file mode 100644
index 000000000..70fd36398
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/Message Components/SelectMenu.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ ///
+ /// Represents a select menu component defined at
+ ///
+ public class SelectMenu : IMessageComponent
+ {
+ ///
+ public ComponentType Type => ComponentType.SelectMenu;
+
+ ///
+ /// The custom id of this Select menu that will be sent with a .
+ ///
+ public string CustomId { get; }
+
+ ///
+ /// The menus options to select from.
+ ///
+ public IReadOnlyCollection Options { get; }
+
+ ///
+ /// A custom placeholder text if nothing is selected, max 100 characters.
+ ///
+ public string Placeholder { get; }
+
+ ///
+ /// The minimum number of items that must be chosen; default 1, min 0, max 25
+ ///
+ public int MinValues { get; }
+
+ ///
+ /// The maximum number of items that can be chosen; default 1, max 25
+ ///
+ public int MaxValues { get; }
+
+ internal SelectMenu(string customId, List options, string placeholder, int minValues, int maxValues)
+ {
+ this.CustomId = customId;
+ this.Options = options;
+ this.Placeholder = placeholder;
+ this.MinValues = minValues;
+ this.MaxValues = maxValues;
+ }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/Message Components/SelectMenuOption.cs b/src/Discord.Net.Core/Entities/Interactions/Message Components/SelectMenuOption.cs
new file mode 100644
index 000000000..74da89cae
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/Message Components/SelectMenuOption.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ ///
+ /// Represents a choice for a .
+ ///
+ public class SelectMenuOption
+ {
+ ///
+ /// The user-facing name of the option, max 25 characters.
+ ///
+ public string Label { get; }
+
+ ///
+ /// The dev-define value of the option, max 100 characters.
+ ///
+ public string Value { get; }
+
+ ///
+ /// An additional description of the option, max 50 characters.
+ ///
+ public string Description { get; }
+
+ ///
+ /// A that will be displayed with this menu option.
+ ///
+ public IEmote Emote { get; }
+
+ ///
+ /// Will render this option as selected by default.
+ ///
+ public bool? Default { get; }
+
+ internal SelectMenuOption(string label, string value, string description, IEmote emote, bool? defaultValue)
+ {
+ this.Label = label;
+ this.Value = value;
+ this.Description = description;
+ this.Emote = emote;
+ this.Default = defaultValue;
+ }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs b/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs
index 7fddac1cf..4de9d6fe1 100644
--- a/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs
@@ -1,4 +1,5 @@
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,19 +8,30 @@ using System.Threading.Tasks;
namespace Discord.API
{
- internal class ActionRowComponent
+ internal class ActionRowComponent : IMessageComponent
{
[JsonProperty("type")]
public ComponentType Type { get; set; }
[JsonProperty("components")]
- public List Components { get; set; }
+ public IMessageComponent[] Components { get; set; }
internal ActionRowComponent() { }
internal ActionRowComponent(Discord.ActionRowComponent c)
{
this.Type = c.Type;
- this.Components = c.Components?.Select(x => new ButtonComponent(x)).ToList();
+ this.Components = c.Components?.Select(x =>
+ {
+ switch (x.Type)
+ {
+ case ComponentType.Button:
+ return new ButtonComponent(x as Discord.ButtonComponent);
+ case ComponentType.SelectMenu:
+ return new SelectMenuComponent(x as Discord.SelectMenu);
+ default: return null;
+
+ }
+ }).ToArray();
}
}
}
diff --git a/src/Discord.Net.Rest/API/Common/ApplicationCommandInteractionData.cs b/src/Discord.Net.Rest/API/Common/ApplicationCommandInteractionData.cs
index c72e8a686..b9647ba65 100644
--- a/src/Discord.Net.Rest/API/Common/ApplicationCommandInteractionData.cs
+++ b/src/Discord.Net.Rest/API/Common/ApplicationCommandInteractionData.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Discord.API
{
- internal class ApplicationCommandInteractionData
+ internal class ApplicationCommandInteractionData : IDiscordInteractionData
{
[JsonProperty("id")]
public ulong Id { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/ButtonComponent.cs b/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
index 775f78101..b9d1147a2 100644
--- a/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Discord.API
{
- internal class ButtonComponent
+ internal class ButtonComponent : IMessageComponent
{
[JsonProperty("type")]
public ComponentType Type { get; set; }
diff --git a/src/Discord.Net.WebSocket/API/Gateway/InteractionCreated.cs b/src/Discord.Net.Rest/API/Common/Interaction.cs
similarity index 84%
rename from src/Discord.Net.WebSocket/API/Gateway/InteractionCreated.cs
rename to src/Discord.Net.Rest/API/Common/Interaction.cs
index 6e9ebb4fb..ebbc5fc1b 100644
--- a/src/Discord.Net.WebSocket/API/Gateway/InteractionCreated.cs
+++ b/src/Discord.Net.Rest/API/Common/Interaction.cs
@@ -1,4 +1,3 @@
-using Discord.API;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -6,9 +5,10 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Discord.API.Gateway
+namespace Discord.API
{
- internal class InteractionCreated
+ [JsonConverter(typeof(Net.Converters.InteractionConverter))]
+ internal class Interaction
{
[JsonProperty("id")]
public ulong Id { get; set; }
@@ -20,7 +20,7 @@ namespace Discord.API.Gateway
public InteractionType Type { get; set; }
[JsonProperty("data")]
- public Optional
+
+
+ The value(s) of a interaction response.
+
+
Represends a Websocket-based recieved over the gateway.
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index 4fe1cfdfe..9f7a1e524 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -1865,7 +1865,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (INTERACTION_CREATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = (payload as JToken).ToObject(_serializer);
SocketChannel channel = null;
if(data.ChannelId.IsSpecified)
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs
index beda6eb53..f42fb58ff 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Model = Discord.API.Gateway.InteractionCreated;
+using Model = Discord.API.Interaction;
using DataModel = Discord.API.MessageComponentInteractionData;
using Newtonsoft.Json.Linq;
using Discord.Rest;
@@ -29,12 +29,10 @@ namespace Discord.WebSocket
: base(client, model.Id, channel)
{
var dataModel = model.Data.IsSpecified ?
- (model.Data.Value as JToken).ToObject()
+ (DataModel)model.Data.Value
: null;
this.Data = new SocketMessageComponentData(dataModel);
-
-
}
new internal static SocketMessageComponent Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponentData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponentData.cs
index 8477432c7..45e688266 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponentData.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponentData.cs
@@ -22,10 +22,16 @@ namespace Discord.WebSocket
///
public ComponentType Type { get; }
+ ///
+ /// The value(s) of a interaction response.
+ ///
+ public IReadOnlyCollection Values { get; }
+
internal SocketMessageComponentData(Model model)
{
this.CustomId = model.CustomId;
this.Type = model.ComponentType;
+ this.Values = model.Values.GetValueOrDefault();
}
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs
index 1b94f5c83..542e8efc5 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs
@@ -4,7 +4,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using DataModel = Discord.API.ApplicationCommandInteractionData;
-using Model = Discord.API.Gateway.InteractionCreated;
+using Model = Discord.API.Interaction;
namespace Discord.WebSocket
{
@@ -22,7 +22,7 @@ namespace Discord.WebSocket
: base(client, model.Id, channel)
{
var dataModel = model.Data.IsSpecified ?
- (model.Data.Value as JToken).ToObject(client._serializer)
+ (DataModel)model.Data.Value
: null;
ulong? guildId = null;
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
index a31ecc692..0876d6eb6 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Model = Discord.API.Gateway.InteractionCreated;
+using Model = Discord.API.Interaction;
namespace Discord.WebSocket
{
@@ -36,7 +36,7 @@ namespace Discord.WebSocket
///
/// The data sent with this interaction.
///
- public object Data { get; private set; }
+ public IDiscordInteractionData Data { get; private set; }
///
/// The version of this interaction.
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
index d0b9443a4..8ec18d49e 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
@@ -1,4 +1,5 @@
using Discord.Rest;
+using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -167,16 +168,49 @@ namespace Discord.WebSocket
if (model.Components.IsSpecified)
{
- Components = model.Components.Value.Select(x => new ActionRowComponent(x.Components.Select(x =>
- new ButtonComponent(
- x.Style,
- x.Label.GetValueOrDefault(),
- x.Emote.IsSpecified ? x.Emote.Value.Id.HasValue ? new Emote(x.Emote.Value.Id.Value, x.Emote.Value.Name, x.Emote.Value.Animated.GetValueOrDefault()) : new Emoji(x.Emote.Value.Name) : null,
- x.CustomId.GetValueOrDefault(),
- x.Url.GetValueOrDefault(),
- x.Disabled.GetValueOrDefault())
- ).ToList()
- )).ToList();
+ Components = model.Components.Value.Select(x => new ActionRowComponent(x.Components.Select(y =>
+ {
+ switch (y.Type)
+ {
+ case ComponentType.Button:
+ {
+ var parsed = (API.ButtonComponent)y;
+ return new Discord.ButtonComponent(
+ parsed.Style,
+ parsed.Label.GetValueOrDefault(),
+ parsed.Emote.IsSpecified
+ ? parsed.Emote.Value.Id.HasValue
+ ? new Emote(parsed.Emote.Value.Id.Value, parsed.Emote.Value.Name, parsed.Emote.Value.Animated.GetValueOrDefault())
+ : new Emoji(parsed.Emote.Value.Name)
+ : null,
+ parsed.CustomId.GetValueOrDefault(),
+ parsed.Url.GetValueOrDefault(),
+ parsed.Disabled.GetValueOrDefault());
+ }
+ case ComponentType.SelectMenu:
+ {
+ var parsed = (API.SelectMenuComponent)y;
+ return new SelectMenu(
+ parsed.CustomId,
+ parsed.Options.Select(z => new SelectMenuOption(
+ z.Label,
+ z.Value,
+ z.Description.GetValueOrDefault(),
+ z.Emoji.IsSpecified
+ ? z.Emoji.Value.Id.HasValue
+ ? new Emote(z.Emoji.Value.Id.Value, z.Emoji.Value.Name, z.Emoji.Value.Animated.GetValueOrDefault())
+ : new Emoji(z.Emoji.Value.Name)
+ : null,
+ z.Default.ToNullable())).ToList(),
+ parsed.Placeholder.GetValueOrDefault(),
+ parsed.MinValues,
+ parsed.MaxValues
+ );
+ }
+ default:
+ return null;
+ }
+ }).ToList())).ToImmutableArray();
}
else
Components = new List();
diff --git a/src/Discord.Net.Webhook/Discord.Net.Webhook.xml b/src/Discord.Net.Webhook/Discord.Net.Webhook.xml
index e2c3e69a9..f629c2c29 100644
--- a/src/Discord.Net.Webhook/Discord.Net.Webhook.xml
+++ b/src/Discord.Net.Webhook/Discord.Net.Webhook.xml
@@ -35,6 +35,33 @@
Sends a message to the channel for this webhook. Returns the ID of the created message.
+
+
+ Modifies a message posted using this webhook.
+
+
+ This method can only modify messages that were sent using the same webhook.
+
+ ID of the modified message.
+ A delegate containing the properties to modify the message with.
+ The options to be used when sending the request.
+
+ A task that represents the asynchronous modification operation.
+
+
+
+
+ Deletes a message posted using this webhook.
+
+
+ This method can only delete messages that were sent using the same webhook.
+
+ ID of the deleted message.
+ The options to be used when sending the request.
+
+ A task that represents the asynchronous deletion operation.
+
+ Sends a message to the channel for this webhook with an attachment. Returns the ID of the created message.
@@ -49,6 +76,29 @@
Deletes this webhook from Discord and disposes the client.
+
+
+ Properties that are used to modify an Webhook message with the specified changes.
+
+
+
+
+ Gets or sets the content of the message.
+
+
+ This must be less than the constant defined by .
+
+
+
+
+ Gets or sets the embed array that the message should display.
+
+
+
+
+ Gets or sets the allowed mentions of the message.
+
+ Could not find a webhook with the supplied credentials.