@@ -8,7 +8,7 @@ | |||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
<PackageId>Discord.Net.Labs.Core</PackageId> | <PackageId>Discord.Net.Labs.Core</PackageId> | ||||
<Version>2.3.3</Version> | |||||
<Version>2.3.4</Version> | |||||
<Product>Discord.Net.Labs.Core</Product> | <Product>Discord.Net.Labs.Core</Product> | ||||
<RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | ||||
<PackageIcon>Temporary.png</PackageIcon> | <PackageIcon>Temporary.png</PackageIcon> | ||||
@@ -7,16 +7,21 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
/// <summary> | |||||
/// Represents a <see cref="IMessageComponent"/> Row for child components to live in. | |||||
/// </summary> | |||||
public class ActionRowComponent : IMessageComponent | public class ActionRowComponent : IMessageComponent | ||||
{ | { | ||||
[JsonProperty("type")] | |||||
/// <inheritdoc/> | |||||
public ComponentType Type { get; } = ComponentType.ActionRow; | public ComponentType Type { get; } = ComponentType.ActionRow; | ||||
[JsonProperty("components")] | |||||
public IReadOnlyCollection<IMessageComponent> Components { get; internal set; } | |||||
/// <summary> | |||||
/// The child components in this row. | |||||
/// </summary> | |||||
public IReadOnlyCollection<ButtonComponent> Components { get; internal set; } | |||||
internal ActionRowComponent() { } | internal ActionRowComponent() { } | ||||
internal ActionRowComponent(IReadOnlyCollection<IMessageComponent> components) | |||||
internal ActionRowComponent(List<ButtonComponent> components) | |||||
{ | { | ||||
this.Components = components; | this.Components = components; | ||||
} | } | ||||
@@ -7,27 +7,45 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
/// <summary> | |||||
/// Represents a <see cref="IMessageComponent"/> Button. | |||||
/// </summary> | |||||
public class ButtonComponent : IMessageComponent | public class ButtonComponent : IMessageComponent | ||||
{ | { | ||||
[JsonProperty("type")] | |||||
/// <inheritdoc/> | |||||
public ComponentType Type { get; } = ComponentType.Button; | public ComponentType Type { get; } = ComponentType.Button; | ||||
[JsonProperty("style")] | |||||
/// <summary> | |||||
/// The <see cref="ButtonStyle"/> of this button, example buttons with each style can be found <see href="https://discord.com/assets/7bb017ce52cfd6575e21c058feb3883b.png">Here</see>. | |||||
/// </summary> | |||||
public ButtonStyle Style { get; } | public ButtonStyle Style { get; } | ||||
[JsonProperty("label")] | |||||
/// <summary> | |||||
/// The label of the button, this is the text that is shown. | |||||
/// </summary> | |||||
public string Label { get; } | public string Label { get; } | ||||
[JsonProperty("emoji")] | |||||
/// <summary> | |||||
/// A <see cref="IEmote"/> that will be displayed with this button. | |||||
/// </summary> | |||||
public IEmote Emote { get; } | public IEmote Emote { get; } | ||||
[JsonProperty("custom_id")] | |||||
/// <summary> | |||||
/// A unique id that will be sent with a <see cref="IDiscordInteraction"/>. This is how you know what button was pressed. | |||||
/// </summary> | |||||
public string CustomId { get; } | public string CustomId { get; } | ||||
[JsonProperty("url")] | |||||
/// <summary> | |||||
/// A URL for a <see cref="ButtonStyle.Link"/> button. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// You cannot have a button with a <b>URL</b> and a <b>CustomId</b>. | |||||
/// </remarks> | |||||
public string Url { get; } | public string Url { get; } | ||||
[JsonProperty("disabled")] | |||||
/// <summary> | |||||
/// Whether this button is disabled or not. | |||||
/// </summary> | |||||
public bool Disabled { get; } | public bool Disabled { get; } | ||||
internal ButtonComponent(ButtonStyle style, string label, IEmote emote, string customId, string url, bool disabled) | internal ButtonComponent(ButtonStyle style, string label, IEmote emote, string customId, string url, bool disabled) | ||||
@@ -39,5 +57,7 @@ namespace Discord | |||||
this.Url = url; | this.Url = url; | ||||
this.Disabled = disabled; | this.Disabled = disabled; | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -6,10 +6,19 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
/// <summary> | |||||
/// Represents a builder for creating a <see cref="MessageComponent"/>. | |||||
/// </summary> | |||||
public class ComponentBuilder | public class ComponentBuilder | ||||
{ | { | ||||
/// <summary> | |||||
/// The max amount of rows a message can have. | |||||
/// </summary> | |||||
public const int MaxActionRowCount = 5; | public const int MaxActionRowCount = 5; | ||||
/// <summary> | |||||
/// Gets or sets the Action Rows for this Component Builder. | |||||
/// </summary> | |||||
public List<ActionRowBuilder> ActionRows | public List<ActionRowBuilder> ActionRows | ||||
{ | { | ||||
get => _actionRows; | get => _actionRows; | ||||
@@ -25,11 +34,22 @@ namespace Discord | |||||
private List<ActionRowBuilder> _actionRows { get; set; } | private List<ActionRowBuilder> _actionRows { get; set; } | ||||
/// <summary> | |||||
/// Adds a button to the specified row. | |||||
/// </summary> | |||||
/// <param name="label">The label text for the newly added button.</param> | |||||
/// <param name="style">The style of this newly added button.</param> | |||||
/// <param name="emote">A <see cref="IEmote"/> to be used with this button.</param> | |||||
/// <param name="customId">The custom id of the newly added button.</param> | |||||
/// <param name="url">A URL to be used only if the <see cref="ButtonStyle"/> is a Link.</param> | |||||
/// <param name="disabled">Whether or not the newly created button is disabled.</param> | |||||
/// <param name="row">The row the button should be placed on.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ComponentBuilder WithButton( | public ComponentBuilder WithButton( | ||||
string label, | string label, | ||||
string customId, | |||||
ButtonStyle style = ButtonStyle.Primary, | ButtonStyle style = ButtonStyle.Primary, | ||||
IEmote emote = null, | IEmote emote = null, | ||||
string customId = null, | |||||
string url = null, | string url = null, | ||||
bool disabled = false, | bool disabled = false, | ||||
int row = 0) | int row = 0) | ||||
@@ -45,9 +65,20 @@ namespace Discord | |||||
return this.WithButton(button, row); | return this.WithButton(button, row); | ||||
} | } | ||||
/// <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) | public ComponentBuilder WithButton(ButtonBuilder button) | ||||
=> this.WithButton(button, 0); | => this.WithButton(button, 0); | ||||
/// <summary> | |||||
/// Adds a button to the specified row. | |||||
/// </summary> | |||||
/// <param name="button">The button to add.</param> | |||||
/// <param name="row">The row to add the button.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ComponentBuilder WithButton(ButtonBuilder button, int row) | public ComponentBuilder WithButton(ButtonBuilder button, int row) | ||||
{ | { | ||||
var builtButton = button.Build(); | var builtButton = button.Build(); | ||||
@@ -75,6 +106,10 @@ namespace Discord | |||||
return this; | return this; | ||||
} | } | ||||
/// <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> | |||||
public MessageComponent Build() | public MessageComponent Build() | ||||
{ | { | ||||
if (this._actionRows != null) | if (this._actionRows != null) | ||||
@@ -84,10 +119,20 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// Represents a class used to build Action rows. | |||||
/// </summary> | |||||
public class ActionRowBuilder | public class ActionRowBuilder | ||||
{ | { | ||||
/// <summary> | |||||
/// The max amount of child components this row can hold. | |||||
/// </summary> | |||||
public const int MaxChildCount = 5; | public const int MaxChildCount = 5; | ||||
public List<IMessageComponent> Components | |||||
/// <summary> | |||||
/// Gets or sets the components inside this row. | |||||
/// </summary> | |||||
public List<ButtonComponent> Components | |||||
{ | { | ||||
get => _components; | get => _components; | ||||
set | set | ||||
@@ -99,33 +144,70 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
private List<IMessageComponent> _components { get; set; } | |||||
private List<ButtonComponent> _components { get; set; } | |||||
public ActionRowBuilder WithComponents(List<IMessageComponent> components) | |||||
/// <summary> | |||||
/// Adds a list of components to the current row. | |||||
/// </summary> | |||||
/// <param name="components">The list of components to add.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ActionRowBuilder WithComponents(List<ButtonComponent> components) | |||||
{ | { | ||||
this.Components = components; | this.Components = components; | ||||
return this; | return this; | ||||
} | } | ||||
public ActionRowBuilder WithComponent(IMessageComponent component) | |||||
/// <summary> | |||||
/// Adds a component at the end of the current row. | |||||
/// </summary> | |||||
/// <param name="component">The component to add.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ActionRowBuilder WithComponent(ButtonComponent component) | |||||
{ | { | ||||
if (this.Components == null) | if (this.Components == null) | ||||
this.Components = new List<IMessageComponent>(); | |||||
this.Components = new List<ButtonComponent>(); | |||||
this.Components.Add(component); | this.Components.Add(component); | ||||
return this; | return this; | ||||
} | } | ||||
/// <summary> | |||||
/// 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() | public ActionRowComponent Build() | ||||
=> new ActionRowComponent(this._components); | |||||
{ | |||||
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); | |||||
} | |||||
} | } | ||||
/// <summary> | |||||
/// Represents a class used to build <see cref="ButtonComponent"/>'s. | |||||
/// </summary> | |||||
public class ButtonBuilder | public class ButtonBuilder | ||||
{ | { | ||||
/// <summary> | |||||
/// The max length of a <see cref="ButtonComponent.Label"/>. | |||||
/// </summary> | |||||
public const int MaxLabelLength = 80; | public const int MaxLabelLength = 80; | ||||
/// <summary> | |||||
/// The max length of a <see cref="ButtonComponent.CustomId"/>. | |||||
/// </summary> | |||||
public const int MaxCustomIdLength = 100; | public const int MaxCustomIdLength = 100; | ||||
/// <summary> | |||||
/// Gets or sets the label of the current button. | |||||
/// </summary> | |||||
public string Label | public string Label | ||||
{ | { | ||||
get => _label; | get => _label; | ||||
@@ -139,6 +221,9 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// Gets or sets the custom id of the current button. | |||||
/// </summary> | |||||
public string CustomId | public string CustomId | ||||
{ | { | ||||
get => _customId; | get => _customId; | ||||
@@ -151,15 +236,36 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// Gets or sets the <see cref="ButtonStyle"/> of the current button. | |||||
/// </summary> | |||||
public ButtonStyle Style { get; set; } | public ButtonStyle Style { get; set; } | ||||
/// <summary> | |||||
/// Gets or sets the <see cref="IEmote"/> of the current button. | |||||
/// </summary> | |||||
public IEmote Emote { get; set; } | public IEmote Emote { get; set; } | ||||
/// <summary> | |||||
/// Gets or sets the url of the current button. | |||||
/// </summary> | |||||
public string Url { get; set; } | public string Url { get; set; } | ||||
/// <summary> | |||||
/// Gets or sets whether the current button is disabled. | |||||
/// </summary> | |||||
public bool Disabled { get; set; } | public bool Disabled { get; set; } | ||||
private string _label; | private string _label; | ||||
private string _customId; | private string _customId; | ||||
/// <summary> | |||||
/// Creates a button with the <see cref="ButtonStyle.Link"/> style. | |||||
/// </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) | public static ButtonBuilder CreateLinkButton(string label, string url) | ||||
{ | { | ||||
var builder = new ButtonBuilder() | var builder = new ButtonBuilder() | ||||
@@ -170,6 +276,12 @@ namespace Discord | |||||
return builder; | return builder; | ||||
} | } | ||||
/// <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> | |||||
/// <returns>A builder with the newly created button.</returns> | |||||
public static ButtonBuilder CreateDangerButton(string label, string customId) | public static ButtonBuilder CreateDangerButton(string label, string customId) | ||||
{ | { | ||||
var builder = new ButtonBuilder() | var builder = new ButtonBuilder() | ||||
@@ -180,6 +292,12 @@ namespace Discord | |||||
return builder; | return builder; | ||||
} | } | ||||
/// <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> | |||||
/// <returns>A builder with the newly created button.</returns> | |||||
public static ButtonBuilder CreatePrimaryButton(string label, string customId) | public static ButtonBuilder CreatePrimaryButton(string label, string customId) | ||||
{ | { | ||||
var builder = new ButtonBuilder() | var builder = new ButtonBuilder() | ||||
@@ -190,6 +308,12 @@ namespace Discord | |||||
return builder; | return builder; | ||||
} | } | ||||
/// <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> | |||||
/// <returns>A builder with the newly created button.</returns> | |||||
public static ButtonBuilder CreateSecondaryButton(string label, string customId) | public static ButtonBuilder CreateSecondaryButton(string label, string customId) | ||||
{ | { | ||||
var builder = new ButtonBuilder() | var builder = new ButtonBuilder() | ||||
@@ -200,6 +324,12 @@ namespace Discord | |||||
return builder; | return builder; | ||||
} | } | ||||
/// <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> | |||||
/// <returns>A builder with the newly created button.</returns> | |||||
public static ButtonBuilder CreateSuccessButton(string label, string customId) | public static ButtonBuilder CreateSuccessButton(string label, string customId) | ||||
{ | { | ||||
var builder = new ButtonBuilder() | var builder = new ButtonBuilder() | ||||
@@ -210,41 +340,78 @@ namespace Discord | |||||
return builder; | return builder; | ||||
} | } | ||||
/// <summary> | |||||
/// Sets the current buttons label to the specified text. | |||||
/// </summary> | |||||
/// <param name="label">The text for the label</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ButtonBuilder WithLabel(string label) | public ButtonBuilder WithLabel(string label) | ||||
{ | { | ||||
this.Label = label; | this.Label = label; | ||||
return this; | return this; | ||||
} | } | ||||
/// <summary> | |||||
/// Sets the current buttons style. | |||||
/// </summary> | |||||
/// <param name="style">The style for this builders button.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ButtonBuilder WithStyle(ButtonStyle style) | public ButtonBuilder WithStyle(ButtonStyle style) | ||||
{ | { | ||||
this.Style = style; | this.Style = style; | ||||
return this; | return this; | ||||
} | } | ||||
/// <summary> | |||||
/// Sets the current buttons emote. | |||||
/// </summary> | |||||
/// <param name="emote">The emote to use for the current button.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ButtonBuilder WithEmote(IEmote emote) | public ButtonBuilder WithEmote(IEmote emote) | ||||
{ | { | ||||
this.Emote = emote; | this.Emote = emote; | ||||
return this; | return this; | ||||
} | } | ||||
/// <summary> | |||||
/// Sets the current buttons url. | |||||
/// </summary> | |||||
/// <param name="url">The url to use for the current button.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ButtonBuilder WithUrl(string url) | public ButtonBuilder WithUrl(string url) | ||||
{ | { | ||||
this.Url = url; | this.Url = url; | ||||
return this; | return this; | ||||
} | } | ||||
/// <summary> | |||||
/// Sets the custom id of the current button. | |||||
/// </summary> | |||||
/// <param name="id">The id to use for the current button.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ButtonBuilder WithCustomId(string id) | public ButtonBuilder WithCustomId(string id) | ||||
{ | { | ||||
this.CustomId = id; | this.CustomId = id; | ||||
return this; | return this; | ||||
} | } | ||||
/// <summary> | |||||
/// Sets whether the current button is disabled. | |||||
/// </summary> | |||||
/// <param name="disabled">Whether the current button is disabled or not.</param> | |||||
/// <returns>The current builder.</returns> | |||||
public ButtonBuilder WithDisabled(bool disabled) | public ButtonBuilder WithDisabled(bool disabled) | ||||
{ | { | ||||
this.Disabled = disabled; | this.Disabled = disabled; | ||||
return this; | return this; | ||||
} | } | ||||
/// <summary> | |||||
/// 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> | |||||
public ButtonComponent Build() | public ButtonComponent Build() | ||||
{ | { | ||||
if (string.IsNullOrEmpty(this.Label) && this.Emote == null) | if (string.IsNullOrEmpty(this.Label) && this.Emote == null) | ||||
@@ -255,7 +422,8 @@ namespace Discord | |||||
if (this.Style == ButtonStyle.Link && !string.IsNullOrEmpty(this.CustomId)) | if (this.Style == ButtonStyle.Link && !string.IsNullOrEmpty(this.CustomId)) | ||||
this.CustomId = null; | this.CustomId = null; | ||||
else if (!string.IsNullOrEmpty(this.Url)) | |||||
else if (this.Style != ButtonStyle.Link && !string.IsNullOrEmpty(this.Url)) // Thanks 𝑴𝒓𝑪𝒂𝒌𝒆𝑺𝒍𝒂𝒚𝒆𝒓 :D | |||||
this.Url = null; | this.Url = null; | ||||
return new ButtonComponent(this.Style, this.Label, this.Emote, this.CustomId, this.Url, this.Disabled); | return new ButtonComponent(this.Style, this.Label, this.Emote, this.CustomId, this.Url, this.Disabled); | ||||
@@ -6,19 +6,25 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
/// <summary> | |||||
/// Represents a component object used to send components with messages. | |||||
/// </summary> | |||||
public class MessageComponent | public class MessageComponent | ||||
{ | { | ||||
public IReadOnlyCollection<IMessageComponent> Components { get; } | |||||
/// <summary> | |||||
/// The components to be used in a message. | |||||
/// </summary> | |||||
public IReadOnlyCollection<ActionRowComponent> Components { get; } | |||||
internal MessageComponent(List<ActionRowComponent> components) | internal MessageComponent(List<ActionRowComponent> components) | ||||
{ | { | ||||
this.Components = components; | this.Components = components; | ||||
} | } | ||||
/// <summary> | |||||
/// Returns a empty <see cref="MessageComponent"/>. | |||||
/// </summary> | |||||
internal static MessageComponent Empty | internal static MessageComponent Empty | ||||
=> new MessageComponent(new List<ActionRowComponent>()); | => new MessageComponent(new List<ActionRowComponent>()); | ||||
internal IMessageComponent[] ToModel() | |||||
=> this.Components.ToArray(); | |||||
} | } | ||||
} | } |
@@ -21,5 +21,10 @@ namespace Discord | |||||
/// Gets or sets the embed the message should display. | /// Gets or sets the embed the message should display. | ||||
/// </summary> | /// </summary> | ||||
public Optional<Embed> Embed { get; set; } | public Optional<Embed> Embed { get; set; } | ||||
/// <summary> | |||||
/// Gets or sets the components for this message. | |||||
/// </summary> | |||||
public Optional<MessageComponent> Components { get; set; } | |||||
} | } | ||||
} | } |
@@ -0,0 +1,25 @@ | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.API | |||||
{ | |||||
internal class ActionRowComponent | |||||
{ | |||||
[JsonProperty("type")] | |||||
public ComponentType Type { get; set; } | |||||
[JsonProperty("components")] | |||||
public List<ButtonComponent> Components { get; set; } | |||||
internal ActionRowComponent() { } | |||||
internal ActionRowComponent(Discord.ActionRowComponent c) | |||||
{ | |||||
this.Type = c.Type; | |||||
this.Components = c.Components?.Select(x => new ButtonComponent(x)).ToList(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,66 @@ | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.API | |||||
{ | |||||
internal class ButtonComponent | |||||
{ | |||||
[JsonProperty("type")] | |||||
public ComponentType Type { get; set; } | |||||
[JsonProperty("style")] | |||||
public ButtonStyle Style { get; set; } | |||||
[JsonProperty("label")] | |||||
public Optional<string> Label { get; set; } | |||||
[JsonProperty("emoji")] | |||||
public Optional<Emoji> Emote { get; set; } | |||||
[JsonProperty("custom_id")] | |||||
public Optional<string> CustomId { get; set; } | |||||
[JsonProperty("url")] | |||||
public Optional<string> Url { get; set; } | |||||
[JsonProperty("disabled")] | |||||
public Optional<bool> Disabled { get; set; } | |||||
public ButtonComponent() { } | |||||
public ButtonComponent(Discord.ButtonComponent c) | |||||
{ | |||||
this.Type = c.Type; | |||||
this.Style = c.Style; | |||||
this.Label = c.Label; | |||||
this.CustomId = c.CustomId; | |||||
this.Url = c.Url; | |||||
this.Disabled = c.Disabled; | |||||
if (c.Emote != null) | |||||
{ | |||||
if (c.Emote is Emote e) | |||||
{ | |||||
this.Emote = new Emoji() | |||||
{ | |||||
Name = e.Name, | |||||
Animated = e.Animated, | |||||
Id = e.Id, | |||||
}; | |||||
} | |||||
else | |||||
{ | |||||
this.Emote = new Emoji() | |||||
{ | |||||
Name = c.Emote.Name | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -26,7 +26,7 @@ namespace Discord.API | |||||
public Optional<int> Flags { get; set; } | public Optional<int> Flags { get; set; } | ||||
[JsonProperty("components")] | [JsonProperty("components")] | ||||
public Optional<IMessageComponent[]> Components { get; set; } | |||||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
public InteractionApplicationCommandCallbackData() { } | public InteractionApplicationCommandCallbackData() { } | ||||
public InteractionApplicationCommandCallbackData(string text) | public InteractionApplicationCommandCallbackData(string text) | ||||
@@ -59,6 +59,6 @@ namespace Discord.API | |||||
[JsonProperty("referenced_message")] | [JsonProperty("referenced_message")] | ||||
public Optional<Message> ReferencedMessage { get; set; } | public Optional<Message> ReferencedMessage { get; set; } | ||||
[JsonProperty("components")] | [JsonProperty("components")] | ||||
public Optional<IMessageComponent[]> Components { get; set; } | |||||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
} | } | ||||
} | } |
@@ -25,7 +25,7 @@ namespace Discord.API.Rest | |||||
public Optional<MessageReference> MessageReference { get; set; } | public Optional<MessageReference> MessageReference { get; set; } | ||||
[JsonProperty("components")] | [JsonProperty("components")] | ||||
public Optional<IMessageComponent[]> Components { get; set; } | |||||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
public CreateMessageParams(string content) | public CreateMessageParams(string content) | ||||
{ | { | ||||
Content = content; | Content = content; | ||||
@@ -31,7 +31,7 @@ namespace Discord.API.Rest | |||||
public Optional<int> Flags { get; set; } | public Optional<int> Flags { get; set; } | ||||
[JsonProperty("components")] | [JsonProperty("components")] | ||||
public Optional<IMessageComponent[]> Components { get; set; } | |||||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
public CreateWebhookMessageParams(string content) | public CreateWebhookMessageParams(string content) | ||||
{ | { | ||||
@@ -1,4 +1,4 @@ | |||||
#pragma warning disable CS1591 | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
@@ -10,5 +10,7 @@ namespace Discord.API.Rest | |||||
public Optional<string> Content { get; set; } | public Optional<string> Content { get; set; } | ||||
[JsonProperty("embed")] | [JsonProperty("embed")] | ||||
public Optional<Embed> Embed { get; set; } | public Optional<Embed> Embed { get; set; } | ||||
[JsonProperty("components")] | |||||
public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
} | } | ||||
} | } |
@@ -21,7 +21,7 @@ namespace Discord.API.Rest | |||||
public Optional<Embed> Embed { get; set; } | public Optional<Embed> Embed { get; set; } | ||||
public Optional<AllowedMentions> AllowedMentions { get; set; } | public Optional<AllowedMentions> AllowedMentions { get; set; } | ||||
public Optional<MessageReference> MessageReference { get; set; } | public Optional<MessageReference> MessageReference { get; set; } | ||||
public Optional<IMessageComponent[]> MessageComponent { get; set; } | |||||
public Optional<ActionRowComponent[]> MessageComponent { get; set; } | |||||
public bool IsSpoiler { get; set; } = false; | public bool IsSpoiler { get; set; } = false; | ||||
public UploadFileParams(Stream file) | public UploadFileParams(Stream file) | ||||
@@ -9,9 +9,11 @@ | |||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
<PackageIcon>Temporary.png</PackageIcon> | <PackageIcon>Temporary.png</PackageIcon> | ||||
<PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | <PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | ||||
<Version>2.3.2</Version> | |||||
<Version>2.3.5</Version> | |||||
<PackageId>Discord.Net.Labs.Rest</PackageId> | <PackageId>Discord.Net.Labs.Rest</PackageId> | ||||
<RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | ||||
<AssemblyVersion>2.3.4</AssemblyVersion> | |||||
<FileVersion>2.3.4</FileVersion> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | ||||
@@ -944,7 +944,7 @@ namespace Discord.API | |||||
public async Task CreateInteractionResponse(InteractionResponse response, ulong interactionId, string interactionToken, RequestOptions options = null) | public async Task CreateInteractionResponse(InteractionResponse response, ulong interactionId, string interactionToken, RequestOptions options = null) | ||||
{ | { | ||||
if(response.Data.IsSpecified && response.Data.Value.Content.IsSpecified) | if(response.Data.IsSpecified && response.Data.Value.Content.IsSpecified) | ||||
Preconditions.AtMost(response.Data.Value.Content.Value.Length, 2000, nameof(response.Data.Value.Content)); | |||||
Preconditions.AtMost(response.Data.Value.Content.Value?.Length ?? 0, 2000, nameof(response.Data.Value.Content)); | |||||
options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
@@ -221,7 +221,7 @@ namespace Discord.Rest | |||||
} | } | ||||
} | } | ||||
var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.ToModel(), AllowedMentions = allowedMentions?.ToModel(), MessageReference = messageReference?.ToModel(), Components = component?.ToModel() }; | |||||
var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.ToModel(), AllowedMentions = allowedMentions?.ToModel(), MessageReference = messageReference?.ToModel(), Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified }; | |||||
var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
return RestUserMessage.Create(client, channel, client.CurrentUser, model); | return RestUserMessage.Create(client, channel, client.CurrentUser, model); | ||||
} | } | ||||
@@ -281,7 +281,7 @@ namespace Discord.Rest | |||||
} | } | ||||
} | } | ||||
var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed?.ToModel() ?? Optional<API.Embed>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageReference = messageReference?.ToModel() ?? Optional<API.MessageReference>.Unspecified, MessageComponent = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified, IsSpoiler = isSpoiler }; | |||||
var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed?.ToModel() ?? Optional<API.Embed>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageReference = messageReference?.ToModel() ?? Optional<API.MessageReference>.Unspecified, MessageComponent = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, IsSpoiler = isSpoiler }; | |||||
var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
return RestUserMessage.Create(client, channel, client.CurrentUser, model); | return RestUserMessage.Create(client, channel, client.CurrentUser, model); | ||||
} | } | ||||
@@ -41,7 +41,8 @@ namespace Discord.Rest | |||||
var apiArgs = new API.Rest.ModifyMessageParams | var apiArgs = new API.Rest.ModifyMessageParams | ||||
{ | { | ||||
Content = args.Content, | Content = args.Content, | ||||
Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create<API.Embed>() | |||||
Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create<API.Embed>(), | |||||
Components = args?.Components.GetValueOrDefault()?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
}; | }; | ||||
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -129,9 +129,16 @@ namespace Discord.Rest | |||||
if (model.Components.IsSpecified) | if (model.Components.IsSpecified) | ||||
{ | { | ||||
Components = model.Components.Value.Select(x => | |||||
(x as Newtonsoft.Json.Linq.JToken).ToObject<ActionRowComponent>() | |||||
).ToList(); | |||||
Components = model.Components.Value.Select(x => new ActionRowComponent(x.Components.Select(x => | |||||
new ButtonComponent( | |||||
x.Style, | |||||
x.Label.GetValueOrDefault(), | |||||
x.Emote.IsSpecified ? x.Emote.Value.Id.HasValue ? new Emote(x.Emote.Value.Id.Value, x.Emote.Value.Name, x.Emote.Value.Animated.GetValueOrDefault()) : new Emoji(x.Emote.Value.Name) : null, | |||||
x.CustomId.GetValueOrDefault(), | |||||
x.Url.GetValueOrDefault(), | |||||
x.Disabled.GetValueOrDefault()) | |||||
).ToList() | |||||
)).ToList(); | |||||
} | } | ||||
else | else | ||||
Components = new List<ActionRowComponent>(); | Components = new List<ActionRowComponent>(); | ||||
@@ -13,6 +13,9 @@ namespace Discord.API.Gateway | |||||
[JsonProperty("id")] | [JsonProperty("id")] | ||||
public ulong Id { get; set; } | public ulong Id { get; set; } | ||||
[JsonProperty("application_id")] | |||||
public ulong ApplicationId { get; set; } | |||||
[JsonProperty("type")] | [JsonProperty("type")] | ||||
public InteractionType Type { get; set; } | public InteractionType Type { get; set; } | ||||
@@ -20,18 +23,25 @@ namespace Discord.API.Gateway | |||||
public Optional<object> Data { get; set; } | public Optional<object> Data { get; set; } | ||||
[JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
public ulong GuildId { get; set; } | |||||
public Optional<ulong> GuildId { get; set; } | |||||
[JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
public ulong ChannelId { get; set; } | |||||
public Optional<ulong> ChannelId { get; set; } | |||||
[JsonProperty("member")] | [JsonProperty("member")] | ||||
public GuildMember Member { get; set; } | |||||
public Optional<GuildMember> Member { get; set; } | |||||
[JsonProperty("user")] | |||||
public Optional<User> User { get; set; } | |||||
[JsonProperty("token")] | [JsonProperty("token")] | ||||
public string Token { get; set; } | public string Token { get; set; } | ||||
[JsonProperty("version")] | [JsonProperty("version")] | ||||
public int Version { get; set; } | public int Version { get; set; } | ||||
[JsonProperty("message")] | |||||
public Optional<Message> Message { get; set; } | |||||
} | } | ||||
} | } |
@@ -8,7 +8,7 @@ | |||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
<Version>2.3.2</Version> | |||||
<Version>2.3.4</Version> | |||||
<RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | ||||
<PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | <PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | ||||
<PackageIcon>Temporary.png</PackageIcon> | <PackageIcon>Temporary.png</PackageIcon> | ||||
@@ -1785,26 +1785,33 @@ namespace Discord.WebSocket | |||||
await _gatewayLogger.DebugAsync("Received Dispatch (INTERACTION_CREATE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (INTERACTION_CREATE)").ConfigureAwait(false); | ||||
var data = (payload as JToken).ToObject<API.Gateway.InteractionCreated>(_serializer); | var data = (payload as JToken).ToObject<API.Gateway.InteractionCreated>(_serializer); | ||||
if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel) | |||||
if (data.Member.IsSpecified && data.ChannelId.IsSpecified) | |||||
{ | { | ||||
var guild = channel.Guild; | |||||
if (!guild.IsSynced) | |||||
if (State.GetChannel(data.ChannelId.Value) is SocketGuildChannel channel) | |||||
{ | { | ||||
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | |||||
return; | |||||
} | |||||
var guild = channel.Guild; | |||||
if (!guild.IsSynced) | |||||
{ | |||||
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | |||||
return; | |||||
} | |||||
var interaction = SocketInteraction.Create(this, data); | |||||
var interaction = SocketInteraction.Create(this, data); | |||||
if (this.AlwaysAcknowledgeInteractions) | |||||
await interaction.AcknowledgeAsync().ConfigureAwait(false); | |||||
if (this.AlwaysAcknowledgeInteractions) | |||||
await interaction.AcknowledgeAsync().ConfigureAwait(false); | |||||
await TimedInvokeAsync(_interactionCreatedEvent, nameof(InteractionCreated), interaction).ConfigureAwait(false); | |||||
await TimedInvokeAsync(_interactionCreatedEvent, nameof(InteractionCreated), interaction).ConfigureAwait(false); | |||||
} | |||||
else | |||||
{ | |||||
await UnknownChannelAsync(type, data.ChannelId.Value).ConfigureAwait(false); | |||||
return; | |||||
} | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); | |||||
return; | |||||
// DM TODO | |||||
} | } | ||||
} | } | ||||
break; | break; | ||||
@@ -10,10 +10,21 @@ using Discord.Rest; | |||||
namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
{ | { | ||||
/// <summary> | |||||
/// Represents a Websocket-based interaction type for Message Components. | |||||
/// </summary> | |||||
public class SocketMessageComponent : SocketInteraction | public class SocketMessageComponent : SocketInteraction | ||||
{ | { | ||||
/// <summary> | |||||
/// The data received with this interaction, contains the button that was clicked. | |||||
/// </summary> | |||||
new public SocketMessageComponentData Data { get; } | new public SocketMessageComponentData Data { get; } | ||||
/// <summary> | |||||
/// The message that contained the trigger for this interaction. | |||||
/// </summary> | |||||
public SocketMessage Message { get; private set; } | |||||
internal SocketMessageComponent(DiscordSocketClient client, Model model) | internal SocketMessageComponent(DiscordSocketClient client, Model model) | ||||
: base(client, model.Id) | : base(client, model.Id) | ||||
{ | { | ||||
@@ -22,6 +33,8 @@ namespace Discord.WebSocket | |||||
: null; | : null; | ||||
this.Data = new SocketMessageComponentData(dataModel); | this.Data = new SocketMessageComponentData(dataModel); | ||||
} | } | ||||
new internal static SocketMessageComponent Create(DiscordSocketClient client, Model model) | new internal static SocketMessageComponent Create(DiscordSocketClient client, Model model) | ||||
@@ -31,6 +44,23 @@ namespace Discord.WebSocket | |||||
return entity; | return entity; | ||||
} | } | ||||
internal override void Update(Model model) | |||||
{ | |||||
base.Update(model); | |||||
if (model.Message.IsSpecified) | |||||
{ | |||||
if (this.Message == null) | |||||
{ | |||||
this.Message = SocketMessage.Create(this.Discord, this.Discord.State, this.User, this.Channel, model.Message.Value); | |||||
} | |||||
else | |||||
{ | |||||
this.Message.Update(this.Discord.State, model.Message.Value); | |||||
} | |||||
} | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// Responds to an Interaction. | /// Responds to an Interaction. | ||||
/// <para> | /// <para> | ||||
@@ -51,7 +81,6 @@ namespace Discord.WebSocket | |||||
/// </returns> | /// </returns> | ||||
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <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> | /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | ||||
public override async Task<RestUserMessage> RespondAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | public override async Task<RestUserMessage> 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) | bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | ||||
{ | { | ||||
@@ -94,7 +123,7 @@ namespace Discord.WebSocket | |||||
? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
: Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
TTS = isTTS, | TTS = isTTS, | ||||
Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
} | } | ||||
}; | }; | ||||
@@ -134,7 +163,7 @@ namespace Discord.WebSocket | |||||
Embeds = embed != null | Embeds = embed != null | ||||
? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
: Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
}; | }; | ||||
if (ephemeral) | if (ephemeral) | ||||
@@ -7,6 +7,9 @@ using Model = Discord.API.MessageComponentInteractionData; | |||||
namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
{ | { | ||||
/// <summary> | |||||
/// Represents the data sent with a <see cref="InteractionType.MessageComponent"/>. | |||||
/// </summary> | |||||
public class SocketMessageComponentData | public class SocketMessageComponentData | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
@@ -21,7 +21,7 @@ namespace Discord.WebSocket | |||||
(model.Data.Value as JToken).ToObject<DataModel>() | (model.Data.Value as JToken).ToObject<DataModel>() | ||||
: null; | : null; | ||||
Data = SocketSlashCommandData.Create(client, dataModel, model.GuildId); | |||||
Data = SocketSlashCommandData.Create(client, dataModel, model.Id); | |||||
} | } | ||||
new internal static SocketInteraction Create(DiscordSocketClient client, Model model) | new internal static SocketInteraction Create(DiscordSocketClient client, Model model) | ||||
@@ -37,7 +37,7 @@ namespace Discord.WebSocket | |||||
(model.Data.Value as JToken).ToObject<DataModel>() | (model.Data.Value as JToken).ToObject<DataModel>() | ||||
: null; | : null; | ||||
this.Data.Update(data, model.GuildId); | |||||
this.Data.Update(data); | |||||
base.Update(model); | base.Update(model); | ||||
} | } | ||||
@@ -107,7 +107,7 @@ namespace Discord.WebSocket | |||||
? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
: Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
TTS = isTTS, | TTS = isTTS, | ||||
Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
} | } | ||||
}; | }; | ||||
@@ -146,7 +146,7 @@ namespace Discord.WebSocket | |||||
Embeds = embed != null | Embeds = embed != null | ||||
? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
: Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
}; | }; | ||||
if (ephemeral) | if (ephemeral) | ||||
@@ -15,27 +15,24 @@ namespace Discord.WebSocket | |||||
/// </summary> | /// </summary> | ||||
public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | ||||
private ulong guildId; | |||||
internal SocketSlashCommandData(DiscordSocketClient client, ulong id) | internal SocketSlashCommandData(DiscordSocketClient client, ulong id) | ||||
: base(client, id) | : base(client, id) | ||||
{ | { | ||||
} | } | ||||
internal static SocketSlashCommandData Create(DiscordSocketClient client, Model model, ulong guildId) | |||||
internal static SocketSlashCommandData Create(DiscordSocketClient client, Model model, ulong id) | |||||
{ | { | ||||
var entity = new SocketSlashCommandData(client, model.Id); | var entity = new SocketSlashCommandData(client, model.Id); | ||||
entity.Update(model, guildId); | |||||
entity.Update(model); | |||||
return entity; | return entity; | ||||
} | } | ||||
internal void Update(Model model, ulong guildId) | |||||
internal void Update(Model model) | |||||
{ | { | ||||
this.Name = model.Name; | this.Name = model.Name; | ||||
this.guildId = guildId; | |||||
this.Options = model.Options.Any() | this.Options = model.Options.Any() | ||||
? model.Options.Select(x => new SocketSlashCommandDataOption(x, this.Discord, guildId)).ToImmutableArray() | |||||
? model.Options.Select(x => new SocketSlashCommandDataOption(x, this.Discord)).ToImmutableArray() | |||||
: null; | : null; | ||||
} | } | ||||
@@ -22,18 +22,16 @@ namespace Discord.WebSocket | |||||
public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | ||||
private DiscordSocketClient discord; | private DiscordSocketClient discord; | ||||
private ulong guild; | |||||
internal SocketSlashCommandDataOption() { } | internal SocketSlashCommandDataOption() { } | ||||
internal SocketSlashCommandDataOption(Model model, DiscordSocketClient discord, ulong guild) | |||||
internal SocketSlashCommandDataOption(Model model, DiscordSocketClient discord) | |||||
{ | { | ||||
this.Name = model.Name; | this.Name = model.Name; | ||||
this.Value = model.Value.IsSpecified ? model.Value.Value : null; | this.Value = model.Value.IsSpecified ? model.Value.Value : null; | ||||
this.discord = discord; | this.discord = discord; | ||||
this.guild = guild; | |||||
this.Options = model.Options.Any() | this.Options = model.Options.Any() | ||||
? model.Options.Select(x => new SocketSlashCommandDataOption(x, discord, guild)).ToImmutableArray() | |||||
? model.Options.Select(x => new SocketSlashCommandDataOption(x, discord)).ToImmutableArray() | |||||
: null; | : null; | ||||
} | } | ||||
@@ -49,7 +47,7 @@ namespace Discord.WebSocket | |||||
{ | { | ||||
if (option.Value is ulong id) | if (option.Value is ulong id) | ||||
{ | { | ||||
var guild = option.discord.GetGuild(option.guild); | |||||
var guild = option.discord.GetGuild(id); | |||||
if (guild == null) | if (guild == null) | ||||
return null; | return null; | ||||
@@ -64,7 +62,7 @@ namespace Discord.WebSocket | |||||
{ | { | ||||
if (option.Value is ulong id) | if (option.Value is ulong id) | ||||
{ | { | ||||
var guild = option.discord.GetGuild(option.guild); | |||||
var guild = option.discord.GetGuild(id); | |||||
if (guild == null) | if (guild == null) | ||||
return null; | return null; | ||||
@@ -79,7 +77,7 @@ namespace Discord.WebSocket | |||||
{ | { | ||||
if(option.Value is ulong id) | if(option.Value is ulong id) | ||||
{ | { | ||||
var guild = option.discord.GetGuild(option.guild); | |||||
var guild = option.discord.GetGuild(id); | |||||
if (guild == null) | if (guild == null) | ||||
return null; | return null; | ||||
@@ -14,21 +14,14 @@ namespace Discord.WebSocket | |||||
public abstract class SocketInteraction : SocketEntity<ulong>, IDiscordInteraction | public abstract class SocketInteraction : SocketEntity<ulong>, IDiscordInteraction | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// The <see cref="SocketGuild"/> this interaction was used in. | |||||
/// The <see cref="ISocketMessageChannel"/> this interaction was used in. | |||||
/// </summary> | /// </summary> | ||||
public SocketGuild Guild | |||||
=> Discord.GetGuild(GuildId); | |||||
public ISocketMessageChannel Channel { get; private set; } | |||||
/// <summary> | /// <summary> | ||||
/// The <see cref="SocketTextChannel"/> this interaction was used in. | |||||
/// The <see cref="SocketUser"/> who triggered this interaction. | |||||
/// </summary> | /// </summary> | ||||
public SocketTextChannel Channel | |||||
=> Guild.GetTextChannel(ChannelId); | |||||
/// <summary> | |||||
/// The <see cref="SocketGuildUser"/> who triggered this interaction. | |||||
/// </summary> | |||||
public SocketGuildUser User { get; private set; } | |||||
public SocketUser User { get; private set; } | |||||
/// <summary> | /// <summary> | ||||
/// The type of this interaction. | /// The type of this interaction. | ||||
@@ -58,9 +51,8 @@ namespace Discord.WebSocket | |||||
public bool IsValidToken | public bool IsValidToken | ||||
=> CheckToken(); | => CheckToken(); | ||||
private ulong GuildId { get; set; } | |||||
private ulong ChannelId { get; set; } | |||||
private ulong UserId { get; set; } | |||||
private ulong? GuildId { get; set; } | |||||
private ulong? ChannelId { get; set; } | |||||
internal SocketInteraction(DiscordSocketClient client, ulong id) | internal SocketInteraction(DiscordSocketClient client, ulong id) | ||||
: base(client, id) | : base(client, id) | ||||
@@ -83,15 +75,35 @@ namespace Discord.WebSocket | |||||
? model.Data.Value | ? model.Data.Value | ||||
: null; | : null; | ||||
this.GuildId = model.GuildId; | |||||
this.ChannelId = model.ChannelId; | |||||
this.GuildId = model.GuildId.ToNullable(); | |||||
this.ChannelId = model.ChannelId.ToNullable(); | |||||
this.Token = model.Token; | this.Token = model.Token; | ||||
this.Version = model.Version; | this.Version = model.Version; | ||||
this.UserId = model.Member.User.Id; | |||||
this.Type = model.Type; | this.Type = model.Type; | ||||
if (this.User == null) | if (this.User == null) | ||||
this.User = SocketGuildUser.Create(this.Guild, Discord.State, model.Member); // Change from getter. | |||||
{ | |||||
if (model.Member.IsSpecified && model.GuildId.IsSpecified) | |||||
{ | |||||
this.User = SocketGuildUser.Create(Discord.State.GetGuild(this.GuildId.Value), Discord.State, model.Member.Value); | |||||
} | |||||
else | |||||
{ | |||||
this.User = SocketGlobalUser.Create(this.Discord, this.Discord.State, model.User.Value); | |||||
} | |||||
} | |||||
if (this.Channel == null) | |||||
{ | |||||
if (model.ChannelId.IsSpecified) | |||||
{ | |||||
this.Channel = Discord.State.GetChannel(model.ChannelId.Value) as ISocketMessageChannel; | |||||
} | |||||
else | |||||
{ | |||||
this.Channel = Discord.State.GetDMChannel(this.User.Id); | |||||
} | |||||
} | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -162,9 +162,16 @@ namespace Discord.WebSocket | |||||
if (model.Components.IsSpecified) | if (model.Components.IsSpecified) | ||||
{ | { | ||||
Components = model.Components.Value.Select(x => | |||||
(x as Newtonsoft.Json.Linq.JToken).ToObject<ActionRowComponent>() | |||||
).ToList(); | |||||
Components = model.Components.Value.Select(x => new ActionRowComponent(x.Components.Select(x => | |||||
new ButtonComponent( | |||||
x.Style, | |||||
x.Label.GetValueOrDefault(), | |||||
x.Emote.IsSpecified ? x.Emote.Value.Id.HasValue ? new Emote(x.Emote.Value.Id.Value, x.Emote.Value.Name, x.Emote.Value.Animated.GetValueOrDefault()) : new Emoji(x.Emote.Value.Name) : null, | |||||
x.CustomId.GetValueOrDefault(), | |||||
x.Url.GetValueOrDefault(), | |||||
x.Disabled.GetValueOrDefault()) | |||||
).ToList() | |||||
)).ToList(); | |||||
} | } | ||||
else | else | ||||
Components = new List<ActionRowComponent>(); | Components = new List<ActionRowComponent>(); | ||||
@@ -2,7 +2,7 @@ | |||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> | <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> | ||||
<metadata> | <metadata> | ||||
<id>Discord.Net.Labs</id> | <id>Discord.Net.Labs</id> | ||||
<version>2.3.1$suffix$</version> | |||||
<version>2.3.3$suffix$</version> | |||||
<title>Discord.Net Labs</title> | <title>Discord.Net Labs</title> | ||||
<authors>Discord.Net Contributors</authors> | <authors>Discord.Net Contributors</authors> | ||||
<owners>quinchs</owners> | <owners>quinchs</owners> | ||||
@@ -14,23 +14,23 @@ | |||||
<iconUrl>https://avatars.githubusercontent.com/u/84047264</iconUrl> | <iconUrl>https://avatars.githubusercontent.com/u/84047264</iconUrl> | ||||
<dependencies> | <dependencies> | ||||
<group targetFramework="net461"> | <group targetFramework="net461"> | ||||
<dependency id="Discord.Net.Labs.Core" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Rest" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.WebSocket" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Core" version="2.3.4$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Rest" version="2.3.5$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.WebSocket" version="2.3.4$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Commands" version="2.3.1$suffix$" /> | <dependency id="Discord.Net.Labs.Commands" version="2.3.1$suffix$" /> | ||||
<dependency id="Discord.Net.Labs.Webhook" version="2.3.1$suffix$" /> | <dependency id="Discord.Net.Labs.Webhook" version="2.3.1$suffix$" /> | ||||
</group> | </group> | ||||
<group targetFramework="netstandard2.0"> | <group targetFramework="netstandard2.0"> | ||||
<dependency id="Discord.Net.Labs.Core" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Rest" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.WebSocket" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Core" version="2.3.4$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Rest" version="2.3.5$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.WebSocket" version="2.3.4$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Commands" version="2.3.1$suffix$" /> | <dependency id="Discord.Net.Labs.Commands" version="2.3.1$suffix$" /> | ||||
<dependency id="Discord.Net.Labs.Webhook" version="2.3.1$suffix$" /> | <dependency id="Discord.Net.Labs.Webhook" version="2.3.1$suffix$" /> | ||||
</group> | </group> | ||||
<group targetFramework="netstandard2.1"> | <group targetFramework="netstandard2.1"> | ||||
<dependency id="Discord.Net.Labs.Core" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Rest" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.WebSocket" version="2.3.1$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Core" version="2.3.4$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Rest" version="2.3.5$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.WebSocket" version="2.3.4$suffix$" /> | |||||
<dependency id="Discord.Net.Labs.Commands" version="2.3.1$suffix$" /> | <dependency id="Discord.Net.Labs.Commands" version="2.3.1$suffix$" /> | ||||
<dependency id="Discord.Net.Labs.Webhook" version="2.3.1$suffix$" /> | <dependency id="Discord.Net.Labs.Webhook" version="2.3.1$suffix$" /> | ||||
</group> | </group> | ||||