| @@ -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; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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); | ||||
| } | } | ||||
| @@ -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; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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; | ||||
| @@ -14,6 +14,8 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| new public SocketMessageComponentData Data { get; } | new public SocketMessageComponentData Data { get; } | ||||
| 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 +24,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 +35,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> | ||||
| @@ -24,7 +24,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) | ||||
| @@ -40,7 +40,7 @@ namespace Discord.WebSocket | |||||
| (model.Data.Value as JToken).ToObject<DataModel>() | (model.Data.Value as JToken).ToObject<DataModel>() | ||||
| : null; | : null; | ||||
| this.Data.Update(data, this.Guild.Id); | |||||
| this.Data.Update(data); | |||||
| base.Update(model); | base.Update(model); | ||||
| } | } | ||||
| @@ -18,27 +18,23 @@ 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.IsSpecified | this.Options = model.Options.IsSpecified | ||||
| ? model.Options.Value.Select(x => new SocketSlashCommandDataOption(x, this.Discord, guildId)).ToImmutableArray() | |||||
| ? model.Options.Value.Select(x => new SocketSlashCommandDataOption(x, this.Discord)).ToImmutableArray() | |||||
| : null; | : null; | ||||
| } | } | ||||
| @@ -25,18 +25,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.IsSpecified | this.Options = model.Options.IsSpecified | ||||
| ? model.Options.Value.Select(x => new SocketSlashCommandDataOption(x, discord, guild)).ToImmutableArray() | |||||
| ? model.Options.Value.Select(x => new SocketSlashCommandDataOption(x, discord)).ToImmutableArray() | |||||
| : null; | : null; | ||||
| } | } | ||||
| @@ -52,7 +50,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; | ||||
| @@ -67,7 +65,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; | ||||
| @@ -82,7 +80,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> | ||||