From d871e9c1dc4162e84e61037ba27f838a4617bba2 Mon Sep 17 00:00:00 2001 From: cat Date: Sat, 22 Oct 2022 04:02:14 -0500 Subject: [PATCH] Initial support for new select types --- .../MessageComponents/ComponentBuilder.cs | 83 ++++++++++++++++--- .../MessageComponents/ComponentType.cs | 19 ++++- .../IComponentInteractionData.cs | 19 ++++- .../MessageComponents/SelectMenuComponent.cs | 14 +++- .../Entities/Interactions/Modals/Modal.cs | 4 +- src/Discord.Net.Core/Utils/ComponentType.cs | 8 ++ .../API/Common/ActionRowComponent.cs | 4 + .../Common/MessageComponentInteractionData.cs | 4 + ...MessageComponentInteractionDataResolved.cs | 19 +++++ .../API/Common/SelectMenuComponent.cs | 7 ++ .../RestMessageComponentData.cs | 20 +++++ .../Interactions/Modals/RestModalData.cs | 16 +++- .../Entities/Messages/RestMessage.cs | 18 ++-- .../Converters/MessageComponentConverter.cs | 4 + .../DiscordSocketApiClient.cs | 2 + .../DiscordSocketClient.cs | 3 +- .../SocketMessageComponentData.cs | 29 ++++--- .../Entities/Messages/SocketMessage.cs | 6 +- 18 files changed, 234 insertions(+), 45 deletions(-) create mode 100644 src/Discord.Net.Core/Utils/ComponentType.cs create mode 100644 src/Discord.Net.Rest/API/Common/MessageComponentInteractionDataResolved.cs diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs index fd8798ed3..4721b0cbd 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs @@ -1,7 +1,7 @@ +using Discord.Utils; using System; using System.Collections.Generic; using System.Linq; -using Discord.Utils; namespace Discord { @@ -278,9 +278,7 @@ namespace Discord { if (_actionRows?.SelectMany(x => x.Components)?.Any(x => x.Type == ComponentType.TextInput) ?? false) throw new ArgumentException("TextInputComponents are not allowed in messages.", nameof(ActionRows)); - if (_actionRows?.SelectMany(x => x.Components)?.Any(x => x.Type == ComponentType.ModalSubmit) ?? false) - throw new ArgumentException("ModalSubmit components are not allowed in messages.", nameof(ActionRows)); - + return _actionRows != null ? new MessageComponent(_actionRows.Select(x => x.Build()).ToList()) : MessageComponent.Empty; @@ -357,7 +355,7 @@ namespace Discord /// The min values of the placeholder. /// The max values of the placeholder. /// Whether or not the menu is disabled. - /// The current builder. + /// The current builder. public ActionRowBuilder WithSelectMenu(string customId, List options, string placeholder = null, int minValues = 1, int maxValues = 1, bool disabled = false) { @@ -431,10 +429,10 @@ namespace Discord { var builtButton = button.Build(); - if(Components.Count >= 5) + if (Components.Count >= 5) throw new InvalidOperationException($"Components count reached {MaxChildCount}"); - if (Components.Any(x => x.Type == ComponentType.SelectMenu)) + if (Components.Any(x => x.Type.IsSelectType())) throw new InvalidOperationException($"A button cannot be added to a row with a SelectMenu"); AddComponent(builtButton); @@ -458,11 +456,15 @@ namespace Discord case ComponentType.ActionRow: return false; case ComponentType.Button: - if (Components.Any(x => x.Type == ComponentType.SelectMenu)) + if (Components.Any(x => x.Type.IsSelectType())) return false; else return Components.Count < 5; case ComponentType.SelectMenu: + case ComponentType.ChannelSelect: + case ComponentType.MentionableSelect: + case ComponentType.RoleSelect: + case ComponentType.UserSelect: return Components.Count == 0; default: return false; @@ -759,6 +761,18 @@ namespace Discord }; } + /// + /// Gets or sets the type of the current select menu. + /// + /// Type must be a select menu type. + public ComponentType Type + { + get => _type; + set => _type = value.IsSelectType() + ? value + : throw new ArgumentException("Type must be a select menu type.", nameof(value)); + } + /// /// Gets or sets the placeholder text of the current select menu. /// @@ -827,11 +841,17 @@ namespace Discord /// public bool IsDisabled { get; set; } + /// + /// Gets or sets the menu's channel types (only valid on s). + /// + public List ChannelTypes { get; set; } + private List _options = new List(); private int _minValues = 1; private int _maxValues = 1; private string _placeholder; private string _customId; + private ComponentType _type = ComponentType.SelectMenu; /// /// Creates a new instance of a . @@ -862,7 +882,9 @@ namespace Discord /// The max values of this select menu. /// The min values of this select menu. /// Disabled this select menu or not. - public SelectMenuBuilder(string customId, List options, string placeholder = null, int maxValues = 1, int minValues = 1, bool isDisabled = false) + /// The of this select menu. + /// The types of channels this menu can select (only valid on s) + public SelectMenuBuilder(string customId, List options = null, string placeholder = null, int maxValues = 1, int minValues = 1, bool isDisabled = false, ComponentType type = ComponentType.SelectMenu, List channelTypes = null) { CustomId = customId; Options = options; @@ -870,6 +892,8 @@ namespace Discord IsDisabled = isDisabled; MaxValues = maxValues; MinValues = minValues; + Type = type; + ChannelTypes = channelTypes ?? new(); } /// @@ -990,6 +1014,45 @@ namespace Discord return this; } + /// + /// Sets the menu's current type. + /// + /// The type of the menu. + /// + /// The current builder. + /// + public SelectMenuBuilder WithType(ComponentType type) + { + Type = type; + return this; + } + + /// + /// Sets the menus valid channel types (only for s). + /// + /// The valid channel types of the menu. + /// + /// The current builder. + /// + public SelectMenuBuilder WithChannelTypes(List channelTypes) + { + ChannelTypes = channelTypes; + return this; + } + + /// + /// Sets the menus valid channel types (only for s). + /// + /// The valid channel types of the menu. + /// + /// The current builder. + /// + public SelectMenuBuilder WithChannelTypes(params ChannelType[] channelTypes) + { + ChannelTypes = channelTypes.ToList(); + return this; + } + /// /// Builds a /// @@ -998,7 +1061,7 @@ namespace Discord { var options = Options?.Select(x => x.Build()).ToList(); - return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled); + return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled, Type, ChannelTypes); } } diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs index 1d63ee829..0ad3f741a 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs @@ -26,8 +26,23 @@ namespace Discord TextInput = 4, /// - /// An interaction sent when a model is submitted. + /// A select menu for picking from users. /// - ModalSubmit = 5, + UserSelect = 5, + + /// + /// A select menu for picking from roles. + /// + RoleSelect = 6, + + /// + /// A select menu for picking from roles and users. + /// + MentionableSelect = 7, + + /// + /// A select menu for picking from channels. + /// + ChannelSelect = 8, } } diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs index 039b6b41f..a3366f45b 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs @@ -18,12 +18,27 @@ namespace Discord ComponentType Type { get; } /// - /// Gets the value(s) of a interaction response. + /// Gets the value(s) of a interaction response. /// IReadOnlyCollection Values { get; } /// - /// Gets the value of a interaction response. + /// Gets the channels(s) of a interaction response. + /// + IReadOnlyCollection Channels { get; } + + /// + /// Gets the user(s) of a or interaction response. + /// + IReadOnlyCollection Users { get; } + + /// + /// Gets the roles(s) of a or interaction response. + /// + IReadOnlyCollection Roles { get; } + + /// + /// Gets the value of a interaction response. /// public string Value { get; } } diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs index 229c1e148..eccdd18c6 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +10,7 @@ namespace Discord public class SelectMenuComponent : IMessageComponent { /// - public ComponentType Type => ComponentType.SelectMenu; + public ComponentType Type { get; } /// public string CustomId { get; } @@ -39,6 +40,11 @@ namespace Discord /// public bool IsDisabled { get; } + /// + /// Gets the allowed channel types for this modal + /// + public IReadOnlyCollection ChannelTypes { get; } + /// /// Turns this select menu into a builder. /// @@ -52,9 +58,9 @@ namespace Discord Placeholder, MaxValues, MinValues, - IsDisabled); + IsDisabled, Type, ChannelTypes.ToList()); - internal SelectMenuComponent(string customId, List options, string placeholder, int minValues, int maxValues, bool disabled) + internal SelectMenuComponent(string customId, List options, string placeholder, int minValues, int maxValues, bool disabled, ComponentType type, IEnumerable channelTypes = null) { CustomId = customId; Options = options; @@ -62,6 +68,8 @@ namespace Discord MinValues = minValues; MaxValues = maxValues; IsDisabled = disabled; + Type = type; + ChannelTypes = channelTypes?.ToArray() ?? Array.Empty(); } } } diff --git a/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs b/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs index a0fde5ea3..a435d33ef 100644 --- a/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs +++ b/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs @@ -7,12 +7,12 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents a modal interaction. + /// Represents a modal interaction. /// public class Modal : IMessageComponent { /// - public ComponentType Type => ComponentType.ModalSubmit; + public ComponentType Type => throw new NotSupportedException("Modals do not have a component type."); /// /// Gets the title of the modal. diff --git a/src/Discord.Net.Core/Utils/ComponentType.cs b/src/Discord.Net.Core/Utils/ComponentType.cs new file mode 100644 index 000000000..c7d42c512 --- /dev/null +++ b/src/Discord.Net.Core/Utils/ComponentType.cs @@ -0,0 +1,8 @@ +namespace Discord.Utils; + +public static class ComponentTypeUtils +{ + public static bool IsSelectType(this ComponentType type) => type is ComponentType.ChannelSelect + or ComponentType.SelectMenu or ComponentType.RoleSelect or ComponentType.UserSelect + or ComponentType.MentionableSelect; +} diff --git a/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs b/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs index 9a7eb80dd..e97ca71d6 100644 --- a/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs +++ b/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs @@ -21,6 +21,10 @@ namespace Discord.API { ComponentType.Button => new ButtonComponent(x as Discord.ButtonComponent), ComponentType.SelectMenu => new SelectMenuComponent(x as Discord.SelectMenuComponent), + ComponentType.ChannelSelect => new SelectMenuComponent(x as Discord.SelectMenuComponent), + ComponentType.UserSelect => new SelectMenuComponent(x as Discord.SelectMenuComponent), + ComponentType.RoleSelect => new SelectMenuComponent(x as Discord.SelectMenuComponent), + ComponentType.MentionableSelect => new SelectMenuComponent(x as Discord.SelectMenuComponent), ComponentType.TextInput => new TextInputComponent(x as Discord.TextInputComponent), _ => null }; diff --git a/src/Discord.Net.Rest/API/Common/MessageComponentInteractionData.cs b/src/Discord.Net.Rest/API/Common/MessageComponentInteractionData.cs index 4633fc25a..1bc45d21b 100644 --- a/src/Discord.Net.Rest/API/Common/MessageComponentInteractionData.cs +++ b/src/Discord.Net.Rest/API/Common/MessageComponentInteractionData.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using System.Collections.Generic; namespace Discord.API { @@ -15,5 +16,8 @@ namespace Discord.API [JsonProperty("value")] public Optional Value { get; set; } + + [JsonProperty("resolved")] + public Optional Resolved { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Common/MessageComponentInteractionDataResolved.cs b/src/Discord.Net.Rest/API/Common/MessageComponentInteractionDataResolved.cs new file mode 100644 index 000000000..04f97cdfd --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/MessageComponentInteractionDataResolved.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Discord.API; + +internal class MessageComponentInteractionDataResolved +{ + [JsonProperty("users")] + public Optional> Users { get; set; } + + [JsonProperty("members")] + public Optional> Members { get; set; } + + [JsonProperty("channels")] + public Optional> Channels { get; set; } + + [JsonProperty("roles")] + public Optional> Roles { get; set; } +} diff --git a/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs b/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs index 25ac476c5..0943a5270 100644 --- a/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs +++ b/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs @@ -26,6 +26,12 @@ namespace Discord.API [JsonProperty("disabled")] public bool Disabled { get; set; } + [JsonProperty("channel_types")] + public Optional ChannelTypes { get; set; } + + [JsonProperty("resolved")] + public Optional Resolved { get; set; } + [JsonProperty("values")] public Optional Values { get; set; } public SelectMenuComponent() { } @@ -39,6 +45,7 @@ namespace Discord.API MinValues = component.MinValues; MaxValues = component.MaxValues; Disabled = component.IsDisabled; + ChannelTypes = component.ChannelTypes.ToArray(); } } } diff --git a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs index b0efe418c..2b70b0040 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs @@ -21,6 +21,26 @@ namespace Discord.Rest /// public IReadOnlyCollection Values { get; } + /// /> + public IReadOnlyCollection Channels { get; } + /// /> + public IReadOnlyCollection Users { get; } + /// /> + public IReadOnlyCollection Roles { get; } + + #region IComponentInteractionData + + /// + IReadOnlyCollection IComponentInteractionData.Channels => Channels; + + /// + IReadOnlyCollection IComponentInteractionData.Users => Users; + + /// + IReadOnlyCollection IComponentInteractionData.Roles => Roles; + + #endregion + /// public string Value { get; } diff --git a/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs b/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs index 22460ae51..153301ab1 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs @@ -21,12 +21,24 @@ namespace Discord.Rest public IReadOnlyCollection Components { get; } /// - public ComponentType Type => ComponentType.ModalSubmit; + public ComponentType Type => throw new NotSupportedException("Modals do not have a component type."); /// public IReadOnlyCollection Values => throw new NotSupportedException("Modal interactions do not have values!"); - + + /// + public IReadOnlyCollection Channels + => throw new NotSupportedException("Modal interactions do not have channels!"); + + /// + public IReadOnlyCollection Users + => throw new NotSupportedException("Modal interactions do not have users!"); + + /// + public IReadOnlyCollection Roles + => throw new NotSupportedException("Modal interactions do not have roles!"); + /// public string Value => throw new NotSupportedException("Modal interactions do not have value!"); diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index 69e038fd2..8b6b44e39 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -170,26 +170,28 @@ namespace Discord.Rest parsed.Url.GetValueOrDefault(), parsed.Disabled.GetValueOrDefault()); } - case ComponentType.SelectMenu: + case ComponentType.SelectMenu or ComponentType.ChannelSelect or ComponentType.RoleSelect or ComponentType.MentionableSelect or ComponentType.UserSelect: { var parsed = (API.SelectMenuComponent)y; return new SelectMenuComponent( parsed.CustomId, - parsed.Options.Select(z => new SelectMenuOption( + parsed.Options?.Select(z => new SelectMenuOption( z.Label, z.Value, z.Description.GetValueOrDefault(), z.Emoji.IsSpecified - ? z.Emoji.Value.Id.HasValue - ? new Emote(z.Emoji.Value.Id.Value, z.Emoji.Value.Name, z.Emoji.Value.Animated.GetValueOrDefault()) - : new Emoji(z.Emoji.Value.Name) - : null, + ? z.Emoji.Value.Id.HasValue + ? new Emote(z.Emoji.Value.Id.Value, z.Emoji.Value.Name, z.Emoji.Value.Animated.GetValueOrDefault()) + : new Emoji(z.Emoji.Value.Name) + : null, z.Default.ToNullable())).ToList(), parsed.Placeholder.GetValueOrDefault(), parsed.MinValues, parsed.MaxValues, - parsed.Disabled - ); + parsed.Disabled, + parsed.Type, + parsed.ChannelTypes.GetValueOrDefault() + ); } default: return null; diff --git a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs index 36542d83b..7888219bc 100644 --- a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs +++ b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs @@ -30,6 +30,10 @@ namespace Discord.Net.Converters messageComponent = new API.ButtonComponent(); break; case ComponentType.SelectMenu: + case ComponentType.ChannelSelect: + case ComponentType.MentionableSelect: + case ComponentType.RoleSelect: + case ComponentType.UserSelect: messageComponent = new API.SelectMenuComponent(); break; case ComponentType.TextInput: diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 465c47a1d..448b57125 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -1,3 +1,5 @@ +#define DEBUG_PACKETS + using Discord.API.Gateway; using Discord.Net.Queue; using Discord.Net.Rest; diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index a90ffff7a..10cbd61d3 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -5,6 +5,7 @@ using Discord.Net.Converters; using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; +using Discord.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -2394,7 +2395,7 @@ namespace Discord.WebSocket await TimedInvokeAsync(_slashCommandExecuted, nameof(SlashCommandExecuted), slashCommand).ConfigureAwait(false); break; case SocketMessageComponent messageComponent: - if (messageComponent.Data.Type == ComponentType.SelectMenu) + if (messageComponent.Data.Type.IsSelectType()) await TimedInvokeAsync(_selectMenuExecuted, nameof(SelectMenuExecuted), messageComponent).ConfigureAwait(false); if (messageComponent.Data.Type == ComponentType.Button) await TimedInvokeAsync(_buttonExecuted, nameof(ButtonExecuted), messageComponent).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs index c7f6c5106..dcfc1fb7c 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs @@ -1,3 +1,5 @@ +using Discord.Utils; +using System.Linq; using System.Collections.Generic; using Model = Discord.API.MessageComponentInteractionData; @@ -8,24 +10,25 @@ namespace Discord.WebSocket /// public class SocketMessageComponentData : IComponentInteractionData { - /// - /// Gets the components Custom Id that was clicked. - /// + /// public string CustomId { get; } - /// - /// Gets the type of the component clicked. - /// + /// public ComponentType Type { get; } - /// - /// Gets the value(s) of a interaction response. - /// + /// public IReadOnlyCollection Values { get; } - /// - /// Gets the value of a interaction response. - /// + /// + public IReadOnlyCollection Channels { get; } + + /// + public IReadOnlyCollection Users { get; } + + /// + public IReadOnlyCollection Roles { get; } + + /// public string Value { get; } internal SocketMessageComponentData(Model model) @@ -45,7 +48,7 @@ namespace Discord.WebSocket ? (component as API.TextInputComponent).Value.Value : null; - Values = component.Type == ComponentType.SelectMenu + Values = component.Type.IsSelectType() ? (component as API.SelectMenuComponent).Values.Value : null; } diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index 3cd67beb5..40a645afb 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -118,7 +118,7 @@ namespace Discord.WebSocket /// /// Collection of WebSocket-based users. /// - public IReadOnlyCollection MentionedUsers => _userMentions; + public IReadOnlyCollection MentionedUsers => _userMentions; /// public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); @@ -226,7 +226,9 @@ namespace Discord.WebSocket parsed.Placeholder.GetValueOrDefault(), parsed.MinValues, parsed.MaxValues, - parsed.Disabled + parsed.Disabled, + parsed.Type, + parsed.ChannelTypes.GetValueOrDefault() ); } default: