From 838d60e2c2f72b7cd6de1133ff608e358003fed2 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 31 Oct 2016 19:50:38 -0400 Subject: [PATCH 01/11] Add API model for Reaction, implement REST methods for reactions --- src/Discord.Net.Core/API/Common/Reaction.cs | 20 +++++++++++ src/Discord.Net.Core/API/DiscordRestApiClient.cs | 42 ++++++++++++++++++++++ .../API/Rest/GetReactionUsersParams.cs | 8 +++++ .../Entities/Messages/IUserMessage.cs | 13 ++++++- src/Discord.Net.Core/Utils/Preconditions.cs | 1 + .../Entities/Messages/MessageHelper.cs | 23 ++++++++++++ .../Entities/Messages/RestUserMessage.cs | 14 ++++++++ .../Entities/Messages/RpcUserMessage.cs | 13 +++++++ .../Entities/Messages/SocketUserMessage.cs | 13 +++++++ 9 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/Discord.Net.Core/API/Common/Reaction.cs create mode 100644 src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs diff --git a/src/Discord.Net.Core/API/Common/Reaction.cs b/src/Discord.Net.Core/API/Common/Reaction.cs new file mode 100644 index 000000000..69dbf4b0c --- /dev/null +++ b/src/Discord.Net.Core/API/Common/Reaction.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Discord.API.Common +{ + public class Reaction + { + [JsonProperty("user_id")] + public ulong UserId { get; set; } + [JsonProperty("message_id")] + public ulong MessageId { get; set; } + [JsonProperty("emoji")] + public Emoji Emoji { get; set; } + [JsonProperty("channel_id")] + public ulong ChannelId { get; set; } + } +} diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index ac18e8ace..4d85c68b2 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -512,6 +512,48 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendJsonAsync("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucketId: ClientBucket.SendEditId, options: options).ConfigureAwait(false); } + public async Task AddReactionAsync(ulong channelId, ulong messageId, string emoji, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); + + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(channelId: channelId); + + await SendAsync("PUT", () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}/@me", ids, options: options).ConfigureAwait(false); + } + public async Task RemoveReactionAsync(ulong channelId, ulong messageId, ulong userId, string emoji, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); + + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(channelId: channelId); + + await SendAsync("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}/{userId}", ids, options: options).ConfigureAwait(false); + } + public async Task> GetReactionUsersAsync(ulong channelId, ulong messageId, string emoji, GetReactionUsersParams args, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); + Preconditions.NotNull(args, nameof(args)); + Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); + Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); + Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); + options = RequestOptions.CreateOrClone(options); + + int limit = args.Limit.GetValueOrDefault(int.MaxValue); + ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); + + var ids = new BucketIds(channelId: channelId); + Expression> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}"; + return await SendAsync>("GET", endpoint, ids, options: options).ConfigureAwait(false); + } public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); diff --git a/src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs b/src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs new file mode 100644 index 000000000..bb9b22ab8 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs @@ -0,0 +1,8 @@ +namespace Discord.API.Rest +{ + public class GetReactionUsersParams + { + public Optional Limit { get; set; } + public Optional AfterUserId { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index a9dc4735c..3a7b5821a 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -1,5 +1,6 @@ using Discord.API.Rest; using System; +using System.Collections.Generic; using System.Threading.Tasks; namespace Discord @@ -12,7 +13,17 @@ namespace Discord Task PinAsync(RequestOptions options = null); /// Removes this message from its channel's pinned messages. Task UnpinAsync(RequestOptions options = null); - + + /// Adds a reaction to this message. + Task AddReactionAsync(Emoji emoji, RequestOptions options = null); + /// Adds a reaction to this message. + Task AddReactionAsync(string emoji, RequestOptions options = null); + /// Removes a reaction from message. + Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null); + /// Removes a reaction from this message. + Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null); + Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options = null); + /// Transforms this message's text into a human readable form by resolving its tags. string Resolve( TagHandling userHandling = TagHandling.Name, diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index 14c9db24d..2c6ea2417 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -45,6 +45,7 @@ namespace Discord throw new ArgumentException("Argument cannot be blank.", name); } } + //Numerics public static void NotEqual(sbyte obj, sbyte value, string name) { if (obj == value) throw new ArgumentOutOfRangeException(name); } diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 358f6f5a9..602a790a5 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -23,6 +23,29 @@ namespace Discord.Rest await client.ApiClient.DeleteMessageAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); } + public static async Task AddReactionAsync(IMessage msg, Emoji emoji, BaseDiscordClient client, RequestOptions options) + => await AddReactionAsync(msg, $"{emoji.Name}:{emoji.Id}", client, options).ConfigureAwait(false); + public static async Task AddReactionAsync(IMessage msg, string emoji, BaseDiscordClient client, RequestOptions options) + { + await client.ApiClient.AddReactionAsync(msg.Channel.Id, msg.Id, emoji, options).ConfigureAwait(false); + } + + public static async Task RemoveReactionAsync(IMessage msg, IUser user, Emoji emoji, BaseDiscordClient client, RequestOptions options) + => await RemoveReactionAsync(msg, user, $"{emoji.Name}:{emoji.Id}", client, options).ConfigureAwait(false); + public static async Task RemoveReactionAsync(IMessage msg, IUser user, string emoji, BaseDiscordClient client, + RequestOptions options) + { + await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options); + } + + public static async Task> GetReactionUsersAsync(IMessage msg, string emoji, + Action func, BaseDiscordClient client, RequestOptions options) + { + var args = new GetReactionUsersParams(); + func(args); + return (await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false)).Select(u => u as IUser).Where(u => u != null).ToImmutableArray(); + } + public static async Task PinAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index d3d3b6fce..66d270fdd 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -117,6 +117,20 @@ namespace Discord.Rest Update(model); } + public Task AddReactionAsync(Emoji emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + public Task AddReactionAsync(string emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) + => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + + public Task PinAsync(RequestOptions options) => MessageHelper.PinAsync(this, Discord, options); public Task UnpinAsync(RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index edfa60484..01cff3611 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -101,6 +101,19 @@ namespace Discord.Rpc public Task ModifyAsync(Action func, RequestOptions options) => MessageHelper.ModifyAsync(this, Discord, func, options); + public Task AddReactionAsync(Emoji emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + public Task AddReactionAsync(string emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) + => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task PinAsync(RequestOptions options) => MessageHelper.PinAsync(this, Discord, options); public Task UnpinAsync(RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index db9b82ebd..4174ff899 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -113,6 +113,19 @@ namespace Discord.WebSocket public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); + public Task AddReactionAsync(Emoji emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + public Task AddReactionAsync(string emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) + => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task PinAsync(RequestOptions options = null) => MessageHelper.PinAsync(this, Discord, options); public Task UnpinAsync(RequestOptions options = null) From 7018bc9c589dfa507cc8b96e9f28b195a9a9c452 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 31 Oct 2016 21:00:35 -0400 Subject: [PATCH 02/11] """Support""" the 'reactions' field on message objects this is all really broken --- src/Discord.Net.Core/API/Common/Message.cs | 2 ++ src/Discord.Net.Core/API/Common/Reaction.cs | 12 +++++----- src/Discord.Net.Core/Entities/Messages/IMessage.cs | 2 ++ .../Entities/Messages/IReaction.cs | 12 ++++++++++ .../Entities/Messages/RestMessage.cs | 2 ++ .../Entities/Messages/RestReaction.cs | 26 ++++++++++++++++++++++ .../Entities/Messages/RestUserMessage.cs | 16 +++++++++++++ .../Entities/Messages/RpcMessage.cs | 1 + .../API/Gateway/GatewayReaction.cs | 20 +++++++++++++++++ .../Entities/Messages/SocketMessage.cs | 1 + .../Entities/Messages/SocketReaction.cs | 24 ++++++++++++++++++++ .../Entities/Messages/SocketUserMessage.cs | 17 ++++++++++++++ 12 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Messages/IReaction.cs create mode 100644 src/Discord.Net.Rest/Entities/Messages/RestReaction.cs create mode 100644 src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs create mode 100644 src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs diff --git a/src/Discord.Net.Core/API/Common/Message.cs b/src/Discord.Net.Core/API/Common/Message.cs index 2c19780b1..f812d0622 100644 --- a/src/Discord.Net.Core/API/Common/Message.cs +++ b/src/Discord.Net.Core/API/Common/Message.cs @@ -36,5 +36,7 @@ namespace Discord.API public Optional Embeds { get; set; } [JsonProperty("pinned")] public Optional Pinned { get; set; } + [JsonProperty("reactions")] + public Optional Reactions { get; set; } } } diff --git a/src/Discord.Net.Core/API/Common/Reaction.cs b/src/Discord.Net.Core/API/Common/Reaction.cs index 69dbf4b0c..e143004ef 100644 --- a/src/Discord.Net.Core/API/Common/Reaction.cs +++ b/src/Discord.Net.Core/API/Common/Reaction.cs @@ -4,17 +4,15 @@ using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; -namespace Discord.API.Common +namespace Discord.API { public class Reaction { - [JsonProperty("user_id")] - public ulong UserId { get; set; } - [JsonProperty("message_id")] - public ulong MessageId { get; set; } + [JsonProperty("count")] + public int Count { get; set; } + [JsonProperty("me")] + public bool Me { get; set; } [JsonProperty("emoji")] public Emoji Emoji { get; set; } - [JsonProperty("channel_id")] - public ulong ChannelId { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs index 6bb44368b..13f3b662a 100644 --- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs @@ -31,6 +31,8 @@ namespace Discord IReadOnlyCollection Attachments { get; } /// Returns all embeds included in this message. IReadOnlyCollection Embeds { get; } + /// Returns all reactions included in this message. + IReadOnlyCollection Reactions { get; } /// Returns all tags included in this message's content. IReadOnlyCollection Tags { get; } /// Returns the ids of channels mentioned in this message. diff --git a/src/Discord.Net.Core/Entities/Messages/IReaction.cs b/src/Discord.Net.Core/Entities/Messages/IReaction.cs new file mode 100644 index 000000000..5d7eae44e --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/IReaction.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Discord +{ + public interface IReaction + { + API.Emoji Emoji { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index ea064dd81..41cff4380 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -27,6 +27,7 @@ namespace Discord.Rest public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; @@ -70,5 +71,6 @@ namespace Discord.Rest IReadOnlyCollection IMessage.Attachments => Attachments; IReadOnlyCollection IMessage.Embeds => Embeds; IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); + IReadOnlyCollection IMessage.Reactions => Reactions; } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs new file mode 100644 index 000000000..2fb534ce4 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Reaction; + +namespace Discord +{ + public class RestReaction : IReaction + { + internal RestReaction(Model model) + { + _emoji = model.Emoji; + _count = model.Count; + + } + + internal readonly API.Emoji _emoji; + internal readonly int _count; + internal readonly bool _me; + + public API.Emoji Emoji => _emoji; + public int Count => _count; + public bool Me => _me; + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 66d270fdd..2625718c7 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -18,6 +18,7 @@ namespace Discord.Rest private ImmutableArray _attachments; private ImmutableArray _embeds; private ImmutableArray _tags; + private ImmutableArray _reactions; public override bool IsTTS => _isTTS; public override bool IsPinned => _isPinned; @@ -29,6 +30,7 @@ namespace Discord.Rest public override IReadOnlyCollection MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); public override IReadOnlyCollection Tags => _tags; + public override IReadOnlyCollection Reactions => _reactions; internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, RestUser author, IGuild guild) : base(discord, id, channel, author, guild) @@ -103,6 +105,20 @@ namespace Discord.Rest } } + if (model.Reactions.IsSpecified) + { + var value = model.Reactions.Value; + if (value.Length > 0) + { + var reactions = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + reactions.Add(new RestReaction(value[i])); + _reactions = reactions.ToImmutable(); + } + else + _reactions = ImmutableArray.Create(); + } + if (model.Content.IsSpecified) { var text = model.Content.Value; diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs index d0464487b..35e4ef52e 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs @@ -28,6 +28,7 @@ namespace Discord.Rpc public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUserIds => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs new file mode 100644 index 000000000..096159478 --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Discord.API.Gateway +{ + public class GatewayReaction : Reaction + { + [JsonProperty("user_id")] + public ulong UserId { get; set; } + [JsonProperty("message_id")] + public ulong MessageId { get; set; } + [JsonProperty("channel_id")] + public ulong ChannelId { get; set; } + [JsonProperty("emoji")] + public Discord.API.Emoji Emoji { get; set; } + } +} diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index 0b09d2d22..3f7512295 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -27,6 +27,7 @@ namespace Discord.WebSocket public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs new file mode 100644 index 000000000..b96513cc1 --- /dev/null +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Gateway.GatewayReaction; + +namespace Discord.WebSocket +{ + public class SocketReaction : IReaction + { + internal SocketReaction(Model model) + { + UserId = model.UserId; + MessageId = model.MessageId; + ChannelId = model.ChannelId; + Emoji = model.Emoji; + } + + public ulong UserId { get; internal set; } + public ulong MessageId { get; internal set; } + public ulong ChannelId { get; internal set; } + public API.Emoji Emoji { get; internal set; } + } +} diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 4174ff899..5ccf1825f 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Threading.Tasks; +using Discord.API.Gateway; using Model = Discord.API.Message; namespace Discord.WebSocket @@ -18,6 +19,7 @@ namespace Discord.WebSocket private ImmutableArray _attachments; private ImmutableArray _embeds; private ImmutableArray _tags; + private ImmutableArray _reactions; public override bool IsTTS => _isTTS; public override bool IsPinned => _isPinned; @@ -29,6 +31,7 @@ namespace Discord.WebSocket public override IReadOnlyCollection MentionedChannels => MessageHelper.FilterTagsByValue(TagType.ChannelMention, _tags); public override IReadOnlyCollection MentionedRoles => MessageHelper.FilterTagsByValue(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); + public override IReadOnlyCollection Reactions => _reactions; internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) : base(discord, id, channel, author) @@ -101,6 +104,20 @@ namespace Discord.WebSocket } } + if (model.Reactions.IsSpecified) + { + var value = model.Reactions.Value; + if (value.Length > 0) + { + var reactions = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + reactions.Add(new SocketReaction(value[i] as GatewayReaction)); + _reactions = reactions.ToImmutable(); + } + else + _reactions = ImmutableArray.Create(); + } + if (model.Content.IsSpecified) { var text = model.Content.Value; From e2e2c4308dde87105c6f33c7722f2ddee67e389f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 18:15:47 -0400 Subject: [PATCH 03/11] Refactor, rearrange, reimplement reactions; receive over gateway --- src/Discord.Net.Core/Entities/Messages/Emoji.cs | 5 +++ src/Discord.Net.Core/Entities/Messages/IMessage.cs | 2 - .../Entities/Messages/IReaction.cs | 2 +- .../Entities/Messages/IUserMessage.cs | 3 ++ .../Entities/Messages/RestMessage.cs | 2 - .../Entities/Messages/RestReaction.cs | 16 +++----- .../Entities/Messages/RestUserMessage.cs | 2 +- .../Entities/Messages/RpcMessage.cs | 1 - .../Entities/Messages/RpcUserMessage.cs | 1 + .../API/Gateway/GatewayReaction.cs | 4 +- .../DiscordSocketClient.Events.cs | 12 ++++++ src/Discord.Net.WebSocket/DiscordSocketClient.cs | 48 ++++++++++++++++++++++ .../Entities/Messages/SocketMessage.cs | 1 - .../Entities/Messages/SocketReaction.cs | 10 ++--- .../Entities/Messages/SocketUserMessage.cs | 28 ++++++------- 15 files changed, 96 insertions(+), 41 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Messages/Emoji.cs b/src/Discord.Net.Core/Entities/Messages/Emoji.cs index 612e99f29..fc71e944b 100644 --- a/src/Discord.Net.Core/Entities/Messages/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Messages/Emoji.cs @@ -19,6 +19,11 @@ namespace Discord Name = name; } + internal static Emoji FromApi(API.Emoji emoji) + { + return new Emoji(emoji.Id, emoji.Name); + } + public static Emoji Parse(string text) { Emoji result; diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs index 13f3b662a..6bb44368b 100644 --- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs @@ -31,8 +31,6 @@ namespace Discord IReadOnlyCollection Attachments { get; } /// Returns all embeds included in this message. IReadOnlyCollection Embeds { get; } - /// Returns all reactions included in this message. - IReadOnlyCollection Reactions { get; } /// Returns all tags included in this message's content. IReadOnlyCollection Tags { get; } /// Returns the ids of channels mentioned in this message. diff --git a/src/Discord.Net.Core/Entities/Messages/IReaction.cs b/src/Discord.Net.Core/Entities/Messages/IReaction.cs index 5d7eae44e..66832760b 100644 --- a/src/Discord.Net.Core/Entities/Messages/IReaction.cs +++ b/src/Discord.Net.Core/Entities/Messages/IReaction.cs @@ -7,6 +7,6 @@ namespace Discord { public interface IReaction { - API.Emoji Emoji { get; } + Emoji Emoji { get; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 3a7b5821a..5b6ab2773 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -14,6 +14,9 @@ namespace Discord /// Removes this message from its channel's pinned messages. Task UnpinAsync(RequestOptions options = null); + /// Returns all reactions included in this message. + IReadOnlyDictionary Reactions { get; } + /// Adds a reaction to this message. Task AddReactionAsync(Emoji emoji, RequestOptions options = null); /// Adds a reaction to this message. diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index 41cff4380..ea064dd81 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -27,7 +27,6 @@ namespace Discord.Rest public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); - public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; @@ -71,6 +70,5 @@ namespace Discord.Rest IReadOnlyCollection IMessage.Attachments => Attachments; IReadOnlyCollection IMessage.Embeds => Embeds; IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); - IReadOnlyCollection IMessage.Reactions => Reactions; } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs index 2fb534ce4..7512dd4d8 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs @@ -10,17 +10,13 @@ namespace Discord { internal RestReaction(Model model) { - _emoji = model.Emoji; - _count = model.Count; - + Emoji = Emoji.FromApi(model.Emoji); + Count = model.Count; + Me = model.Me; } - internal readonly API.Emoji _emoji; - internal readonly int _count; - internal readonly bool _me; - - public API.Emoji Emoji => _emoji; - public int Count => _count; - public bool Me => _me; + public Emoji Emoji { get; private set; } + public int Count { get; private set; } + public bool Me { get; private set; } } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 2625718c7..1af844501 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -30,7 +30,7 @@ namespace Discord.Rest public override IReadOnlyCollection MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); public override IReadOnlyCollection Tags => _tags; - public override IReadOnlyCollection Reactions => _reactions; + public IReadOnlyDictionary Reactions => _reactions.ToDictionary(x => x.Emoji, x => x.Count); internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, RestUser author, IGuild guild) : base(discord, id, channel, author, guild) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs index 35e4ef52e..d0464487b 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs @@ -28,7 +28,6 @@ namespace Discord.Rpc public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUserIds => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); - public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index 01cff3611..832e54d81 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -30,6 +30,7 @@ namespace Discord.Rpc public override IReadOnlyCollection MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUserIds => MessageHelper.FilterTagsByKey(TagType.UserMention, _tags); public override IReadOnlyCollection Tags => _tags; + public IReadOnlyDictionary Reactions => ImmutableDictionary.Create(); internal RpcUserMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) : base(discord, id, channel, author) diff --git a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs index 096159478..2b9d6becc 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GatewayReaction : Reaction + public class GatewayReaction { [JsonProperty("user_id")] public ulong UserId { get; set; } @@ -15,6 +15,6 @@ namespace Discord.API.Gateway [JsonProperty("channel_id")] public ulong ChannelId { get; set; } [JsonProperty("emoji")] - public Discord.API.Emoji Emoji { get; set; } + public Emoji Emoji { get; set; } } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 529caaa87..4454e619f 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -71,6 +71,18 @@ namespace Discord.WebSocket remove { _messageUpdatedEvent.Remove(value); } } private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>(); + public event Func, SocketReaction, Task> ReactionAdded + { + add { _reactionAddedEvent.Add(value); } + remove { _reactionAddedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, SocketReaction, Task> ReactionRemoved + { + add { _reactionRemovedEvent.Add(value); } + remove { _reactionRemovedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 3eb4158d1..2a70824d4 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1304,6 +1304,54 @@ namespace Discord.WebSocket } } break; + case "MESSAGE_REACTION_ADD": + { + await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; + if (channel != null) + { + SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + SocketReaction reaction = new SocketReaction(data); + if (cachedMsg != null) + { + cachedMsg.AddReaction(reaction); + await _reactionAddedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + } + await _reactionAddedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); + } + else + { + await _gatewayLogger.WarningAsync("MESSAGE_REACTION_ADD referenced an unknown channel.").ConfigureAwait(false); + return; + } + break; + } + case "MESSAGE_REACTION_REMOVE": + { + await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; + if (channel != null) + { + SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + SocketReaction reaction = new SocketReaction(data); + if (cachedMsg != null) + { + cachedMsg.RemoveReaction(reaction); + await _reactionRemovedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + } + await _reactionRemovedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); + } + else + { + await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE referenced an unknown channel.").ConfigureAwait(false); + return; + } + break; + } case "MESSAGE_DELETE_BULK": { await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index 3f7512295..0b09d2d22 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -27,7 +27,6 @@ namespace Discord.WebSocket public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); - public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs index b96513cc1..0ac8c4342 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -13,12 +13,12 @@ namespace Discord.WebSocket UserId = model.UserId; MessageId = model.MessageId; ChannelId = model.ChannelId; - Emoji = model.Emoji; + Emoji = Emoji.FromApi(model.Emoji); } - public ulong UserId { get; internal set; } - public ulong MessageId { get; internal set; } - public ulong ChannelId { get; internal set; } - public API.Emoji Emoji { get; internal set; } + public ulong UserId { get; private set; } + public ulong MessageId { get; private set; } + public ulong ChannelId { get; private set; } + public Emoji Emoji { get; private set; } } } diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 5ccf1825f..957fa64b3 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Threading.Tasks; +using System.Linq; using Discord.API.Gateway; using Model = Discord.API.Message; @@ -19,7 +20,7 @@ namespace Discord.WebSocket private ImmutableArray _attachments; private ImmutableArray _embeds; private ImmutableArray _tags; - private ImmutableArray _reactions; + private List _reactions = new List(); public override bool IsTTS => _isTTS; public override bool IsPinned => _isPinned; @@ -31,7 +32,7 @@ namespace Discord.WebSocket public override IReadOnlyCollection MentionedChannels => MessageHelper.FilterTagsByValue(TagType.ChannelMention, _tags); public override IReadOnlyCollection MentionedRoles => MessageHelper.FilterTagsByValue(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); - public override IReadOnlyCollection Reactions => _reactions; + public IReadOnlyDictionary Reactions => _reactions.GroupBy(r => r.Emoji).ToDictionary(x => x.Key, x => x.Count()); internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) : base(discord, id, channel, author) @@ -104,20 +105,6 @@ namespace Discord.WebSocket } } - if (model.Reactions.IsSpecified) - { - var value = model.Reactions.Value; - if (value.Length > 0) - { - var reactions = ImmutableArray.CreateBuilder(value.Length); - for (int i = 0; i < value.Length; i++) - reactions.Add(new SocketReaction(value[i] as GatewayReaction)); - _reactions = reactions.ToImmutable(); - } - else - _reactions = ImmutableArray.Create(); - } - if (model.Content.IsSpecified) { var text = model.Content.Value; @@ -126,6 +113,15 @@ namespace Discord.WebSocket model.Content = text; } } + internal void AddReaction(SocketReaction reaction) + { + _reactions.Add(reaction); + } + internal void RemoveReaction(SocketReaction reaction) + { + if (_reactions.Contains(reaction)) + _reactions.Remove(reaction); + } public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); From c6d9bbf06373ca04a0f8ce0c55a9ad6f219c6fa4 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 18:45:35 -0400 Subject: [PATCH 04/11] Clean up some bugs when parsing unicode emoji --- src/Discord.Net.Core/API/Common/Emoji.cs | 2 +- src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs | 2 +- src/Discord.Net.Core/Entities/Messages/Emoji.cs | 2 +- src/Discord.Net.Core/Entities/Messages/IUserMessage.cs | 2 +- src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs | 4 ++-- src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs | 4 ++-- src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Discord.Net.Core/API/Common/Emoji.cs b/src/Discord.Net.Core/API/Common/Emoji.cs index 032ae51eb..c04786039 100644 --- a/src/Discord.Net.Core/API/Common/Emoji.cs +++ b/src/Discord.Net.Core/API/Common/Emoji.cs @@ -6,7 +6,7 @@ namespace Discord.API public class Emoji { [JsonProperty("id")] - public ulong Id { get; set; } + public ulong? Id { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("roles")] diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs b/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs index 8b2bbd9c2..94f79afa5 100644 --- a/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs @@ -24,7 +24,7 @@ namespace Discord } internal static GuildEmoji Create(Model model) { - return new GuildEmoji(model.Id, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); + return new GuildEmoji(model.Id.Value, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); } public override string ToString() => Name; diff --git a/src/Discord.Net.Core/Entities/Messages/Emoji.cs b/src/Discord.Net.Core/Entities/Messages/Emoji.cs index fc71e944b..dddbe65f1 100644 --- a/src/Discord.Net.Core/Entities/Messages/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Messages/Emoji.cs @@ -21,7 +21,7 @@ namespace Discord internal static Emoji FromApi(API.Emoji emoji) { - return new Emoji(emoji.Id, emoji.Name); + return new Emoji(emoji.Id.GetValueOrDefault(), emoji.Name); } public static Emoji Parse(string text) diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 5b6ab2773..5fe0ccbea 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -25,7 +25,7 @@ namespace Discord Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null); /// Removes a reaction from this message. Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null); - Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options = null); + Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null); /// Transforms this message's text into a human readable form by resolving its tags. string Resolve( diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 1af844501..bc70e608e 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -143,8 +143,8 @@ namespace Discord.Rest public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) - => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); public Task PinAsync(RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index 832e54d81..20129054f 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -112,8 +112,8 @@ namespace Discord.Rpc public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) - => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task> GetReactionUsersAsync(string emoji, int limit, ulong? afterUserId, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); public Task PinAsync(RequestOptions options) => MessageHelper.PinAsync(this, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 957fa64b3..4d097fb32 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -135,9 +135,9 @@ namespace Discord.WebSocket => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - - public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) - => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); public Task PinAsync(RequestOptions options = null) => MessageHelper.PinAsync(this, Discord, options); From 718560917c240a9a17068943c96eb2150b3d4103 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 21:43:18 -0500 Subject: [PATCH 05/11] RequestOptions should be optional on Reactions --- src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs | 8 ++++---- src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs | 8 ++++---- src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index bc70e608e..1313235a2 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -133,14 +133,14 @@ namespace Discord.Rest Update(model); } - public Task AddReactionAsync(Emoji emoji, RequestOptions options) + public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task AddReactionAsync(string emoji, RequestOptions options) + public Task AddReactionAsync(string emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index 20129054f..b2e6b40e3 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -102,14 +102,14 @@ namespace Discord.Rpc public Task ModifyAsync(Action func, RequestOptions options) => MessageHelper.ModifyAsync(this, Discord, func, options); - public Task AddReactionAsync(Emoji emoji, RequestOptions options) + public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task AddReactionAsync(string emoji, RequestOptions options) + public Task AddReactionAsync(string emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit, ulong? afterUserId, RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 4d097fb32..3205f2d7a 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -126,14 +126,14 @@ namespace Discord.WebSocket public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); - public Task AddReactionAsync(Emoji emoji, RequestOptions options) + public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task AddReactionAsync(string emoji, RequestOptions options) + public Task AddReactionAsync(string emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) From 676fbbcd236cd2c85c4baf1b0c7861a57c7c4e8e Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 22:23:01 -0500 Subject: [PATCH 06/11] Fix invoking reaction events twice --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 2a70824d4..53deeffef 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1318,6 +1318,7 @@ namespace Discord.WebSocket { cachedMsg.AddReaction(reaction); await _reactionAddedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + return; } await _reactionAddedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); } @@ -1342,6 +1343,7 @@ namespace Discord.WebSocket { cachedMsg.RemoveReaction(reaction); await _reactionRemovedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + return; } await _reactionRemovedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); } From 8dfc6aea67de6f296b69f73ee0325d134cecde48 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 6 Nov 2016 13:33:37 -0500 Subject: [PATCH 07/11] Add optional user/message/channel objects to Reactions --- src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs | 2 +- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 7 +++++-- .../Entities/Messages/SocketReaction.cs | 12 ++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 602a790a5..8caaa2532 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -35,7 +35,7 @@ namespace Discord.Rest public static async Task RemoveReactionAsync(IMessage msg, IUser user, string emoji, BaseDiscordClient client, RequestOptions options) { - await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options); + await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options).ConfigureAwait(false); } public static async Task> GetReactionUsersAsync(IMessage msg, string emoji, diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 53deeffef..238cdbcb1 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1313,7 +1313,9 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - SocketReaction reaction = new SocketReaction(data); + var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); + SocketReaction reaction = new SocketReaction(data, channel, Optional.Create(cachedMsg), Optional.Create(user)); + if (cachedMsg != null) { cachedMsg.AddReaction(reaction); @@ -1338,7 +1340,8 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - SocketReaction reaction = new SocketReaction(data); + var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); + SocketReaction reaction = new SocketReaction(data, channel, Optional.Create(cachedMsg), Optional.Create(user)); if (cachedMsg != null) { cachedMsg.RemoveReaction(reaction); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs index 0ac8c4342..c2e544a2c 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -8,17 +8,21 @@ namespace Discord.WebSocket { public class SocketReaction : IReaction { - internal SocketReaction(Model model) + internal SocketReaction(Model model, ISocketMessageChannel channel, Optional message, Optional user) { - UserId = model.UserId; + Channel = channel; + Message = message; MessageId = model.MessageId; - ChannelId = model.ChannelId; + User = user; + UserId = model.UserId; Emoji = Emoji.FromApi(model.Emoji); } public ulong UserId { get; private set; } + public Optional User { get; private set; } public ulong MessageId { get; private set; } - public ulong ChannelId { get; private set; } + public Optional Message { get; private set; } + public ISocketMessageChannel Channel { get; private set; } public Emoji Emoji { get; private set; } } } From 9725dcec24bb004d1effa0f1d5e29c1d4b1814f4 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 6 Nov 2016 13:34:38 -0500 Subject: [PATCH 08/11] Use ToString in converter instead of boxing-cast In cases where Discord sent a value of `id=0`, this would throw an invalid-cast, where 0u64 cannot be cast to string. --- src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs b/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs index a2e409292..fa22da656 100644 --- a/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs +++ b/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs @@ -16,7 +16,7 @@ namespace Discord.Net.Converters { object value = reader.Value; if (value != null) - return ulong.Parse((string)value, NumberStyles.None, CultureInfo.InvariantCulture); + return ulong.Parse(value.ToString(), NumberStyles.None, CultureInfo.InvariantCulture); else return null; } From 6b5a4b3ee69709e9396af14a777a787b362f1c16 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 11 Nov 2016 15:52:14 -0500 Subject: [PATCH 09/11] Support the "Clear Reactions" endpoint --- src/Discord.Net.Core/API/DiscordRestApiClient.cs | 11 +++++++++++ src/Discord.Net.Core/Entities/Messages/IUserMessage.cs | 2 ++ src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs | 5 +++++ src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs | 3 +++ src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs | 3 +++ .../Entities/Messages/SocketUserMessage.cs | 3 +++ 6 files changed, 27 insertions(+) diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index 4d85c68b2..6b5cbb484 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -536,6 +536,17 @@ namespace Discord.API await SendAsync("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}/{userId}", ids, options: options).ConfigureAwait(false); } + public async Task RemoveAllReactionsAsync(ulong channelId, ulong messageId, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(channelId: channelId); + + await SendAsync("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions", ids, options: options).ConfigureAwait(false); + } public async Task> GetReactionUsersAsync(ulong channelId, ulong messageId, string emoji, GetReactionUsersParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 5fe0ccbea..ba2a00bd8 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -25,6 +25,8 @@ namespace Discord Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null); /// Removes a reaction from this message. Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null); + /// Removes all reactions from this message. + Task RemoveAllReactionsAsync(RequestOptions options = null); Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null); /// Transforms this message's text into a human readable form by resolving its tags. diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 8caaa2532..33df3631c 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -38,6 +38,11 @@ namespace Discord.Rest await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options).ConfigureAwait(false); } + public static async Task RemoveAllReactionsAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) + { + await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options); + } + public static async Task> GetReactionUsersAsync(IMessage msg, string emoji, Action func, BaseDiscordClient client, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 1313235a2..dde1c9d34 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -142,6 +142,9 @@ namespace Discord.Rest => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index b2e6b40e3..476a1ed25 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -111,6 +111,9 @@ namespace Discord.Rpc => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit, ulong? afterUserId, RequestOptions options = null) => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 3205f2d7a..43e61f670 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -136,6 +136,9 @@ namespace Discord.WebSocket public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); + public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); From 983b50120daa5d9d472e889b2df8e42c9ef69d18 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 11 Nov 2016 16:28:07 -0500 Subject: [PATCH 10/11] Support "MESSAGE_REACTION_REMOVE_ALL" dispatch --- .../API/Gateway/RemoveAllReactionsEvent.cs | 12 +++++++++ .../DiscordSocketClient.Events.cs | 6 +++++ src/Discord.Net.WebSocket/DiscordSocketClient.cs | 29 ++++++++++++++++++++-- .../Entities/Messages/SocketUserMessage.cs | 4 +++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs new file mode 100644 index 000000000..944a6e7c9 --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Gateway +{ + public class RemoveAllReactionsEvent + { + [JsonProperty("channel_id")] + public ulong ChannelId { get; set; } + [JsonProperty("message_id")] + public ulong MessageId { get; set; } + } +} diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 4454e619f..a150a6d15 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -83,6 +83,12 @@ namespace Discord.WebSocket remove { _reactionRemovedEvent.Remove(value); } } private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, Task> ReactionsCleared + { + add { _reactionsClearedEvent.Add(value); } + remove { _reactionsClearedEvent.Remove(value); } + } + private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 238cdbcb1..ebe7c6835 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1306,7 +1306,7 @@ namespace Discord.WebSocket break; case "MESSAGE_REACTION_ADD": { - await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; @@ -1333,7 +1333,7 @@ namespace Discord.WebSocket } case "MESSAGE_REACTION_REMOVE": { - await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; @@ -1357,6 +1357,31 @@ namespace Discord.WebSocket } break; } + case "MESSAGE_REACTION_REMOVE_ALL": + { + await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_ALL)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; + if (channel != null) + { + SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + if (cachedMsg != null) + { + cachedMsg.ClearReactions(); + await _reactionsClearedEvent.InvokeAsync(data.MessageId, cachedMsg).ConfigureAwait(false); + return; + } + await _reactionsClearedEvent.InvokeAsync(data.MessageId, Optional.Create()); + } + else + { + await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE_ALL referenced an unknown channel.").ConfigureAwait(false); + return; + } + + break; + } case "MESSAGE_DELETE_BULK": { await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 43e61f670..cd65f4513 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -122,6 +122,10 @@ namespace Discord.WebSocket if (_reactions.Contains(reaction)) _reactions.Remove(reaction); } + internal void ClearReactions() + { + _reactions.Clear(); + } public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); From 8222eaff8654b927dfe44afe4c5237c1d0cf8f06 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 18:27:20 -0500 Subject: [PATCH 11/11] Add permissions for reactions --- src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs | 1 + src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs | 4 +++- src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs | 1 + src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs | 6 ++++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs index 5bedfbfae..7698390f1 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs @@ -11,6 +11,7 @@ //ManageGuild = 5, //Text + AddReactions = 6, ReadMessages = 10, SendMessages = 11, SendTTSMessages = 12, diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index bada89a32..06635feed 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -10,7 +10,7 @@ namespace Discord //TODO: C#7 Candidate for binary literals private static ChannelPermissions _allDM { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001011100110000000000", 2)); private static ChannelPermissions _allVoice { get; } = new ChannelPermissions(Convert.ToUInt64("00010011111100000000000000010001", 2)); - private static ChannelPermissions _allText { get; } = new ChannelPermissions(Convert.ToUInt64("00010000000001111111110000010001", 2)); + private static ChannelPermissions _allText { get; } = new ChannelPermissions(Convert.ToUInt64("00010000000001111111110001010001", 2)); private static ChannelPermissions _allGroup { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001111110110000000000", 2)); /// Gets a blank ChannelPermissions that grants no permissions. @@ -35,6 +35,8 @@ namespace Discord /// If True, a user may create, delete and modify this channel. public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannel); + /// If true, a user may add reactions. + public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions); /// If True, a user may join channels. public bool ReadMessages => Permissions.GetValue(RawValue, ChannelPermission.ReadMessages); /// If True, a user may send messages. diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs index e74a4da49..3975c1b8b 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs @@ -11,6 +11,7 @@ ManageGuild = 5, //Text + AddReactions = 6, ReadMessages = 10, SendMessages = 11, SendTTSMessages = 12, diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index 5941fde97..92fca96bd 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -11,7 +11,7 @@ namespace Discord public static readonly GuildPermissions None = new GuildPermissions(); /// Gets a GuildPermissions that grants all permissions. //TODO: C#7 Candidate for binary literals - public static readonly GuildPermissions All = new GuildPermissions(Convert.ToUInt64("01111111111100111111110000111111", 2)); + public static readonly GuildPermissions All = new GuildPermissions(Convert.ToUInt64("01111111111100111111110001111111", 2)); /// Gets a packed value representing all the permissions in this GuildPermissions. public ulong RawValue { get; } @@ -28,7 +28,9 @@ namespace Discord public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels); /// If True, a user may adjust guild properties. public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild); - + + /// If true, a user may add reactions. + public bool AddReactions => Permissions.GetValue(RawValue, GuildPermission.AddReactions); /// If True, a user may join channels. public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages); /// If True, a user may send messages.