@@ -96,7 +96,6 @@ Theres a new field in all `SendMessageAsync` functions that takes in a `MessageC | |||
var builder = new ComponentBuilder() | |||
.WithSelectMenu(new SelectMenuBuilder() | |||
.WithCustomId("id_2") | |||
.WithLabel("Select menu!") | |||
.WithPlaceholder("This is a placeholder") | |||
.WithOptions(new List<SelectMenuOptionBuilder>() | |||
{ | |||
@@ -81,3 +81,4 @@ public async Task Client_Ready() | |||
} | |||
``` | |||
**Note**: Slash commands only need to be created once. They do _not_ have to be 'created' on every startup or connection. The example simple shows creating them in the ready event as it's simpler than creating normal bot commands to register slash commands. |
@@ -75,7 +75,7 @@ private async Task Client_InteractionCreated(SocketInteraction arg) | |||
} | |||
} | |||
private async Task HandleListRoleCommand(SocketSlashCommandData command) | |||
private async Task HandleListRoleCommand(SocketSlashCommand command) | |||
{ | |||
// We need to extract the user parameter from the command. since we only have one option and it's required, we can just use the first option. | |||
var guildUser = (SocketGuildUser)command.Data.Options.First().Value; | |||
@@ -2206,7 +2206,7 @@ | |||
</member> | |||
<member name="M:Discord.Emoji.ToString"> | |||
<summary> | |||
Gets the Unicode representation of this emote. | |||
Gets the Unicode representation of this emoji. | |||
</summary> | |||
<returns> | |||
A string that resolves to <see cref="P:Discord.Emoji.Name"/>. | |||
@@ -2224,6 +2224,17 @@ | |||
</summary> | |||
<param name="other">The object to compare with the current object.</param> | |||
</member> | |||
<member name="M:Discord.Emoji.TryParse(System.String,Discord.Emoji@)"> | |||
<summary> Tries to parse an <see cref="T:Discord.Emoji"/> from its raw format. </summary> | |||
<param name="text">The raw encoding of an emoji. For example: <code>:heart: or ❤</code></param> | |||
<param name="result">An emoji.</param> | |||
</member> | |||
<member name="M:Discord.Emoji.Parse(System.String)"> | |||
<summary> Parse an <see cref="T:Discord.Emoji"/> from its raw format. </summary> | |||
<param name="text">The raw encoding of an emoji. For example: <c>:heart: or ❤</c></param> | |||
<param name="result">An emoji.</param> | |||
<exception cref="T:System.FormatException">String is not emoji or unicode!</exception> | |||
</member> | |||
<member name="M:Discord.Emoji.GetHashCode"> | |||
<inheritdoc /> | |||
</member> | |||
@@ -4460,11 +4471,13 @@ | |||
<summary> | |||
Gets or sets the Action Rows for this Component Builder. | |||
</summary> | |||
<exception cref="T:System.ArgumentNullException" accessor="set"><see cref="P:Discord.ComponentBuilder.ActionRows"/> cannot be null.</exception> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.ComponentBuilder.ActionRows"/> count exceeds <see cref="F:Discord.ComponentBuilder.MaxActionRowCount"/>.</exception> | |||
</member> | |||
<member name="M:Discord.ComponentBuilder.WithSelectMenu(System.String,System.String,System.Collections.Generic.List{Discord.SelectMenuOptionBuilder},System.String,System.Int32,System.Int32,System.Int32)"> | |||
<member name="M:Discord.ComponentBuilder.WithSelectMenu(System.String,System.String,System.Collections.Generic.List{Discord.SelectMenuOptionBuilder},System.String,System.Int32,System.Int32,System.Boolean,System.Int32)"> | |||
<summary> | |||
Adds a <see cref="T:Discord.SelectMenuBuilder"/> to the first row, if the first row cannot | |||
accept the component then it will add it to a row that can | |||
Adds a <see cref="T:Discord.SelectMenuBuilder"/> to the <see cref="T:Discord.ComponentBuilder"/> at the specific row. | |||
If the row cannot accept the component then it will add it to a row that can. | |||
</summary> | |||
<param name="label">The label of the menu.</param> | |||
<param name="customId">The custom id of the menu.</param> | |||
@@ -4472,23 +4485,19 @@ | |||
<param name="placeholder">The placeholder of the menu.</param> | |||
<param name="minValues">The min values of the placeholder.</param> | |||
<param name="maxValues">The max values of the placeholder.</param> | |||
<param name="disabled">Whether or not the menu is disabled.</param> | |||
<param name="row">The row to add the menu to.</param> | |||
<returns></returns> | |||
</member> | |||
<member name="M:Discord.ComponentBuilder.WithSelectMenu(Discord.SelectMenuBuilder)"> | |||
<summary> | |||
Adds a <see cref="T:Discord.SelectMenuBuilder"/> to the first row, if the first row cannot | |||
accept the component then it will add it to a row that can | |||
</summary> | |||
<param name="menu">The menu to add</param> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ComponentBuilder.WithSelectMenu(Discord.SelectMenuBuilder,System.Int32)"> | |||
<summary> | |||
Adds a <see cref="T:Discord.SelectMenuBuilder"/> to the current builder at the specific row. | |||
Adds a <see cref="T:Discord.SelectMenuBuilder"/> to the <see cref="T:Discord.ComponentBuilder"/> at the specific row. | |||
If the row cannot accept the component then it will add it to a row that can. | |||
</summary> | |||
<param name="menu">The menu to add.</param> | |||
<param name="row">The row to attempt to add this component on.</param> | |||
<exception cref="T:System.InvalidOperationException">There is no more row to add a menu.</exception> | |||
<exception cref="T:System.ArgumentException"><paramref name="row"/> must be less than <see cref="F:Discord.ComponentBuilder.MaxActionRowCount"/>.</exception> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ComponentBuilder.WithButton(System.String,System.String,Discord.ButtonStyle,Discord.IEmote,System.String,System.Boolean,System.Int32)"> | |||
@@ -4504,26 +4513,22 @@ | |||
<param name="row">The row the button should be placed on.</param> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ComponentBuilder.WithButton(Discord.ButtonBuilder)"> | |||
<summary> | |||
Adds a button to the first row. | |||
</summary> | |||
<param name="button">The button to add to the first row.</param> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ComponentBuilder.WithButton(Discord.ButtonBuilder,System.Int32)"> | |||
<summary> | |||
Adds a button to the specified row. | |||
Adds a <see cref="T:Discord.ButtonBuilder"/> to the <see cref="T:Discord.ComponentBuilder"/> at the specific row. | |||
If the row cannot accept the component then it will add it to a row that can. | |||
</summary> | |||
<param name="button">The button to add.</param> | |||
<param name="row">The row to add the button.</param> | |||
<exception cref="T:System.InvalidOperationException">There is no more row to add a menu.</exception> | |||
<exception cref="T:System.ArgumentException"><paramref name="row"/> must be less than <see cref="F:Discord.ComponentBuilder.MaxActionRowCount"/>.</exception> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ComponentBuilder.Build"> | |||
<summary> | |||
Builds this builder into a <see cref="T:Discord.MessageComponent"/> used to send your components. | |||
</summary> | |||
<returns>A <see cref="T:Discord.MessageComponent"/> that can be sent with <see cref="M:Discord.IMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.Embed,Discord.RequestOptions,Discord.AllowedMentions,Discord.MessageReference,Discord.MessageComponent)"/></returns> | |||
<returns>A <see cref="T:Discord.MessageComponent"/> that can be sent with <see cref="M:Discord.IMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.Embed,Discord.RequestOptions,Discord.AllowedMentions,Discord.MessageReference,Discord.MessageComponent)"/>.</returns> | |||
</member> | |||
<member name="T:Discord.ActionRowBuilder"> | |||
<summary> | |||
@@ -4539,19 +4544,23 @@ | |||
<summary> | |||
Gets or sets the components inside this row. | |||
</summary> | |||
<exception cref="T:System.ArgumentNullException" accessor="set"><see cref="P:Discord.ActionRowBuilder.Components"/> cannot be null.</exception> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.ActionRowBuilder.Components"/> count exceeds <see cref="F:Discord.ActionRowBuilder.MaxChildCount"/>.</exception> | |||
</member> | |||
<member name="M:Discord.ActionRowBuilder.WithComponents(System.Collections.Generic.List{Discord.IMessageComponent})"> | |||
<summary> | |||
Adds a list of components to the current row. | |||
</summary> | |||
<param name="components">The list of components to add.</param> | |||
<inheritdoc cref="P:Discord.ActionRowBuilder.Components"/> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ActionRowBuilder.WithComponent(Discord.IMessageComponent)"> | |||
<member name="M:Discord.ActionRowBuilder.AddComponent(Discord.IMessageComponent)"> | |||
<summary> | |||
Adds a component at the end of the current row. | |||
</summary> | |||
<param name="component">The component to add.</param> | |||
<exception cref="T:System.InvalidOperationException">Components count reached <see cref="F:Discord.ActionRowBuilder.MaxChildCount"/></exception> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ActionRowBuilder.Build"> | |||
@@ -4559,8 +4568,6 @@ | |||
Builds the current builder to a <see cref="T:Discord.ActionRowComponent"/> that can be used within a <see cref="T:Discord.ComponentBuilder"/> | |||
</summary> | |||
<returns>A <see cref="T:Discord.ActionRowComponent"/> that can be used within a <see cref="T:Discord.ComponentBuilder"/></returns> | |||
<exception cref="T:System.ArgumentNullException"><see cref="P:Discord.ActionRowBuilder.Components"/> cannot be null.</exception> | |||
<exception cref="T:System.ArgumentException">There must be at least 1 component in a row.</exception> | |||
</member> | |||
<member name="T:Discord.ButtonBuilder"> | |||
<summary> | |||
@@ -4571,11 +4578,13 @@ | |||
<summary> | |||
Gets or sets the label of the current button. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.ButtonBuilder.Label"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxLabelLength"/>.</exception> | |||
</member> | |||
<member name="P:Discord.ButtonBuilder.CustomId"> | |||
<summary> | |||
Gets or sets the custom id of the current button. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.ButtonBuilder.CustomId"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxCustomIdLength"/></exception> | |||
</member> | |||
<member name="P:Discord.ButtonBuilder.Style"> | |||
<summary> | |||
@@ -4597,6 +4606,27 @@ | |||
Gets or sets whether the current button is disabled. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.ButtonBuilder.#ctor"> | |||
<summary> | |||
Creates a new instance of a <see cref="T:Discord.ButtonBuilder"/>. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.ButtonBuilder.#ctor(System.String,System.String,Discord.ButtonStyle,System.String,Discord.IEmote,System.Boolean)"> | |||
<summary> | |||
Creates a new instance of a <see cref="T:Discord.ButtonBuilder"/>. | |||
</summary> | |||
<param name="label">The label to use on the newly created link button.</param> | |||
<param name="url">The url of this button.</param> | |||
<param name="customId">The custom ID of this button</param> | |||
<param name="style">The custom ID of this button</param> | |||
<param name="emote">The emote of this button</param> | |||
<param name="disabled">Disabled this button or not</param> | |||
</member> | |||
<member name="M:Discord.ButtonBuilder.#ctor(Discord.ButtonComponent)"> | |||
<summary> | |||
Creates a new instance of a <see cref="T:Discord.ButtonBuilder"/> from instance of a <see cref="T:Discord.ButtonComponent"/>. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.ButtonBuilder.CreateLinkButton(System.String,System.String)"> | |||
<summary> | |||
Creates a button with the <see cref="F:Discord.ButtonStyle.Link"/> style. | |||
@@ -4642,6 +4672,7 @@ | |||
Sets the current buttons label to the specified text. | |||
</summary> | |||
<param name="label">The text for the label</param> | |||
<inheritdoc cref="P:Discord.ButtonBuilder.Label"/> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ButtonBuilder.WithStyle(Discord.ButtonStyle)"> | |||
@@ -4670,6 +4701,7 @@ | |||
Sets the custom id of the current button. | |||
</summary> | |||
<param name="id">The id to use for the current button.</param> | |||
<inheritdoc cref="P:Discord.ButtonBuilder.CustomId"/> | |||
<returns>The current builder.</returns> | |||
</member> | |||
<member name="M:Discord.ButtonBuilder.WithDisabled(System.Boolean)"> | |||
@@ -4684,8 +4716,8 @@ | |||
Builds this builder into a <see cref="T:Discord.ButtonComponent"/> to be used in a <see cref="T:Discord.ComponentBuilder"/>. | |||
</summary> | |||
<returns>A <see cref="T:Discord.ButtonComponent"/> to be used in a <see cref="T:Discord.ComponentBuilder"/>.</returns> | |||
<exception cref="T:System.InvalidOperationException">A button cannot contain a URL and a CustomId.</exception> | |||
<exception cref="T:System.ArgumentException">A button must have an Emote or a label.</exception> | |||
<exception cref="T:System.InvalidOperationException">A button cannot contain a <see cref="P:Discord.ButtonBuilder.Url"/> and a <see cref="P:Discord.ButtonBuilder.CustomId"/>.</exception> | |||
<exception cref="T:System.InvalidOperationException">A button must have an <see cref="P:Discord.ButtonBuilder.Emote"/> or a <see cref="P:Discord.ButtonBuilder.Label"/>.</exception> | |||
</member> | |||
<member name="T:Discord.SelectMenuBuilder"> | |||
<summary> | |||
@@ -4707,62 +4739,69 @@ | |||
The maximum number of options a <see cref="T:Discord.SelectMenu"/> can have. | |||
</summary> | |||
</member> | |||
<member name="P:Discord.SelectMenuBuilder.Label"> | |||
<summary> | |||
Gets or sets the label of the current select menu. | |||
</summary> | |||
</member> | |||
<member name="P:Discord.SelectMenuBuilder.CustomId"> | |||
<summary> | |||
Gets or sets the custom id of the current select menu. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuBuilder.CustomId"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxCustomIdLength"/>.</exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuBuilder.Placeholder"> | |||
<summary> | |||
Gets or sets the placeholder text of the current select menu. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuBuilder.Placeholder"/> length exceeds <see cref="F:Discord.SelectMenuBuilder.MaxPlaceholderLength"/>.</exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuBuilder.MinValues"> | |||
<summary> | |||
Gets or sets the minimum values of the current select menu. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuBuilder.MinValues"/> exceeds <see cref="F:Discord.SelectMenuBuilder.MaxValuesCount"/>.</exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuBuilder.MaxValues"> | |||
<summary> | |||
Gets or sets the maximum values of the current select menu. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuBuilder.MaxValues"/> exceeds <see cref="F:Discord.SelectMenuBuilder.MaxValuesCount"/>.</exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuBuilder.Options"> | |||
<summary> | |||
Gets or sets a collection of <see cref="T:Discord.SelectMenuOptionBuilder"/> for this current select menu. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuBuilder.Options"/> count exceeds <see cref="F:Discord.SelectMenuBuilder.MaxOptionCount"/>.</exception> | |||
<exception cref="T:System.ArgumentNullException" accessor="set"><see cref="P:Discord.SelectMenuBuilder.Options"/> is null.</exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuBuilder.Disabled"> | |||
<summary> | |||
Gets or sets whether the current menu is disabled. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.SelectMenuBuilder.#ctor"> | |||
<summary> | |||
Creates a new instance of a <see cref="T:Discord.SelectMenuBuilder"/>. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.SelectMenuBuilder.#ctor(System.String,System.Collections.Generic.List{Discord.SelectMenuOptionBuilder})"> | |||
<member name="M:Discord.SelectMenuBuilder.#ctor(Discord.SelectMenu)"> | |||
<summary> | |||
Creates a new instance of a <see cref="T:Discord.SelectMenuBuilder"/>. | |||
Creates a new instance of a <see cref="T:Discord.SelectMenuBuilder"/> from instance of <see cref="T:Discord.SelectMenu"/>. | |||
</summary> | |||
<param name="customId">The custom id of this select menu.</param> | |||
<param name="options">The options for this select menu.</param> | |||
</member> | |||
<member name="M:Discord.SelectMenuBuilder.WithLabel(System.String)"> | |||
<member name="M:Discord.SelectMenuBuilder.#ctor(System.String,System.Collections.Generic.List{Discord.SelectMenuOptionBuilder},System.String,System.Int32,System.Int32,System.Boolean)"> | |||
<summary> | |||
Sets the field label. | |||
Creates a new instance of a <see cref="T:Discord.SelectMenuBuilder"/>. | |||
</summary> | |||
<param name="label">The value to set the field label to.</param> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
<param name="customId">The custom id of this select menu.</param> | |||
<param name="options">The options for this select menu.</param> | |||
<param name="placeholder">The placeholder of this select menu.</param> | |||
<param name="maxValues">The max values of this select menu.</param> | |||
<param name="minValues">The min values of this select menu.</param> | |||
<param name="disabled">Disabled this select menu or not.</param> | |||
</member> | |||
<member name="M:Discord.SelectMenuBuilder.WithCustomId(System.String)"> | |||
<summary> | |||
Sets the field CustomId. | |||
</summary> | |||
<param name="customId">The value to set the field CustomId to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuBuilder.CustomId"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4772,6 +4811,7 @@ | |||
Sets the field placeholder. | |||
</summary> | |||
<param name="placeholder">The value to set the field placeholder to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuBuilder.Placeholder"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4781,6 +4821,7 @@ | |||
Sets the field minValues. | |||
</summary> | |||
<param name="minValues">The value to set the field minValues to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuBuilder.MinValues"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4790,6 +4831,7 @@ | |||
Sets the field maxValues. | |||
</summary> | |||
<param name="maxValues">The value to set the field maxValues to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuBuilder.MaxValues"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4799,6 +4841,40 @@ | |||
Sets the field options. | |||
</summary> | |||
<param name="options">The value to set the field options to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuBuilder.Options"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
</member> | |||
<member name="M:Discord.SelectMenuBuilder.AddOption(Discord.SelectMenuOptionBuilder)"> | |||
<summary> | |||
Add one option to menu options. | |||
</summary> | |||
<param name="option">The option builder class containing the option properties.</param> | |||
<exception cref="T:System.InvalidOperationException">Options count reached <see cref="F:Discord.SelectMenuBuilder.MaxOptionCount"/>.</exception> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
</member> | |||
<member name="M:Discord.SelectMenuBuilder.AddOption(System.String,System.String,System.String,Discord.IEmote,System.Nullable{System.Boolean})"> | |||
<summary> | |||
Add one option to menu options. | |||
</summary> | |||
<param name="label">The label for this option.</param> | |||
<param name="value">The value of this option.</param> | |||
<param name="description">The description of this option.</param> | |||
<param name="emote">The emote of this option.</param> | |||
<param name="default">Render this option as selected by default or not.</param> | |||
<exception cref="T:System.InvalidOperationException">Options count reached <see cref="F:Discord.SelectMenuBuilder.MaxOptionCount"/>.</exception> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
</member> | |||
<member name="M:Discord.SelectMenuBuilder.WithDisabled(System.Boolean)"> | |||
<summary> | |||
Sets whether the current menu is disabled. | |||
</summary> | |||
<param name="disabled">Whether the current menu is disabled or not.</param> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4823,16 +4899,19 @@ | |||
<summary> | |||
Gets or sets the label of the current select menu. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuOptionBuilder.Label"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxLabelLength"/></exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuOptionBuilder.Value"> | |||
<summary> | |||
Gets or sets the custom id of the current select menu. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuOptionBuilder.Value"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxCustomIdLength"/>.</exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuOptionBuilder.Description"> | |||
<summary> | |||
Gets or sets this menu options description. | |||
</summary> | |||
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuOptionBuilder.Description"/> length exceeds <see cref="F:Discord.SelectMenuOptionBuilder.MaxDescriptionLength"/>.</exception> | |||
</member> | |||
<member name="P:Discord.SelectMenuOptionBuilder.Emote"> | |||
<summary> | |||
@@ -4849,18 +4928,27 @@ | |||
Creates a new instance of a <see cref="T:Discord.SelectMenuOptionBuilder"/>. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.SelectMenuOptionBuilder.#ctor(System.String,System.String)"> | |||
<member name="M:Discord.SelectMenuOptionBuilder.#ctor(System.String,System.String,System.String,Discord.IEmote,System.Nullable{System.Boolean})"> | |||
<summary> | |||
Creates a new instance of a <see cref="T:Discord.SelectMenuOptionBuilder"/>. | |||
</summary> | |||
<param name="label">The label for this option.</param> | |||
<param name="value">The value of this option.</param> | |||
<param name="description">The description of this option.</param> | |||
<param name="emote">The emote of this option.</param> | |||
<param name="default">Render this option as selected by default or not.</param> | |||
</member> | |||
<member name="M:Discord.SelectMenuOptionBuilder.#ctor(Discord.SelectMenuOption)"> | |||
<summary> | |||
Creates a new instance of a <see cref="T:Discord.SelectMenuOptionBuilder"/> from instance of a <see cref="T:Discord.SelectMenuOption"/>. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.SelectMenuOptionBuilder.WithLabel(System.String)"> | |||
<summary> | |||
Sets the field label. | |||
</summary> | |||
<param name="label">The value to set the field label to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuOptionBuilder.Label"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4870,6 +4958,7 @@ | |||
Sets the field value. | |||
</summary> | |||
<param name="value">The value to set the field value to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuOptionBuilder.Value"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4879,6 +4968,7 @@ | |||
Sets the field description. | |||
</summary> | |||
<param name="description">The value to set the field description to.</param> | |||
<inheritdoc cref="P:Discord.SelectMenuOptionBuilder.Description"/> | |||
<returns> | |||
The current builder. | |||
</returns> | |||
@@ -4980,6 +5070,11 @@ | |||
The maximum number of items that can be chosen; default 1, max 25 | |||
</summary> | |||
</member> | |||
<member name="P:Discord.SelectMenu.Disabled"> | |||
<summary> | |||
Whether this menu is disabled or not. | |||
</summary> | |||
</member> | |||
<member name="T:Discord.SelectMenuOption"> | |||
<summary> | |||
Represents a choice for a <see cref="T:Discord.SelectMenu"/>. | |||
@@ -5651,23 +5746,23 @@ | |||
</summary> | |||
</member> | |||
<member name="F:Discord.EmbedBuilder.MaxFieldCount"> | |||
<summary> | |||
Returns the maximum number of fields allowed by Discord. | |||
<summary> | |||
Returns the maximum number of fields allowed by Discord. | |||
</summary> | |||
</member> | |||
<member name="F:Discord.EmbedBuilder.MaxTitleLength"> | |||
<summary> | |||
Returns the maximum length of title allowed by Discord. | |||
<summary> | |||
Returns the maximum length of title allowed by Discord. | |||
</summary> | |||
</member> | |||
<member name="F:Discord.EmbedBuilder.MaxDescriptionLength"> | |||
<summary> | |||
Returns the maximum length of description allowed by Discord. | |||
<summary> | |||
Returns the maximum length of description allowed by Discord. | |||
</summary> | |||
</member> | |||
<member name="F:Discord.EmbedBuilder.MaxEmbedLength"> | |||
<summary> | |||
Returns the maximum length of total characters allowed by Discord. | |||
<summary> | |||
Returns the maximum length of total characters allowed by Discord. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.EmbedBuilder.#ctor"> | |||
@@ -5701,9 +5796,9 @@ | |||
</member> | |||
<member name="P:Discord.EmbedBuilder.Fields"> | |||
<summary> Gets or sets the list of <see cref="T:Discord.EmbedFieldBuilder"/> of an <see cref="T:Discord.Embed"/>. </summary> | |||
<exception cref="T:System.ArgumentNullException" accessor="set">An embed builder's fields collection is set to | |||
<exception cref="T:System.ArgumentNullException" accessor="set">An embed builder's fields collection is set to | |||
<c>null</c>.</exception> | |||
<exception cref="T:System.ArgumentException" accessor="set">Description length exceeds <see cref="F:Discord.EmbedBuilder.MaxFieldCount"/>. | |||
<exception cref="T:System.ArgumentException" accessor="set">Fields count exceeds <see cref="F:Discord.EmbedBuilder.MaxFieldCount"/>. | |||
</exception> | |||
<returns> The list of existing <see cref="T:Discord.EmbedFieldBuilder"/>.</returns> | |||
</member> | |||
@@ -5744,7 +5839,7 @@ | |||
Gets the total length of all embed properties. | |||
</summary> | |||
<returns> | |||
The combined length of <see cref="P:Discord.EmbedBuilder.Title"/>, <see cref="P:Discord.EmbedAuthor.Name"/>, <see cref="P:Discord.EmbedBuilder.Description"/>, | |||
The combined length of <see cref="P:Discord.EmbedBuilder.Title"/>, <see cref="P:Discord.EmbedAuthor.Name"/>, <see cref="P:Discord.EmbedBuilder.Description"/>, | |||
<see cref="P:Discord.EmbedFooter.Text"/>, <see cref="P:Discord.EmbedField.Name"/>, and <see cref="P:Discord.EmbedField.Value"/>. | |||
</returns> | |||
</member> | |||
@@ -5758,7 +5853,7 @@ | |||
</returns> | |||
</member> | |||
<member name="M:Discord.EmbedBuilder.WithDescription(System.String)"> | |||
<summary> | |||
<summary> | |||
Sets the description of an <see cref="T:Discord.Embed"/>. | |||
</summary> | |||
<param name="description"> The description to be set. </param> | |||
@@ -5767,7 +5862,7 @@ | |||
</returns> | |||
</member> | |||
<member name="M:Discord.EmbedBuilder.WithUrl(System.String)"> | |||
<summary> | |||
<summary> | |||
Sets the URL of an <see cref="T:Discord.Embed"/>. | |||
</summary> | |||
<param name="url"> The URL to be set. </param> | |||
@@ -5776,7 +5871,7 @@ | |||
</returns> | |||
</member> | |||
<member name="M:Discord.EmbedBuilder.WithThumbnailUrl(System.String)"> | |||
<summary> | |||
<summary> | |||
Sets the thumbnail URL of an <see cref="T:Discord.Embed"/>. | |||
</summary> | |||
<param name="thumbnailUrl"> The thumbnail URL to be set. </param> | |||
@@ -7149,7 +7244,7 @@ | |||
Properties that are used to modify an <see cref="T:Discord.IUserMessage" /> with the specified changes. | |||
</summary> | |||
<remarks> | |||
The content of a message can be cleared with <see cref="F:System.String.Empty"/> if and only if an | |||
The content of a message can be cleared with <see cref="F:System.String.Empty"/> if and only if an | |||
<see cref="T:Discord.Embed"/> is present. | |||
</remarks> | |||
<seealso cref="M:Discord.IUserMessage.ModifyAsync(System.Action{Discord.MessageProperties},Discord.RequestOptions)"/> | |||
@@ -7162,9 +7257,9 @@ | |||
This must be less than the constant defined by <see cref="F:Discord.DiscordConfig.MaxMessageSize"/>. | |||
</remarks> | |||
</member> | |||
<member name="P:Discord.MessageProperties.Embed"> | |||
<member name="P:Discord.MessageProperties.Embeds"> | |||
<summary> | |||
Gets or sets the embed the message should display. | |||
Gets or sets the embeds of the message. | |||
</summary> | |||
</member> | |||
<member name="P:Discord.MessageProperties.Components"> | |||
@@ -54,6 +54,11 @@ namespace Discord | |||
/// <summary> | |||
/// A <see cref="IUser"/> or <see cref="IRole"/>. | |||
/// </summary> | |||
Mentionable = 9 | |||
Mentionable = 9, | |||
/// <summary> | |||
/// A <see cref="double"/>. | |||
/// </summary> | |||
Number = 10 | |||
} | |||
} |
@@ -1,8 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
@@ -29,6 +27,8 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the Action Rows for this Component Builder. | |||
/// </summary> | |||
/// <exception cref="ArgumentNullException" accessor="set"><see cref="ActionRows"/> cannot be null.</exception> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="ActionRows"/> count exceeds <see cref="MaxActionRowCount"/>.</exception> | |||
public List<ActionRowBuilder> ActionRows | |||
{ | |||
get => _actionRows; | |||
@@ -45,8 +45,8 @@ namespace Discord | |||
private List<ActionRowBuilder> _actionRows { get; set; } | |||
/// <summary> | |||
/// Adds a <see cref="SelectMenuBuilder"/> to the first row, if the first row cannot | |||
/// accept the component then it will add it to a row that can | |||
/// Adds a <see cref="SelectMenuBuilder"/> to the <see cref="ComponentBuilder"/> at the specific row. | |||
/// If the row cannot accept the component then it will add it to a row that can. | |||
/// </summary> | |||
/// <param name="label">The label of the menu.</param> | |||
/// <param name="customId">The custom id of the menu.</param> | |||
@@ -54,54 +54,51 @@ namespace Discord | |||
/// <param name="placeholder">The placeholder of the menu.</param> | |||
/// <param name="minValues">The min values of the placeholder.</param> | |||
/// <param name="maxValues">The max values of the placeholder.</param> | |||
/// <param name="disabled">Whether or not the menu is disabled.</param> | |||
/// <param name="row">The row to add the menu to.</param> | |||
/// <returns></returns> | |||
public ComponentBuilder WithSelectMenu(string label, string customId, List<SelectMenuOptionBuilder> options, | |||
string placeholder = null, int minValues = 1, int maxValues = 1, int row = 0) | |||
string placeholder = null, int minValues = 1, int maxValues = 1, bool disabled = false, int row = 0) | |||
{ | |||
return WithSelectMenu(new SelectMenuBuilder() | |||
.WithLabel(label) | |||
.WithCustomId(customId) | |||
.WithOptions(options) | |||
.WithPlaceholder(placeholder) | |||
.WithMaxValues(maxValues) | |||
.WithMinValues(minValues), | |||
.WithMinValues(minValues) | |||
.WithDisabled(disabled), | |||
row); | |||
} | |||
/// <summary> | |||
/// Adds a <see cref="SelectMenuBuilder"/> to the first row, if the first row cannot | |||
/// accept the component then it will add it to a row that can | |||
/// </summary> | |||
/// <param name="menu">The menu to add</param> | |||
/// <returns>The current builder.</returns> | |||
public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu) | |||
=> WithSelectMenu(menu, 0); | |||
/// <summary> | |||
/// Adds a <see cref="SelectMenuBuilder"/> to the current builder at the specific row. | |||
/// Adds a <see cref="SelectMenuBuilder"/> to the <see cref="ComponentBuilder"/> at the specific row. | |||
/// If the row cannot accept the component then it will add it to a row that can. | |||
/// </summary> | |||
/// <param name="menu">The menu to add.</param> | |||
/// <param name="row">The row to attempt to add this component on.</param> | |||
/// <exception cref="InvalidOperationException">There is no more row to add a menu.</exception> | |||
/// <exception cref="ArgumentException"><paramref name="row"/> must be less than <see cref="MaxActionRowCount"/>.</exception> | |||
/// <returns>The current builder.</returns> | |||
public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu, int row) | |||
public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu, int row = 0) | |||
{ | |||
Preconditions.LessThan(row, 5, nameof(row)); | |||
Preconditions.LessThan(row, MaxActionRowCount, nameof(row)); | |||
var builtMenu = menu.Build(); | |||
if (_actionRows == null) | |||
{ | |||
_actionRows = new List<ActionRowBuilder>(); | |||
_actionRows.Add(new ActionRowBuilder().WithComponent(builtMenu)); | |||
_actionRows = new List<ActionRowBuilder> | |||
{ | |||
new ActionRowBuilder().AddComponent(builtMenu) | |||
}; | |||
} | |||
else | |||
{ | |||
if (_actionRows.Count == row) | |||
_actionRows.Add(new ActionRowBuilder().WithComponent(builtMenu)); | |||
_actionRows.Add(new ActionRowBuilder().AddComponent(builtMenu)); | |||
else | |||
{ | |||
ActionRowBuilder actionRow = null; | |||
ActionRowBuilder actionRow; | |||
if (_actionRows.Count > row) | |||
actionRow = _actionRows.ElementAt(row); | |||
else | |||
@@ -111,11 +108,11 @@ namespace Discord | |||
} | |||
if (actionRow.CanTakeComponent(builtMenu)) | |||
actionRow.WithComponent(builtMenu); | |||
else if (row < 5) | |||
actionRow.AddComponent(builtMenu); | |||
else if (row < MaxActionRowCount) | |||
WithSelectMenu(menu, row + 1); | |||
else | |||
throw new ArgumentOutOfRangeException($"There is no more room to add a {nameof(builtMenu)}"); | |||
throw new InvalidOperationException($"There is no more row to add a {nameof(builtMenu)}"); | |||
} | |||
} | |||
@@ -123,7 +120,8 @@ namespace Discord | |||
} | |||
/// <summary> | |||
/// Adds a button to the specified row. | |||
/// Adds a <see cref="ButtonBuilder"/> with specified parameters to the <see cref="ComponentBuilder"/> at the specific row. | |||
/// If the row cannot accept the component then it will add it to a row that can. | |||
/// </summary> | |||
/// <param name="label">The label text for the newly added button.</param> | |||
/// <param name="style">The style of this newly added button.</param> | |||
@@ -134,8 +132,8 @@ namespace Discord | |||
/// <param name="row">The row the button should be placed on.</param> | |||
/// <returns>The current builder.</returns> | |||
public ComponentBuilder WithButton( | |||
string label, | |||
string customId, | |||
string label = null, | |||
string customId = null, | |||
ButtonStyle style = ButtonStyle.Primary, | |||
IEmote emote = null, | |||
string url = null, | |||
@@ -154,37 +152,32 @@ namespace Discord | |||
} | |||
/// <summary> | |||
/// Adds a button to the first row. | |||
/// </summary> | |||
/// <param name="button">The button to add to the first row.</param> | |||
/// <returns>The current builder.</returns> | |||
public ComponentBuilder WithButton(ButtonBuilder button) | |||
=> this.WithButton(button, 0); | |||
/// <summary> | |||
/// Adds a button to the specified row. | |||
/// Adds a <see cref="ButtonBuilder"/> to the <see cref="ComponentBuilder"/> at the specific row. | |||
/// If the row cannot accept the component then it will add it to a row that can. | |||
/// </summary> | |||
/// <param name="button">The button to add.</param> | |||
/// <param name="row">The row to add the button.</param> | |||
/// <exception cref="InvalidOperationException">There is no more row to add a menu.</exception> | |||
/// <exception cref="ArgumentException"><paramref name="row"/> must be less than <see cref="MaxActionRowCount"/>.</exception> | |||
/// <returns>The current builder.</returns> | |||
public ComponentBuilder WithButton(ButtonBuilder button, int row = 0) | |||
{ | |||
Preconditions.LessThan(row, 5, nameof(row)); | |||
Preconditions.LessThan(row, MaxActionRowCount, nameof(row)); | |||
var builtButton = button.Build(); | |||
if (_actionRows == null) | |||
{ | |||
_actionRows = new List<ActionRowBuilder>(); | |||
_actionRows.Add(new ActionRowBuilder().WithComponent(builtButton)); | |||
_actionRows.Add(new ActionRowBuilder().AddComponent(builtButton)); | |||
} | |||
else | |||
{ | |||
if (_actionRows.Count == row) | |||
_actionRows.Add(new ActionRowBuilder().WithComponent(builtButton)); | |||
_actionRows.Add(new ActionRowBuilder().AddComponent(builtButton)); | |||
else | |||
{ | |||
ActionRowBuilder actionRow = null; | |||
ActionRowBuilder actionRow; | |||
if(_actionRows.Count > row) | |||
actionRow = _actionRows.ElementAt(row); | |||
else | |||
@@ -194,11 +187,11 @@ namespace Discord | |||
} | |||
if (actionRow.CanTakeComponent(builtButton)) | |||
actionRow.WithComponent(builtButton); | |||
else if (row < 5) | |||
actionRow.AddComponent(builtButton); | |||
else if (row < MaxActionRowCount) | |||
WithButton(button, row + 1); | |||
else | |||
throw new ArgumentOutOfRangeException($"There is no more room to add a {nameof(button)}"); | |||
throw new InvalidOperationException($"There is no more row to add a {nameof(button)}"); | |||
} | |||
} | |||
@@ -208,7 +201,7 @@ namespace Discord | |||
/// <summary> | |||
/// Builds this builder into a <see cref="MessageComponent"/> used to send your components. | |||
/// </summary> | |||
/// <returns>A <see cref="MessageComponent"/> that can be sent with <see cref="IMessageChannel.SendMessageAsync(string, bool, Embed, RequestOptions, AllowedMentions, MessageReference, MessageComponent)"/></returns> | |||
/// <returns>A <see cref="MessageComponent"/> that can be sent with <see cref="IMessageChannel.SendMessageAsync(string, bool, Embed, RequestOptions, AllowedMentions, MessageReference, MessageComponent)"/>.</returns> | |||
public MessageComponent Build() | |||
{ | |||
if (this._actionRows != null) | |||
@@ -231,14 +224,22 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the components inside this row. | |||
/// </summary> | |||
/// <exception cref="ArgumentNullException" accessor="set"><see cref="Components"/> cannot be null.</exception> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="Components"/> count exceeds <see cref="MaxChildCount"/>.</exception> | |||
public List<IMessageComponent> Components | |||
{ | |||
get => _components; | |||
set | |||
{ | |||
if (value != null) | |||
if (value.Count > MaxChildCount) | |||
throw new ArgumentException(message: $"Action row can only contain {MaxChildCount} child components!", paramName: nameof(Components)); | |||
if (value == null) | |||
throw new ArgumentNullException(message: "Action row components cannot be null!", paramName: nameof(Components)); | |||
if (value.Count <= 0) | |||
throw new ArgumentException(message: "There must be at least 1 component in a row", paramName: nameof(Components)); | |||
if (value.Count > MaxChildCount) | |||
throw new ArgumentException(message: $"Action row can only contain {MaxChildCount} child components!", paramName: nameof(Components)); | |||
_components = value; | |||
} | |||
} | |||
@@ -249,6 +250,7 @@ namespace Discord | |||
/// Adds a list of components to the current row. | |||
/// </summary> | |||
/// <param name="components">The list of components to add.</param> | |||
/// <inheritdoc cref="Components"/> | |||
/// <returns>The current builder.</returns> | |||
public ActionRowBuilder WithComponents(List<IMessageComponent> components) | |||
{ | |||
@@ -260,14 +262,14 @@ namespace Discord | |||
/// Adds a component at the end of the current row. | |||
/// </summary> | |||
/// <param name="component">The component to add.</param> | |||
/// <exception cref="InvalidOperationException">Components count reached <see cref="MaxChildCount"/></exception> | |||
/// <returns>The current builder.</returns> | |||
public ActionRowBuilder WithComponent(IMessageComponent component) | |||
public ActionRowBuilder AddComponent(IMessageComponent component) | |||
{ | |||
if (this.Components == null) | |||
this.Components = new List<IMessageComponent>(); | |||
if (this.Components.Count >= MaxChildCount) | |||
throw new InvalidOperationException($"Components count reached {MaxChildCount}"); | |||
this.Components.Add(component); | |||
return this; | |||
} | |||
@@ -275,16 +277,8 @@ namespace Discord | |||
/// Builds the current builder to a <see cref="ActionRowComponent"/> that can be used within a <see cref="ComponentBuilder"/> | |||
/// </summary> | |||
/// <returns>A <see cref="ActionRowComponent"/> that can be used within a <see cref="ComponentBuilder"/></returns> | |||
/// <exception cref="ArgumentNullException"><see cref="Components"/> cannot be null.</exception> | |||
/// <exception cref="ArgumentException">There must be at least 1 component in a row.</exception> | |||
public ActionRowComponent Build() | |||
{ | |||
if (this.Components == null) | |||
throw new ArgumentNullException($"{nameof(Components)} cannot be null!"); | |||
if (this.Components.Count == 0) | |||
throw new ArgumentException("There must be at least 1 component in a row"); | |||
return new ActionRowComponent(this._components); | |||
} | |||
@@ -315,14 +309,14 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the label of the current button. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="Label"/> length exceeds <see cref="ComponentBuilder.MaxLabelLength"/>.</exception> | |||
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)); | |||
if (value != null && value.Length > ComponentBuilder.MaxLabelLength) | |||
throw new ArgumentException(message: $"Button label must be {ComponentBuilder.MaxLabelLength} characters or less!", paramName: nameof(Label)); | |||
_label = value; | |||
} | |||
@@ -331,14 +325,14 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the custom id of the current button. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="CustomId"/> length exceeds <see cref="ComponentBuilder.MaxCustomIdLength"/></exception> | |||
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)); | |||
if (value != null && value.Length > ComponentBuilder.MaxCustomIdLength) | |||
throw new ArgumentException(message: $"Custom Id must be {ComponentBuilder.MaxCustomIdLength} characters or less!", paramName: nameof(CustomId)); | |||
_customId = value; | |||
} | |||
} | |||
@@ -368,89 +362,97 @@ namespace Discord | |||
private string _customId; | |||
/// <summary> | |||
/// Creates a button with the <see cref="ButtonStyle.Link"/> style. | |||
/// Creates a new instance of a <see cref="ButtonBuilder"/>. | |||
/// </summary> | |||
public ButtonBuilder() { } | |||
/// <summary> | |||
/// Creates a new instance of a <see cref="ButtonBuilder"/>. | |||
/// </summary> | |||
/// <param name="label">The label to use on the newly created link button.</param> | |||
/// <param name="url">The url for this link button to go to.</param> | |||
/// <returns>A builder with the newly created button.</returns> | |||
public static ButtonBuilder CreateLinkButton(string label, string url) | |||
/// <param name="url">The url of this button.</param> | |||
/// <param name="customId">The custom ID of this button</param> | |||
/// <param name="style">The custom ID of this button</param> | |||
/// <param name="emote">The emote of this button</param> | |||
/// <param name="disabled">Disabled this button or not</param> | |||
public ButtonBuilder(string label = null, string customId = null, ButtonStyle style = ButtonStyle.Primary, string url = null, IEmote emote = null, bool disabled = false) | |||
{ | |||
var builder = new ButtonBuilder() | |||
.WithStyle(ButtonStyle.Link) | |||
.WithUrl(url) | |||
.WithLabel(label); | |||
this.CustomId = customId; | |||
this.Style = style; | |||
this.Url = url; | |||
this.Label = label; | |||
this.Disabled = disabled; | |||
this.Emote = emote; | |||
} | |||
return builder; | |||
/// <summary> | |||
/// Creates a new instance of a <see cref="ButtonBuilder"/> from instance of a <see cref="ButtonComponent"/>. | |||
/// </summary> | |||
public ButtonBuilder(ButtonComponent button) | |||
{ | |||
this.CustomId = button.CustomId; | |||
this.Style = button.Style; | |||
this.Url = button.Url; | |||
this.Label = button.Label; | |||
this.Disabled = button.Disabled; | |||
this.Emote = button.Emote; | |||
} | |||
/// <summary> | |||
/// Creates a button with the <see cref="ButtonStyle.Link"/> style. | |||
/// </summary> | |||
/// <param name="label">The label for this link button.</param> | |||
/// <param name="url">The url for this link button to go to.</param> | |||
/// <param name="emote">The emote for this link button</param> | |||
/// <returns>A builder with the newly created button.</returns> | |||
public static ButtonBuilder CreateLinkButton(string label, string url, IEmote emote = null) | |||
=> new ButtonBuilder(label, null, ButtonStyle.Link, url, emote: emote); | |||
/// <summary> | |||
/// Creates a button with the <see cref="ButtonStyle.Danger"/> style. | |||
/// </summary> | |||
/// <param name="label">The label for this danger button.</param> | |||
/// <param name="customId">The custom id for this danger button.</param> | |||
/// <param name="emote">The emote for this danger button</param> | |||
/// <returns>A builder with the newly created button.</returns> | |||
public static ButtonBuilder CreateDangerButton(string label, string customId) | |||
{ | |||
var builder = new ButtonBuilder() | |||
.WithStyle(ButtonStyle.Danger) | |||
.WithCustomId(customId) | |||
.WithLabel(label); | |||
return builder; | |||
} | |||
public static ButtonBuilder CreateDangerButton(string label, string customId, IEmote emote = null) | |||
=> new ButtonBuilder(label, customId, ButtonStyle.Danger, emote: emote); | |||
/// <summary> | |||
/// Creates a button with the <see cref="ButtonStyle.Primary"/> style. | |||
/// </summary> | |||
/// <param name="label">The label for this primary button.</param> | |||
/// <param name="customId">The custom id for this primary button.</param> | |||
/// <param name="emote">The emote for this primary button</param> | |||
/// <returns>A builder with the newly created button.</returns> | |||
public static ButtonBuilder CreatePrimaryButton(string label, string customId) | |||
{ | |||
var builder = new ButtonBuilder() | |||
.WithStyle(ButtonStyle.Primary) | |||
.WithCustomId(customId) | |||
.WithLabel(label); | |||
return builder; | |||
} | |||
public static ButtonBuilder CreatePrimaryButton(string label, string customId, IEmote emote = null) | |||
=> new ButtonBuilder(label, customId, emote: emote); | |||
/// <summary> | |||
/// Creates a button with the <see cref="ButtonStyle.Secondary"/> style. | |||
/// </summary> | |||
/// <param name="label">The label for this secondary button.</param> | |||
/// <param name="customId">The custom id for this secondary button.</param> | |||
/// <param name="emote">The emote for this secondary button</param> | |||
/// <returns>A builder with the newly created button.</returns> | |||
public static ButtonBuilder CreateSecondaryButton(string label, string customId) | |||
{ | |||
var builder = new ButtonBuilder() | |||
.WithStyle(ButtonStyle.Secondary) | |||
.WithCustomId(customId) | |||
.WithLabel(label); | |||
return builder; | |||
} | |||
public static ButtonBuilder CreateSecondaryButton(string label, string customId, IEmote emote = null) | |||
=> new ButtonBuilder(label, customId, ButtonStyle.Secondary, emote: emote); | |||
/// <summary> | |||
/// Creates a button with the <see cref="ButtonStyle.Success"/> style. | |||
/// </summary> | |||
/// <param name="label">The label for this success button.</param> | |||
/// <param name="customId">The custom id for this success button.</param> | |||
/// <param name="emote">The emote for this success button</param> | |||
/// <returns>A builder with the newly created button.</returns> | |||
public static ButtonBuilder CreateSuccessButton(string label, string customId) | |||
{ | |||
var builder = new ButtonBuilder() | |||
.WithStyle(ButtonStyle.Success) | |||
.WithCustomId(customId) | |||
.WithLabel(label); | |||
return builder; | |||
} | |||
public static ButtonBuilder CreateSuccessButton(string label, string customId, IEmote emote = null) | |||
=> new ButtonBuilder(label, customId, ButtonStyle.Success, emote: emote); | |||
/// <summary> | |||
/// Sets the current buttons label to the specified text. | |||
/// </summary> | |||
/// <param name="label">The text for the label</param> | |||
/// <inheritdoc cref="Label"/> | |||
/// <returns>The current builder.</returns> | |||
public ButtonBuilder WithLabel(string label) | |||
{ | |||
@@ -495,6 +497,7 @@ namespace Discord | |||
/// Sets the custom id of the current button. | |||
/// </summary> | |||
/// <param name="id">The id to use for the current button.</param> | |||
/// <inheritdoc cref="CustomId"/> | |||
/// <returns>The current builder.</returns> | |||
public ButtonBuilder WithCustomId(string id) | |||
{ | |||
@@ -517,15 +520,15 @@ namespace Discord | |||
/// Builds this builder into a <see cref="ButtonComponent"/> to be used in a <see cref="ComponentBuilder"/>. | |||
/// </summary> | |||
/// <returns>A <see cref="ButtonComponent"/> to be used in a <see cref="ComponentBuilder"/>.</returns> | |||
/// <exception cref="InvalidOperationException">A button cannot contain a URL and a CustomId.</exception> | |||
/// <exception cref="ArgumentException">A button must have an Emote or a label.</exception> | |||
/// <exception cref="InvalidOperationException">A button cannot contain a <see cref="Url"/> and a <see cref="CustomId"/>.</exception> | |||
/// <exception cref="InvalidOperationException">A button must have an <see cref="Emote"/> or a <see cref="Label"/>.</exception> | |||
public ButtonComponent Build() | |||
{ | |||
if (string.IsNullOrEmpty(this.Label) && this.Emote == null) | |||
throw new ArgumentException("A button must have an Emote or a label!"); | |||
throw new InvalidOperationException("A button must have an Emote or a label!"); | |||
if (!string.IsNullOrEmpty(this.Url) && !string.IsNullOrEmpty(this.CustomId)) | |||
throw new InvalidOperationException("A button cannot contain a URL and a CustomId"); | |||
throw new InvalidOperationException("A button cannot contain a URL and a CustomId!"); | |||
if (this.Style == ButtonStyle.Link && !string.IsNullOrEmpty(this.CustomId)) | |||
this.CustomId = null; | |||
@@ -557,33 +560,17 @@ namespace Discord | |||
/// </summary> | |||
public const int MaxOptionCount = 25; | |||
/// <summary> | |||
/// Gets or sets the label of the current select menu. | |||
/// </summary> | |||
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; | |||
} | |||
} | |||
/// <summary> | |||
/// Gets or sets the custom id of the current select menu. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="CustomId"/> length exceeds <see cref="ComponentBuilder.MaxCustomIdLength"/>.</exception> | |||
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)); | |||
if (value != null && value.Length > ComponentBuilder.MaxCustomIdLength) | |||
throw new ArgumentException(message: $"Custom Id must be {ComponentBuilder.MaxCustomIdLength} characters or less!", paramName: nameof(CustomId)); | |||
_customId = value; | |||
} | |||
} | |||
@@ -591,6 +578,7 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the placeholder text of the current select menu. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="Placeholder"/> length exceeds <see cref="MaxPlaceholderLength"/>.</exception> | |||
public string Placeholder | |||
{ | |||
get => _placeholder; | |||
@@ -606,32 +594,36 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the minimum values of the current select menu. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="MinValues"/> exceeds <see cref="MaxValuesCount"/>.</exception> | |||
public int MinValues | |||
{ | |||
get => _minvalues; | |||
get => _minValues; | |||
set | |||
{ | |||
Preconditions.LessThan(value, MaxValuesCount, nameof(MinValues)); | |||
_minvalues = value; | |||
_minValues = value; | |||
} | |||
} | |||
/// <summary> | |||
/// Gets or sets the maximum values of the current select menu. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="MaxValues"/> exceeds <see cref="MaxValuesCount"/>.</exception> | |||
public int MaxValues | |||
{ | |||
get => _maxvalues; | |||
get => _maxValues; | |||
set | |||
{ | |||
Preconditions.LessThan(value, MaxValuesCount, nameof(MaxValues)); | |||
_maxvalues = value; | |||
_maxValues = value; | |||
} | |||
} | |||
/// <summary> | |||
/// Gets or sets a collection of <see cref="SelectMenuOptionBuilder"/> for this current select menu. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="Options"/> count exceeds <see cref="MaxOptionCount"/>.</exception> | |||
/// <exception cref="ArgumentNullException" accessor="set"><see cref="Options"/> is null.</exception> | |||
public List<SelectMenuOptionBuilder> Options | |||
{ | |||
get => _options; | |||
@@ -639,16 +631,22 @@ namespace Discord | |||
{ | |||
if (value != null) | |||
Preconditions.LessThan(value.Count, MaxOptionCount, nameof(Options)); | |||
else | |||
throw new ArgumentNullException(nameof(value)); | |||
_options = value; | |||
} | |||
} | |||
private List<SelectMenuOptionBuilder> _options; | |||
private int _minvalues = 1; | |||
private int _maxvalues = 1; | |||
/// <summary> | |||
/// Gets or sets whether the current menu is disabled. | |||
/// </summary> | |||
public bool Disabled { get; set; } | |||
private List<SelectMenuOptionBuilder> _options = new List<SelectMenuOptionBuilder>(); | |||
private int _minValues = 1; | |||
private int _maxValues = 1; | |||
private string _placeholder; | |||
private string _label; | |||
private string _customId; | |||
/// <summary> | |||
@@ -657,33 +655,44 @@ namespace Discord | |||
public SelectMenuBuilder() { } | |||
/// <summary> | |||
/// Creates a new instance of a <see cref="SelectMenuBuilder"/>. | |||
/// Creates a new instance of a <see cref="SelectMenuBuilder"/> from instance of <see cref="SelectMenu"/>. | |||
/// </summary> | |||
/// <param name="customId">The custom id of this select menu.</param> | |||
/// <param name="options">The options for this select menu.</param> | |||
public SelectMenuBuilder(string customId, List<SelectMenuOptionBuilder> options) | |||
public SelectMenuBuilder(SelectMenu selectMenu) | |||
{ | |||
this.CustomId = customId; | |||
this.Options = options; | |||
this.Placeholder = selectMenu.Placeholder; | |||
this.CustomId = selectMenu.Placeholder; | |||
this.MaxValues = selectMenu.MaxValues; | |||
this.MinValues = selectMenu.MinValues; | |||
this.Disabled = selectMenu.Disabled; | |||
this.Options = selectMenu.Options? | |||
.Select(x => new SelectMenuOptionBuilder(x.Label, x.Value, x.Description, x.Emote, x.Default)) | |||
.ToList(); | |||
} | |||
/// <summary> | |||
/// Sets the field label. | |||
/// Creates a new instance of a <see cref="SelectMenuBuilder"/>. | |||
/// </summary> | |||
/// <param name="label">The value to set the field label to.</param> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
public SelectMenuBuilder WithLabel(string label) | |||
/// <param name="customId">The custom id of this select menu.</param> | |||
/// <param name="options">The options for this select menu.</param> | |||
/// <param name="placeholder">The placeholder of this select menu.</param> | |||
/// <param name="maxValues">The max values of this select menu.</param> | |||
/// <param name="minValues">The min values of this select menu.</param> | |||
/// <param name="disabled">Disabled this select menu or not.</param> | |||
public SelectMenuBuilder(string customId, List<SelectMenuOptionBuilder> options, string placeholder = null, int maxValues = 1, int minValues = 1, bool disabled = false) | |||
{ | |||
this.Label = label; | |||
return this; | |||
this.CustomId = customId; | |||
this.Options = options; | |||
this.Placeholder = placeholder; | |||
this.Disabled = disabled; | |||
this.MaxValues = maxValues; | |||
this.MinValues = minValues; | |||
} | |||
/// <summary> | |||
/// Sets the field CustomId. | |||
/// </summary> | |||
/// <param name="customId">The value to set the field CustomId to.</param> | |||
/// <inheritdoc cref="CustomId"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -697,6 +706,7 @@ namespace Discord | |||
/// Sets the field placeholder. | |||
/// </summary> | |||
/// <param name="placeholder">The value to set the field placeholder to.</param> | |||
/// <inheritdoc cref="Placeholder"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -710,6 +720,7 @@ namespace Discord | |||
/// Sets the field minValues. | |||
/// </summary> | |||
/// <param name="minValues">The value to set the field minValues to.</param> | |||
/// <inheritdoc cref="MinValues"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -723,6 +734,7 @@ namespace Discord | |||
/// Sets the field maxValues. | |||
/// </summary> | |||
/// <param name="maxValues">The value to set the field maxValues to.</param> | |||
/// <inheritdoc cref="MaxValues"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -736,6 +748,7 @@ namespace Discord | |||
/// Sets the field options. | |||
/// </summary> | |||
/// <param name="options">The value to set the field options to.</param> | |||
/// <inheritdoc cref="Options"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -745,15 +758,63 @@ namespace Discord | |||
return this; | |||
} | |||
/// <summary> | |||
/// Add one option to menu options. | |||
/// </summary> | |||
/// <param name="option">The option builder class containing the option properties.</param> | |||
/// <exception cref="InvalidOperationException">Options count reached <see cref="MaxOptionCount"/>.</exception> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
public SelectMenuBuilder AddOption(SelectMenuOptionBuilder option) | |||
{ | |||
if (this.Options.Count >= MaxOptionCount) | |||
throw new InvalidOperationException($"Options count reached {MaxOptionCount}."); | |||
this.Options.Add(option); | |||
return this; | |||
} | |||
/// <summary> | |||
/// Add one option to menu options. | |||
/// </summary> | |||
/// <param name="label">The label for this option.</param> | |||
/// <param name="value">The value of this option.</param> | |||
/// <param name="description">The description of this option.</param> | |||
/// <param name="emote">The emote of this option.</param> | |||
/// <param name="default">Render this option as selected by default or not.</param> | |||
/// <exception cref="InvalidOperationException">Options count reached <see cref="MaxOptionCount"/>.</exception> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
public SelectMenuBuilder AddOption(string label, string value, string description = null, IEmote emote = null, bool? @default = null) | |||
{ | |||
AddOption(new SelectMenuOptionBuilder(label, value, description, emote, @default)); | |||
return this; | |||
} | |||
/// <summary> | |||
/// Sets whether the current menu is disabled. | |||
/// </summary> | |||
/// <param name="disabled">Whether the current menu is disabled or not.</param> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
public SelectMenuBuilder WithDisabled(bool disabled) | |||
{ | |||
this.Disabled = disabled; | |||
return this; | |||
} | |||
/// <summary> | |||
/// Builds a <see cref="SelectMenu"/> | |||
/// </summary> | |||
/// <returns>The newly built <see cref="SelectMenu"/></returns> | |||
public SelectMenu Build() | |||
{ | |||
var opt = this.Options?.Select(x => x.Build()).ToList(); | |||
var options = this.Options?.Select(x => x.Build()).ToList(); | |||
return new SelectMenu(this.CustomId, opt, this.Placeholder, this.MinValues, this.MaxValues); | |||
return new SelectMenu(this.CustomId, options, this.Placeholder, this.MinValues, this.MaxValues, this.Disabled); | |||
} | |||
} | |||
@@ -770,6 +831,7 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the label of the current select menu. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="Label"/> length exceeds <see cref="ComponentBuilder.MaxLabelLength"/></exception> | |||
public string Label | |||
{ | |||
get => _label; | |||
@@ -786,14 +848,14 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets the custom id of the current select menu. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="Value"/> length exceeds <see cref="ComponentBuilder.MaxCustomIdLength"/>.</exception> | |||
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)); | |||
if (value != null && value.Length > ComponentBuilder.MaxCustomIdLength) | |||
throw new ArgumentException(message: $"Value must be {ComponentBuilder.MaxCustomIdLength} characters or less!", paramName: nameof(Value)); | |||
_value = value; | |||
} | |||
} | |||
@@ -801,13 +863,14 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or sets this menu options description. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"><see cref="Description"/> length exceeds <see cref="MaxDescriptionLength"/>.</exception> | |||
public string Description | |||
{ | |||
get => _description; | |||
set | |||
{ | |||
if (value != null) | |||
Preconditions.LessThan(value.Length, MaxDescriptionLength, nameof(Description)); | |||
if (value != null && value.Length > MaxDescriptionLength) | |||
throw new ArgumentException($"Description must be {MaxDescriptionLength} characters or less!", nameof(Description)); | |||
_description = value; | |||
} | |||
@@ -837,16 +900,35 @@ namespace Discord | |||
/// </summary> | |||
/// <param name="label">The label for this option.</param> | |||
/// <param name="value">The value of this option.</param> | |||
public SelectMenuOptionBuilder(string label, string value) | |||
/// <param name="description">The description of this option.</param> | |||
/// <param name="emote">The emote of this option.</param> | |||
/// <param name="default">Render this option as selected by default or not.</param> | |||
public SelectMenuOptionBuilder(string label, string value, string description = null, IEmote emote = null, bool? @default = null) | |||
{ | |||
this.Label = label; | |||
this.Value = value; | |||
this.Description = description; | |||
this.Emote = emote; | |||
this.Default = @default; | |||
} | |||
/// <summary> | |||
/// Creates a new instance of a <see cref="SelectMenuOptionBuilder"/> from instance of a <see cref="SelectMenuOption"/>. | |||
/// </summary> | |||
public SelectMenuOptionBuilder(SelectMenuOption option) | |||
{ | |||
this.Label = option.Label; | |||
this.Value = option.Value; | |||
this.Description = option.Description; | |||
this.Emote = option.Emote; | |||
this.Default = option.Default; | |||
} | |||
/// <summary> | |||
/// Sets the field label. | |||
/// </summary> | |||
/// <param name="label">The value to set the field label to.</param> | |||
/// <inheritdoc cref="Label"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -860,6 +942,7 @@ namespace Discord | |||
/// Sets the field value. | |||
/// </summary> | |||
/// <param name="value">The value to set the field value to.</param> | |||
/// <inheritdoc cref="Value"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -873,6 +956,7 @@ namespace Discord | |||
/// Sets the field description. | |||
/// </summary> | |||
/// <param name="description">The value to set the field description to.</param> | |||
/// <inheritdoc cref="Description"/> | |||
/// <returns> | |||
/// The current builder. | |||
/// </returns> | |||
@@ -39,13 +39,19 @@ namespace Discord | |||
/// </summary> | |||
public int MaxValues { get; } | |||
internal SelectMenu(string customId, List<SelectMenuOption> options, string placeholder, int minValues, int maxValues) | |||
/// <summary> | |||
/// Whether this menu is disabled or not. | |||
/// </summary> | |||
public bool Disabled { get; } | |||
internal SelectMenu(string customId, List<SelectMenuOption> options, string placeholder, int minValues, int maxValues, bool disabled) | |||
{ | |||
this.CustomId = customId; | |||
this.Options = options; | |||
this.Placeholder = placeholder; | |||
this.MinValues = minValues; | |||
this.MaxValues = maxValues; | |||
this.Disabled = disabled; | |||
} | |||
} | |||
} |
@@ -23,7 +23,7 @@ namespace Discord | |||
/// <summary> | |||
/// Returns the maximum count of command options allowed by Discord | |||
/// </summary> | |||
public const int MaxOptionsCount = 10; | |||
public const int MaxOptionsCount = 25; | |||
/// <summary> | |||
/// The name of this slash command. | |||
@@ -43,7 +43,7 @@ namespace Discord | |||
// Discord updated the docs, this regex prevents special characters like @!$%(... etc, | |||
// https://discord.com/developers/docs/interactions/slash-commands#applicationcommand | |||
if (!Regex.IsMatch(value, @"^[\w-]{3,32}$")) | |||
throw new ArgumentException("Command name cannot contian any special characters or whitespaces!"); | |||
throw new ArgumentException("Command name cannot contain any special characters or whitespaces!"); | |||
_name = value; | |||
} | |||
@@ -282,7 +282,7 @@ namespace Discord | |||
/// <summary> | |||
/// The maximum number of choices allowed by Discord. | |||
/// </summary> | |||
public const int MaxChoiceCount = 10; | |||
public const int MaxChoiceCount = 25; | |||
private string _name; | |||
private string _description; | |||
@@ -16,20 +16,20 @@ namespace Discord | |||
private EmbedThumbnail? _thumbnail; | |||
private List<EmbedFieldBuilder> _fields; | |||
/// <summary> | |||
/// Returns the maximum number of fields allowed by Discord. | |||
/// <summary> | |||
/// Returns the maximum number of fields allowed by Discord. | |||
/// </summary> | |||
public const int MaxFieldCount = 25; | |||
/// <summary> | |||
/// Returns the maximum length of title allowed by Discord. | |||
/// <summary> | |||
/// Returns the maximum length of title allowed by Discord. | |||
/// </summary> | |||
public const int MaxTitleLength = 256; | |||
/// <summary> | |||
/// Returns the maximum length of description allowed by Discord. | |||
/// <summary> | |||
/// Returns the maximum length of description allowed by Discord. | |||
/// </summary> | |||
public const int MaxDescriptionLength = 4096; | |||
/// <summary> | |||
/// Returns the maximum length of total characters allowed by Discord. | |||
/// <summary> | |||
/// Returns the maximum length of total characters allowed by Discord. | |||
/// </summary> | |||
public const int MaxEmbedLength = 6000; | |||
@@ -88,9 +88,9 @@ namespace Discord | |||
} | |||
/// <summary> Gets or sets the list of <see cref="EmbedFieldBuilder"/> of an <see cref="Embed"/>. </summary> | |||
/// <exception cref="ArgumentNullException" accessor="set">An embed builder's fields collection is set to | |||
/// <exception cref="ArgumentNullException" accessor="set">An embed builder's fields collection is set to | |||
/// <c>null</c>.</exception> | |||
/// <exception cref="ArgumentException" accessor="set">Description length exceeds <see cref="MaxFieldCount"/>. | |||
/// <exception cref="ArgumentException" accessor="set">Fields count exceeds <see cref="MaxFieldCount"/>. | |||
/// </exception> | |||
/// <returns> The list of existing <see cref="EmbedFieldBuilder"/>.</returns> | |||
public List<EmbedFieldBuilder> Fields | |||
@@ -137,7 +137,7 @@ namespace Discord | |||
/// Gets the total length of all embed properties. | |||
/// </summary> | |||
/// <returns> | |||
/// The combined length of <see cref="Title"/>, <see cref="EmbedAuthor.Name"/>, <see cref="Description"/>, | |||
/// The combined length of <see cref="Title"/>, <see cref="EmbedAuthor.Name"/>, <see cref="Description"/>, | |||
/// <see cref="EmbedFooter.Text"/>, <see cref="EmbedField.Name"/>, and <see cref="EmbedField.Value"/>. | |||
/// </returns> | |||
public int Length | |||
@@ -166,7 +166,7 @@ namespace Discord | |||
Title = title; | |||
return this; | |||
} | |||
/// <summary> | |||
/// <summary> | |||
/// Sets the description of an <see cref="Embed"/>. | |||
/// </summary> | |||
/// <param name="description"> The description to be set. </param> | |||
@@ -178,7 +178,7 @@ namespace Discord | |||
Description = description; | |||
return this; | |||
} | |||
/// <summary> | |||
/// <summary> | |||
/// Sets the URL of an <see cref="Embed"/>. | |||
/// </summary> | |||
/// <param name="url"> The URL to be set. </param> | |||
@@ -190,7 +190,7 @@ namespace Discord | |||
Url = url; | |||
return this; | |||
} | |||
/// <summary> | |||
/// <summary> | |||
/// Sets the thumbnail URL of an <see cref="Embed"/>. | |||
/// </summary> | |||
/// <param name="thumbnailUrl"> The thumbnail URL to be set. </param> | |||
@@ -4,7 +4,7 @@ namespace Discord | |||
/// Properties that are used to modify an <see cref="IUserMessage" /> with the specified changes. | |||
/// </summary> | |||
/// <remarks> | |||
/// The content of a message can be cleared with <see cref="System.String.Empty"/> if and only if an | |||
/// The content of a message can be cleared with <see cref="System.String.Empty"/> if and only if an | |||
/// <see cref="Discord.Embed"/> is present. | |||
/// </remarks> | |||
/// <seealso cref="IUserMessage.ModifyAsync"/> | |||
@@ -17,10 +17,11 @@ namespace Discord | |||
/// This must be less than the constant defined by <see cref="DiscordConfig.MaxMessageSize"/>. | |||
/// </remarks> | |||
public Optional<string> Content { get; set; } | |||
/// <summary> | |||
/// Gets or sets the embed the message should display. | |||
/// Gets or sets the embeds of the message. | |||
/// </summary> | |||
public Optional<Embed> Embed { get; set; } | |||
public Optional<Embed[]> Embeds { get; set; } | |||
/// <summary> | |||
/// Gets or sets the components for this message. | |||
@@ -7,7 +7,7 @@ namespace Discord | |||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
public struct Optional<T> | |||
{ | |||
public static Optional<T> Unspecified => default(Optional<T>); | |||
public static Optional<T> Unspecified => default; | |||
private readonly T _value; | |||
/// <summary> Gets the value for this parameter. </summary> | |||
@@ -43,18 +43,18 @@ namespace Discord | |||
public override int GetHashCode() => IsSpecified ? _value.GetHashCode() : 0; | |||
public override string ToString() => IsSpecified ? _value?.ToString() : null; | |||
private string DebuggerDisplay => IsSpecified ? (_value?.ToString() ?? "<null>") : "<unspecified>"; | |||
private string DebuggerDisplay => IsSpecified ? _value?.ToString() ?? "<null>" : "<unspecified>"; | |||
public static implicit operator Optional<T>(T value) => new Optional<T>(value); | |||
public static implicit operator Optional<T>(T value) => new(value); | |||
public static explicit operator T(Optional<T> value) => value.Value; | |||
} | |||
public static class Optional | |||
{ | |||
public static Optional<T> Create<T>() => Optional<T>.Unspecified; | |||
public static Optional<T> Create<T>(T value) => new Optional<T>(value); | |||
public static Optional<T> Create<T>(T value) => new(value); | |||
public static T? ToNullable<T>(this Optional<T> val) | |||
where T : struct | |||
=> val.IsSpecified ? val.Value : (T?)null; | |||
=> val.IsSpecified ? val.Value : null; | |||
} | |||
} |
@@ -1,13 +1,8 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.API | |||
{ | |||
internal class InteractionApplicationCommandCallbackData | |||
internal class InteractionCallbackData | |||
{ | |||
[JsonProperty("tts")] | |||
public Optional<bool> TTS { get; set; } | |||
@@ -16,7 +11,7 @@ namespace Discord.API | |||
public Optional<string> Content { get; set; } | |||
[JsonProperty("embeds")] | |||
public Optional<Embed[]> Embeds { get; set; } | |||
public Optional<API.Embed[]> Embeds { get; set; } | |||
[JsonProperty("allowed_mentions")] | |||
public Optional<AllowedMentions> AllowedMentions { get; set; } | |||
@@ -27,12 +22,5 @@ namespace Discord.API | |||
[JsonProperty("components")] | |||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||
public InteractionApplicationCommandCallbackData() { } | |||
public InteractionApplicationCommandCallbackData(string text) | |||
{ | |||
this.Content = text; | |||
} | |||
} | |||
} |
@@ -13,6 +13,6 @@ namespace Discord.API | |||
public InteractionResponseType Type { get; set; } | |||
[JsonProperty("data")] | |||
public Optional<InteractionApplicationCommandCallbackData> Data { get; set; } | |||
public Optional<InteractionCallbackData> Data { get; set; } | |||
} | |||
} |
@@ -27,6 +27,9 @@ namespace Discord.API | |||
[JsonProperty("max_values")] | |||
public int MaxValues { get; set; } | |||
[JsonProperty("disabled")] | |||
public bool Disabled { get; set; } | |||
public SelectMenuComponent() { } | |||
public SelectMenuComponent(Discord.SelectMenu component) | |||
@@ -37,6 +40,7 @@ namespace Discord.API | |||
this.Placeholder = component.Placeholder; | |||
this.MinValues = component.MinValues; | |||
this.MaxValues = component.MaxValues; | |||
this.Disabled = component.Disabled; | |||
} | |||
} | |||
} |
@@ -7,7 +7,7 @@ namespace Discord.API.Rest | |||
internal class CreateWebhookMessageParams | |||
{ | |||
[JsonProperty("content")] | |||
public string Content { get; } | |||
public string Content { get; set; } | |||
[JsonProperty("nonce")] | |||
public Optional<string> Nonce { get; set; } | |||
@@ -32,10 +32,5 @@ namespace Discord.API.Rest | |||
[JsonProperty("components")] | |||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||
public CreateWebhookMessageParams(string content) | |||
{ | |||
Content = content; | |||
} | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.API.Rest | |||
{ | |||
internal class ModifyGuildApplicationCommandPermissionsParams | |||
{ | |||
[JsonProperty("permissions")] | |||
public ApplicationCommandPermissions[] Permissions { get; set; } | |||
} | |||
} |
@@ -8,8 +8,8 @@ namespace Discord.API.Rest | |||
{ | |||
[JsonProperty("content")] | |||
public Optional<string> Content { get; set; } | |||
[JsonProperty("embed")] | |||
public Optional<Embed> Embed { get; set; } | |||
[JsonProperty("embeds")] | |||
public Optional<API.Embed[]> Embeds { get; set; } | |||
[JsonProperty("components")] | |||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||
[JsonProperty("flags")] | |||
@@ -55,7 +55,7 @@ namespace Discord.API | |||
_restClientProvider = restClientProvider; | |||
UserAgent = userAgent; | |||
DefaultRetryMode = defaultRetryMode; | |||
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; | |||
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver(), NullValueHandling = NullValueHandling.Ignore }; | |||
UseSystemClock = useSystemClock; | |||
RequestQueue = new RequestQueue(); | |||
@@ -1158,7 +1158,7 @@ namespace Discord.API | |||
return await SendAsync<GuildApplicationCommandPermission>("GET", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}/permissions", new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
public async Task<GuildApplicationCommandPermission> ModifyApplicationCommandPermissions(ApplicationCommandPermissions[] permissions, ulong guildId, ulong commandId, RequestOptions options = null) | |||
public async Task<GuildApplicationCommandPermission> ModifyApplicationCommandPermissions(ModifyGuildApplicationCommandPermissionsParams permissions, ulong guildId, ulong commandId, RequestOptions options = null) | |||
{ | |||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
Preconditions.NotEqual(commandId, 0, nameof(commandId)); | |||
@@ -4,7 +4,6 @@ using Discord.Net; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.Rest | |||
@@ -21,7 +20,7 @@ namespace Discord.Rest | |||
return client.ApiClient.BulkOverwriteGlobalApplicationCommands(new CreateApplicationCommandParams[0], options); | |||
} | |||
public static Task SendInteractionResponse(BaseDiscordClient client, IMessageChannel channel, InteractionResponse response, | |||
public static Task SendInteractionResponse(BaseDiscordClient client, InteractionResponse response, | |||
ulong interactionId, string interactionToken, RequestOptions options = null) | |||
{ | |||
return client.ApiClient.CreateInteractionResponse(response, interactionId, interactionToken, options); | |||
@@ -298,14 +297,14 @@ namespace Discord.Rest | |||
func(args); | |||
bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(message.Content); | |||
bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : message.Embeds.Any(); | |||
bool hasEmbed = args.Embeds.IsSpecified ? args.Embeds.Value != null : message.Embeds.Any(); | |||
if (!hasText && !hasEmbed) | |||
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); | |||
var apiArgs = new API.Rest.ModifyInteractionResponseParams | |||
{ | |||
Content = args.Content, | |||
Embeds = args.Embed.IsSpecified ? new API.Embed[] { args.Embed.Value.ToModel() } : Optional.Create<API.Embed[]>(), | |||
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value.Select(x => x.ToModel()).ToArray() : Optional.Create<API.Embed[]>(), | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional<API.AllowedMentions>.Unspecified, | |||
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified, | |||
}; | |||
@@ -316,27 +315,22 @@ namespace Discord.Rest | |||
public static async Task DeleteFollowupMessage(BaseDiscordClient client, RestFollowupMessage message, RequestOptions options = null) | |||
=> await client.ApiClient.DeleteInteractionFollowupMessage(message.Id, message.Token, options); | |||
public static async Task<Discord.API.Message> ModifyInteractionResponse(BaseDiscordClient client, RestInteractionMessage message, Action<MessageProperties> func, | |||
public static async Task<Message> ModifyInteractionResponse(BaseDiscordClient client, string token, Action<MessageProperties> func, | |||
RequestOptions options = null) | |||
{ | |||
var args = new MessageProperties(); | |||
func(args); | |||
bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(message.Content); | |||
bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : message.Embeds.Any(); | |||
if (!hasText && !hasEmbed) | |||
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); | |||
var apiArgs = new API.Rest.ModifyInteractionResponseParams | |||
var apiArgs = new ModifyInteractionResponseParams | |||
{ | |||
Content = args.Content, | |||
Embeds = args.Embed.IsSpecified ? new API.Embed[] { args.Embed.Value.ToModel() } : Optional.Create<API.Embed[]>(), | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional<API.AllowedMentions>.Unspecified, | |||
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value?.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified, | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified, | |||
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified, | |||
Flags = args.Flags | |||
}; | |||
return await client.ApiClient.ModifyInteractionResponse(apiArgs, message.Token, options).ConfigureAwait(false); | |||
return await client.ApiClient.ModifyInteractionResponse(apiArgs, token, options).ConfigureAwait(false); | |||
} | |||
public static async Task DeletedInteractionResponse(BaseDiscordClient client, RestInteractionMessage message, RequestOptions options = null) | |||
@@ -378,21 +372,26 @@ namespace Discord.Rest | |||
Preconditions.AtMost(args.Length, 10, nameof(args)); | |||
Preconditions.GreaterThan(args.Length, 0, nameof(args)); | |||
List<ApplicationCommandPermissions> models = new List<ApplicationCommandPermissions>(); | |||
List<ApplicationCommandPermissions> permissionsList = new List<ApplicationCommandPermissions>(); | |||
foreach (var arg in args) | |||
{ | |||
var model = new ApplicationCommandPermissions() | |||
var permissions = new ApplicationCommandPermissions() | |||
{ | |||
Id = arg.TargetId, | |||
Permission = arg.Permission, | |||
Type = arg.TargetType | |||
}; | |||
models.Add(model); | |||
permissionsList.Add(permissions); | |||
} | |||
var apiModel = await client.ApiClient.ModifyApplicationCommandPermissions(models.ToArray(), guildId, commandId, options); | |||
ModifyGuildApplicationCommandPermissionsParams model = new ModifyGuildApplicationCommandPermissionsParams() | |||
{ | |||
Permissions = permissionsList.ToArray() | |||
}; | |||
var apiModel = await client.ApiClient.ModifyApplicationCommandPermissions(model, guildId, commandId, options); | |||
return new GuildApplicationCommandPermission(apiModel.Id, apiModel.ApplicationId, guildId, apiModel.Permissions.Select( | |||
x => new ApplicationCommandPermission(x.Id, x.Type, x.Permission)).ToArray()); | |||
@@ -30,11 +30,11 @@ namespace Discord.Rest | |||
var args = new MessageProperties(); | |||
func(args); | |||
if (msg.Author.Id != client.CurrentUser.Id && (args.Content.IsSpecified || args.Embed.IsSpecified || args.AllowedMentions.IsSpecified)) | |||
if (msg.Author.Id != client.CurrentUser.Id && (args.Content.IsSpecified || args.Embeds.IsSpecified || args.AllowedMentions.IsSpecified)) | |||
throw new InvalidOperationException("Only the author of a message may modify the message content, embed, or allowed mentions."); | |||
bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(msg.Content); | |||
bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : msg.Embeds.Any(); | |||
bool hasEmbed = args.Embeds.IsSpecified ? args.Embeds.Value != null : msg.Embeds.Any(); | |||
if (!hasText && !hasEmbed) | |||
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); | |||
@@ -61,13 +61,13 @@ namespace Discord.Rest | |||
} | |||
} | |||
var apiArgs = new API.Rest.ModifyMessageParams | |||
var apiArgs = new ModifyMessageParams | |||
{ | |||
Content = args.Content, | |||
Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create<API.Embed>(), | |||
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified, | |||
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified, | |||
Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create<MessageFlags?>(), | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create<API.AllowedMentions>(), | |||
Flags = args.Flags, | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional<API.AllowedMentions>.Unspecified, | |||
}; | |||
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | |||
} | |||
@@ -78,7 +78,7 @@ namespace Discord.Rest | |||
var args = new MessageProperties(); | |||
func(args); | |||
if ((args.Content.IsSpecified && string.IsNullOrEmpty(args.Content.Value)) && (args.Embed.IsSpecified && args.Embed.Value == null)) | |||
if (args.Content.IsSpecified && string.IsNullOrEmpty(args.Content.Value) && args.Embeds.IsSpecified && args.Embeds.Value == null) | |||
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); | |||
if (args.AllowedMentions.IsSpecified) | |||
@@ -86,6 +86,7 @@ namespace Discord.Rest | |||
AllowedMentions allowedMentions = args.AllowedMentions.Value; | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(args.Embeds.Value?.Length ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed."); | |||
// check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||
@@ -107,7 +108,7 @@ namespace Discord.Rest | |||
var apiArgs = new API.Rest.ModifyMessageParams | |||
{ | |||
Content = args.Content, | |||
Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create<API.Embed>(), | |||
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value.Select(x => x.ToModel()).ToArray() : Optional.Create<API.Embed[]>(), | |||
Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create<MessageFlags?>(), | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create<API.AllowedMentions>(), | |||
}; | |||
@@ -1,7 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.Message; | |||
@@ -64,7 +61,7 @@ namespace Discord.Rest | |||
{ | |||
try | |||
{ | |||
var model = await InteractionHelper.ModifyInteractionResponse(Discord, this, func, options).ConfigureAwait(false); | |||
var model = await InteractionHelper.ModifyInteractionResponse(Discord, this.Token, func, options).ConfigureAwait(false); | |||
this.Update(model); | |||
} | |||
catch (Discord.Net.HttpException x) | |||
@@ -174,7 +174,8 @@ namespace Discord.Rest | |||
z.Default.ToNullable())).ToList(), | |||
parsed.Placeholder.GetValueOrDefault(), | |||
parsed.MinValues, | |||
parsed.MaxValues | |||
parsed.MaxValues, | |||
parsed.Disabled | |||
); | |||
} | |||
default: | |||
@@ -230,7 +231,7 @@ namespace Discord.Rest | |||
IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | |||
/// <inheritdoc /> | |||
IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | |||
/// <inheritdoc/> | |||
IReadOnlyCollection<IMessageComponent> IMessage.Components => Components; | |||
@@ -3426,16 +3426,24 @@ | |||
The message that contained the trigger for this interaction. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketMessageComponent.RespondAsync(Discord.Embed[],System.String,System.Boolean,Discord.InteractionResponseType,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<member name="M:Discord.WebSocket.SocketMessageComponent.RespondAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<inheritdoc/> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketMessageComponent.FollowupAsync(Discord.Embed[],System.String,System.Boolean,System.Boolean,Discord.InteractionResponseType,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<member name="M:Discord.WebSocket.SocketMessageComponent.UpdateAsync(System.Action{Discord.MessageProperties},Discord.RequestOptions)"> | |||
<summary> | |||
Updates the original message of the component on which the interaction was received on. | |||
</summary> | |||
<param name="func">A delegate containing the properties to modify the message with.</param> | |||
<param name="options">The request options for this async request.</param> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketMessageComponent.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<inheritdoc/> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketMessageComponent.AcknowledgeAsync(Discord.RequestOptions)"> | |||
<member name="M:Discord.WebSocket.SocketMessageComponent.DeferAsync(Discord.RequestOptions)"> | |||
<summary> | |||
Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredUpdateMessage"/>. | |||
</summary> | |||
<param name="options">The request options for this async request.</param> | |||
<returns> | |||
A task that represents the asynchronous operation of acknowledging the interaction. | |||
</returns> | |||
@@ -3544,14 +3552,19 @@ | |||
The data associated with this interaction. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketSlashCommand.RespondAsync(Discord.Embed[],System.String,System.Boolean,Discord.InteractionResponseType,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<member name="M:Discord.WebSocket.SocketSlashCommand.RespondAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<inheritdoc/> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketSlashCommand.FollowupAsync(Discord.Embed[],System.String,System.Boolean,System.Boolean,Discord.InteractionResponseType,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<member name="M:Discord.WebSocket.SocketSlashCommand.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<inheritdoc/> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketSlashCommand.AcknowledgeAsync(Discord.RequestOptions)"> | |||
<inheritdoc/> | |||
<member name="M:Discord.WebSocket.SocketSlashCommand.DeferAsync(Discord.RequestOptions)"> | |||
<summary> | |||
Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||
</summary> | |||
<returns> | |||
A task that represents the asynchronous operation of acknowledging the interaction. | |||
</returns> | |||
</member> | |||
<member name="T:Discord.WebSocket.SocketSlashCommandData"> | |||
<summary> | |||
@@ -3628,18 +3641,17 @@ | |||
<see langword="true"/> if the token is valid for replying to, otherwise <see langword="false"/>. | |||
</summary> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketInteraction.RespondAsync(System.String,System.Boolean,Discord.Embed,Discord.InteractionResponseType,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<member name="M:Discord.WebSocket.SocketInteraction.RespondAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<summary> | |||
Responds to an Interaction. | |||
Responds to an Interaction with type <see cref="F:Discord.InteractionResponseType.ChannelMessageWithSource"/>. | |||
<para> | |||
If you have <see cref="P:Discord.WebSocket.DiscordSocketConfig.AlwaysAcknowledgeInteractions"/> set to <see langword="true"/>, You should use | |||
<see cref="M:Discord.WebSocket.SocketInteraction.FollowupAsync(Discord.Embed[],System.String,System.Boolean,System.Boolean,Discord.InteractionResponseType,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"/> instead. | |||
<see cref="!:FollowupAsync(Discord.Embed[],string,bool,bool,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"/> instead. | |||
</para> | |||
</summary> | |||
<param name="text">The text of the message to be sent.</param> | |||
<param name="embeds">A array of embeds to send with this response. Max 10</param> | |||
<param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
<param name="embed">A <see cref="T:Discord.Embed"/> to send with this response.</param> | |||
<param name="type">The type of response to this Interaction.</param> | |||
<param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
<param name="allowedMentions">The allowed mentions for this response.</param> | |||
<param name="options">The request options for this response.</param> | |||
@@ -3647,50 +3659,14 @@ | |||
<exception cref="T:System.ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="F:Discord.DiscordConfig.MaxMessageSize"/>.</exception> | |||
<exception cref="T:System.InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketInteraction.FollowupAsync(System.String,System.Boolean,Discord.Embed,Discord.InteractionResponseType,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<member name="M:Discord.WebSocket.SocketInteraction.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<summary> | |||
Sends a followup message for this interaction. | |||
</summary> | |||
<param name="text">The text of the message to be sent</param> | |||
<param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
<param name="embed">A <see cref="T:Discord.Embed"/> to send with this response</param> | |||
<param name="type">The type of response to this Interaction.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
<param name="allowedMentions">The allowed mentions for this response.</param> | |||
<param name="options">The request options for this response.</param> | |||
<param name="component">A <see cref="T:Discord.MessageComponent"/> to be sent with this response</param> | |||
<returns> | |||
The sent message. | |||
</returns> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketInteraction.RespondAsync(Discord.Embed[],System.String,System.Boolean,Discord.InteractionResponseType,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<summary> | |||
Responds to an Interaction. | |||
<para> | |||
If you have <see cref="P:Discord.WebSocket.DiscordSocketConfig.AlwaysAcknowledgeInteractions"/> set to <see langword="true"/>, You should use | |||
<see cref="M:Discord.WebSocket.SocketInteraction.FollowupAsync(Discord.Embed[],System.String,System.Boolean,System.Boolean,Discord.InteractionResponseType,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"/> instead. | |||
</para> | |||
</summary> | |||
<param name="text">The text of the message to be sent.</param> | |||
<param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
<param name="embeds">A array of embeds to send with this response. Max 10</param> | |||
<param name="type">The type of response to this Interaction.</param> | |||
<param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
<param name="allowedMentions">The allowed mentions for this response.</param> | |||
<param name="options">The request options for this response.</param> | |||
<param name="component">A <see cref="T:Discord.MessageComponent"/> to be sent with this response</param> | |||
<exception cref="T:System.ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="F:Discord.DiscordConfig.MaxMessageSize"/>.</exception> | |||
<exception cref="T:System.InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketInteraction.FollowupAsync(Discord.Embed[],System.String,System.Boolean,System.Boolean,Discord.InteractionResponseType,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||
<summary> | |||
Sends a followup message for this interaction. | |||
</summary> | |||
<param name="text">The text of the message to be sent</param> | |||
<param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
<param name="embeds">A array of embeds to send with this response. Max 10</param> | |||
<param name="type">The type of response to this Interaction.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
<param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
<param name="allowedMentions">The allowed mentions for this response.</param> | |||
<param name="options">The request options for this response.</param> | |||
<param name="component">A <see cref="T:Discord.MessageComponent"/> to be sent with this response</param> | |||
@@ -3703,11 +3679,19 @@ | |||
Gets the original response for this interaction. | |||
</summary> | |||
<param name="options">The request options for this async request.</param> | |||
<returns>A <see cref="T:Discord.Rest.RestInteractionMessage"/> that represents the intitial response, or <see langword="null"/> if there is no response.</returns> | |||
<returns>A <see cref="T:Discord.Rest.RestInteractionMessage"/> that represents the initial response.</returns> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketInteraction.AcknowledgeAsync(Discord.RequestOptions)"> | |||
<member name="M:Discord.WebSocket.SocketInteraction.ModifyOriginalResponseAsync(System.Action{Discord.MessageProperties},Discord.RequestOptions)"> | |||
<summary> | |||
Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||
Edits original response for this interaction. | |||
</summary> | |||
<param name="func">A delegate containing the properties to modify the message with.</param> | |||
<param name="options">The request options for this async request.</param> | |||
<returns>A <see cref="T:Discord.Rest.RestInteractionMessage"/> that represents the initial response.</returns> | |||
</member> | |||
<member name="M:Discord.WebSocket.SocketInteraction.DeferAsync(Discord.RequestOptions)"> | |||
<summary> | |||
Acknowledges this interaction. | |||
</summary> | |||
<returns> | |||
A task that represents the asynchronous operation of acknowledging the interaction. | |||
@@ -1906,7 +1906,7 @@ namespace Discord.WebSocket | |||
var interaction = SocketInteraction.Create(this, data, channel as ISocketMessageChannel); | |||
if (this.AlwaysAcknowledgeInteractions) | |||
await interaction.AcknowledgeAsync().ConfigureAwait(false); | |||
await interaction.DeferAsync().ConfigureAwait(false); | |||
await TimedInvokeAsync(_interactionCreatedEvent, nameof(InteractionCreated), interaction).ConfigureAwait(false); | |||
} | |||
@@ -1,11 +1,8 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.Interaction; | |||
using DataModel = Discord.API.MessageComponentInteractionData; | |||
using Newtonsoft.Json.Linq; | |||
using Discord.Rest; | |||
namespace Discord.WebSocket | |||
@@ -50,15 +47,15 @@ namespace Discord.WebSocket | |||
{ | |||
if (this.Message == null) | |||
{ | |||
SocketUser author; | |||
SocketUser author = null; | |||
if (this.Channel is SocketGuildChannel channel) | |||
{ | |||
if (model.Message.Value.WebhookId.IsSpecified) | |||
author = SocketWebhookUser.Create(channel.Guild, Discord.State, model.Message.Value.Author.Value, model.Message.Value.WebhookId.Value); | |||
else | |||
else if (model.Message.Value.Author.IsSpecified) | |||
author = channel.Guild.GetUser(model.Message.Value.Author.Value.Id); | |||
} | |||
else | |||
else if (model.Message.Value.Author.IsSpecified) | |||
author = (this.Channel as SocketChannel).GetUser(model.Message.Value.Author.Value.Id); | |||
this.Message = SocketUserMessage.Create(this.Discord, this.Discord.State, author, this.Channel, model.Message.Value); | |||
@@ -71,18 +68,21 @@ namespace Discord.WebSocket | |||
} | |||
/// <inheritdoc/> | |||
public override async Task RespondAsync(Embed[] embeds = null, string text = null, bool isTTS = false, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | |||
public override async Task RespondAsync( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null) | |||
{ | |||
if (type == InteractionResponseType.Pong) | |||
throw new InvalidOperationException($"Cannot use {Type} on a send message function"); | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
if (Discord.AlwaysAcknowledgeInteractions) | |||
{ | |||
await FollowupAsync(embeds, text, isTTS, ephemeral, type, allowedMentions, options); | |||
await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options); | |||
return; | |||
} | |||
@@ -107,15 +107,14 @@ namespace Discord.WebSocket | |||
} | |||
var response = new API.InteractionResponse() | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = type, | |||
Data = new API.InteractionApplicationCommandCallbackData(text) | |||
Type = InteractionResponseType.ChannelMessageWithSource, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
Content = text ?? Optional<string>.Unspecified, | |||
AllowedMentions = allowedMentions?.ToModel(), | |||
Embeds = embeds != null | |||
? embeds.Select(x => x.ToModel()).ToArray() | |||
: Optional<API.Embed[]>.Unspecified, | |||
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||
TTS = isTTS, | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||
} | |||
@@ -124,17 +123,78 @@ namespace Discord.WebSocket | |||
if (ephemeral) | |||
response.Data.Value.Flags = 64; | |||
await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); | |||
await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options); | |||
} | |||
/// <inheritdoc/> | |||
public override async Task<RestFollowupMessage> FollowupAsync(Embed[] embeds = null, string text = null, bool isTTS = false, bool ephemeral = false, | |||
InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | |||
/// <summary> | |||
/// Updates the message which this component resides in with the type <see cref="InteractionResponseType.UpdateMessage"/> | |||
/// </summary> | |||
/// <param name="func">A delegate containing the properties to modify the message with.</param> | |||
/// <param name="options">The request options for this async request.</param> | |||
/// <returns>A task that represents the asynchronous operation of updating the message.</returns> | |||
public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions options = null) | |||
{ | |||
if (type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.Pong) | |||
throw new InvalidOperationException($"Cannot use {type} on a slash command!"); | |||
var args = new MessageProperties(); | |||
func(args); | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
if (args.AllowedMentions.IsSpecified) | |||
{ | |||
var allowedMentions = args.AllowedMentions.Value; | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 user Ids are allowed."); | |||
} | |||
if (args.Embeds.IsSpecified) | |||
Preconditions.AtMost(args.Embeds.Value?.Length ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed."); | |||
// check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
if (args.AllowedMentions.IsSpecified && args.AllowedMentions.Value != null && args.AllowedMentions.Value.AllowedTypes.HasValue) | |||
{ | |||
var allowedMentions = args.AllowedMentions.Value; | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) | |||
&& allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(args.AllowedMentions)); | |||
} | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) | |||
&& allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(args.AllowedMentions)); | |||
} | |||
} | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.UpdateMessage, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
Content = args.Content, | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified, | |||
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value?.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified, | |||
Components = args.Components.IsSpecified | |||
? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() | |||
: Optional<API.ActionRowComponent[]>.Unspecified, | |||
Flags = args.Flags.IsSpecified ? (int?)args.Flags.Value ?? Optional<int>.Unspecified : Optional<int>.Unspecified | |||
} | |||
}; | |||
await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, this.Token, options); | |||
} | |||
/// <inheritdoc/> | |||
public override async Task<RestFollowupMessage> FollowupAsync( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
@@ -142,13 +202,12 @@ namespace Discord.WebSocket | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
var args = new API.Rest.CreateWebhookMessageParams(text) | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
AllowedMentions = allowedMentions?.ToModel(), | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds != null | |||
? embeds.Select(x => x.ToModel()).ToArray() | |||
: Optional<API.Embed[]>.Unspecified, | |||
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||
}; | |||
@@ -161,10 +220,11 @@ namespace Discord.WebSocket | |||
/// <summary> | |||
/// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredUpdateMessage"/>. | |||
/// </summary> | |||
/// <param name="options">The request options for this async request.</param> | |||
/// <returns> | |||
/// A task that represents the asynchronous operation of acknowledging the interaction. | |||
/// </returns> | |||
public override Task AcknowledgeAsync(RequestOptions options = null) | |||
public override Task DeferAsync(RequestOptions options = null) | |||
{ | |||
var response = new API.InteractionResponse() | |||
{ | |||
@@ -1,5 +1,4 @@ | |||
using Discord.Rest; | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
@@ -16,7 +15,7 @@ namespace Discord.WebSocket | |||
/// <summary> | |||
/// The data associated with this interaction. | |||
/// </summary> | |||
new public SocketSlashCommandData Data { get; private set; } | |||
new public SocketSlashCommandData Data { get; } | |||
internal SocketSlashCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | |||
: base(client, model.Id, channel) | |||
@@ -51,21 +50,21 @@ namespace Discord.WebSocket | |||
} | |||
/// <inheritdoc/> | |||
public override async Task RespondAsync(Embed[] embeds = null, string text = null, bool isTTS = false, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | |||
public override async Task RespondAsync( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null) | |||
{ | |||
if (type == InteractionResponseType.Pong) | |||
throw new InvalidOperationException($"Cannot use {Type} on a send message function"); | |||
if(type == InteractionResponseType.DeferredUpdateMessage || type == InteractionResponseType.UpdateMessage) | |||
throw new InvalidOperationException($"Cannot use {Type} on a slash command!"); | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
if (Discord.AlwaysAcknowledgeInteractions) | |||
{ | |||
await FollowupAsync(embeds, text, isTTS, ephemeral, type, allowedMentions, options); | |||
await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component); | |||
return; | |||
} | |||
@@ -90,16 +89,15 @@ namespace Discord.WebSocket | |||
} | |||
var response = new API.InteractionResponse() | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = type, | |||
Data = new API.InteractionApplicationCommandCallbackData(text) | |||
Type = InteractionResponseType.ChannelMessageWithSource, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
AllowedMentions = allowedMentions?.ToModel(), | |||
Embeds = embeds != null | |||
? embeds.Select(x => x.ToModel()).ToArray() | |||
: Optional<API.Embed[]>.Unspecified, | |||
TTS = isTTS, | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||
TTS = isTTS ? true : Optional<bool>.Unspecified, | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||
} | |||
}; | |||
@@ -107,17 +105,19 @@ namespace Discord.WebSocket | |||
if (ephemeral) | |||
response.Data.Value.Flags = 64; | |||
await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); | |||
await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options); | |||
} | |||
/// <inheritdoc/> | |||
public override async Task<RestFollowupMessage> FollowupAsync(Embed[] embeds = null, string text = null, bool isTTS = false, bool ephemeral = false, | |||
InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | |||
public override async Task<RestFollowupMessage> FollowupAsync( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null) | |||
{ | |||
if (type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.Pong || type == InteractionResponseType.DeferredUpdateMessage || type == InteractionResponseType.UpdateMessage) | |||
throw new InvalidOperationException($"Cannot use {type} on a slash command!"); | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
@@ -125,13 +125,12 @@ namespace Discord.WebSocket | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
var args = new API.Rest.CreateWebhookMessageParams(text) | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
AllowedMentions = allowedMentions?.ToModel(), | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds != null | |||
? embeds.Select(x => x.ToModel()).ToArray() | |||
: Optional<API.Embed[]>.Unspecified, | |||
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||
}; | |||
@@ -141,10 +140,15 @@ namespace Discord.WebSocket | |||
return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options); | |||
} | |||
/// <inheritdoc/> | |||
public override Task AcknowledgeAsync(RequestOptions options = null) | |||
/// <summary> | |||
/// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||
/// </summary> | |||
/// <returns> | |||
/// A task that represents the asynchronous operation of acknowledging the interaction. | |||
/// </returns> | |||
public override Task DeferAsync(RequestOptions options = null) | |||
{ | |||
var response = new API.InteractionResponse() | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.DeferredChannelMessageWithSource, | |||
}; | |||
@@ -96,7 +96,15 @@ namespace Discord.WebSocket | |||
{ | |||
if (model.Value.Value is bool val) | |||
this.Value = val; | |||
else if (bool.TryParse(model.Value.Value.ToString(), out var res)) | |||
else if (bool.TryParse(model.Value.Value.ToString(), out bool res)) | |||
this.Value = res; | |||
} | |||
break; | |||
case ApplicationCommandOptionType.Number: | |||
{ | |||
if (model.Value.Value is int val) | |||
this.Value = val; | |||
else if (double.TryParse(model.Value.Value.ToString(), out double res)) | |||
this.Value = res; | |||
} | |||
break; | |||
@@ -1,8 +1,5 @@ | |||
using Discord.Rest; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.Interaction; | |||
@@ -96,97 +93,76 @@ namespace Discord.WebSocket | |||
} | |||
/// <summary> | |||
/// Responds to an Interaction. | |||
/// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>. | |||
/// <para> | |||
/// If you have <see cref="DiscordSocketConfig.AlwaysAcknowledgeInteractions"/> set to <see langword="true"/>, You should use | |||
/// <see cref="FollowupAsync(Embed[],string, bool, bool, InteractionResponseType, AllowedMentions, RequestOptions, MessageComponent)"/> instead. | |||
/// <see cref="FollowupAsync(Discord.Embed[],string,bool,bool,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"/> instead. | |||
/// </para> | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="embed">A <see cref="Embed"/> to send with this response.</param> | |||
/// <param name="type">The type of response to this Interaction.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param> | |||
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||
/// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||
public Task RespondAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | |||
=> RespondAsync(embed != null ? new Embed[] { embed } : null, text, isTTS, type, ephemeral, allowedMentions, options, component); | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="embed">A <see cref="Embed"/> to send with this response</param> | |||
/// <param name="type">The type of response to this Interaction.</param> | |||
/// /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public Task<RestFollowupMessage> FollowupAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | |||
=> FollowupAsync(embed != null ? new Embed[] { embed } : null, text, isTTS, ephemeral, type, allowedMentions, options, component); | |||
/// <summary> | |||
/// Responds to an Interaction. | |||
/// <para> | |||
/// If you have <see cref="DiscordSocketConfig.AlwaysAcknowledgeInteractions"/> set to <see langword="true"/>, You should use | |||
/// <see cref="FollowupAsync( Embed[],string, bool, bool, InteractionResponseType, AllowedMentions, RequestOptions, MessageComponent)"/> instead. | |||
/// </para> | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10</param> | |||
/// <param name="type">The type of response to this Interaction.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param> | |||
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||
/// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||
public abstract Task RespondAsync(Embed[] embeds = null, string text = null, bool isTTS = false, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
public abstract Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, | |||
bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null); | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10</param> | |||
/// <param name="type">The type of response to this Interaction.</param> | |||
/// /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public abstract Task<RestFollowupMessage> FollowupAsync(Embed[] embeds = null, string text = null, bool isTTS = false, bool ephemeral = false, | |||
InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||
public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null); | |||
/// <summary> | |||
/// Gets the original response for this interaction. | |||
/// </summary> | |||
/// <param name="options">The request options for this async request.</param> | |||
/// <returns>A <see cref="RestInteractionMessage"/> that represents the intitial response, or <see langword="null"/> if there is no response.</returns> | |||
/// <returns>A <see cref="RestInteractionMessage"/> that represents the initial response.</returns> | |||
public Task<RestInteractionMessage> GetOriginalResponseAsync(RequestOptions options = null) | |||
=> InteractionHelper.GetOriginalResponseAsync(this.Discord, this.Channel, this, options); | |||
/// <summary> | |||
/// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||
/// Edits original response for this interaction. | |||
/// </summary> | |||
/// <param name="func">A delegate containing the properties to modify the message with.</param> | |||
/// <param name="options">The request options for this async request.</param> | |||
/// <returns>A <see cref="RestInteractionMessage"/> that represents the initial response.</returns> | |||
public async Task<RestInteractionMessage> ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options = null) | |||
{ | |||
var model = await InteractionHelper.ModifyInteractionResponse(this.Discord, this.Token, func, options); | |||
return RestInteractionMessage.Create(this.Discord, model, this.Token, this.Channel); | |||
} | |||
/// <summary> | |||
/// Acknowledges this interaction. | |||
/// </summary> | |||
/// <returns> | |||
/// A task that represents the asynchronous operation of acknowledging the interaction. | |||
/// </returns> | |||
[Obsolete("This method deprecated, please use DeferAsync instead")] | |||
public Task AcknowledgeAsync(RequestOptions options = null) => DeferAsync(options); | |||
/// <summary> | |||
/// Acknowledges this interaction. | |||
/// </summary> | |||
/// <returns> | |||
/// A task that represents the asynchronous operation of acknowledging the interaction. | |||
/// </returns> | |||
public abstract Task AcknowledgeAsync(RequestOptions options = null); | |||
public abstract Task DeferAsync(RequestOptions options = null); | |||
private bool CheckToken() | |||
{ | |||
@@ -209,7 +209,8 @@ namespace Discord.WebSocket | |||
z.Default.ToNullable())).ToList(), | |||
parsed.Placeholder.GetValueOrDefault(), | |||
parsed.MinValues, | |||
parsed.MaxValues | |||
parsed.MaxValues, | |||
parsed.Disabled | |||
); | |||
} | |||
default: | |||
@@ -252,7 +253,7 @@ namespace Discord.WebSocket | |||
IReadOnlyCollection<ulong> IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray(); | |||
/// <inheritdoc /> | |||
IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | |||
/// <inheritdoc/> | |||
IReadOnlyCollection<IMessageComponent> IMessage.Components => Components; | |||
@@ -20,10 +20,15 @@ namespace Discord.Webhook | |||
throw new InvalidOperationException("Could not find a webhook with the supplied credentials."); | |||
return RestInternalWebhook.Create(client, model); | |||
} | |||
public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client, | |||
public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client, | |||
string text, bool isTTS, IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options) | |||
{ | |||
var args = new CreateWebhookMessageParams(text) { IsTTS = isTTS }; | |||
var args = new CreateWebhookMessageParams | |||
{ | |||
Content = text, | |||
IsTTS = isTTS | |||
}; | |||
if (embeds != null) | |||
args.Embeds = embeds.Select(x => x.ToModel()).ToArray(); | |||
if (username != null) | |||