diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs index 36fa602a7..1eba1e076 100644 --- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; namespace Discord { @@ -138,5 +139,89 @@ namespace Discord /// A message's application, if any is associated. /// MessageApplication Application { get; } + + /// + /// Gets all reactions included in this message. + /// + IReadOnlyDictionary Reactions { get; } + + /// + /// Adds a reaction to this message. + /// + /// + /// The following example adds the reaction, 💕, to the message. + /// + /// await msg.AddReactionAsync(new Emoji("\U0001f495")); + /// + /// + /// The emoji used to react to this message. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous operation for adding a reaction to this message. + /// + /// + Task AddReactionAsync(IEmote emote, RequestOptions options = null); + /// + /// Removes a reaction from message. + /// + /// + /// The following example removes the reaction, 💕, added by the message author from the message. + /// + /// await msg.RemoveReactionAsync(new Emoji("\U0001f495"), msg.Author); + /// + /// + /// The emoji used to react to this message. + /// The user that added the emoji. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous operation for removing a reaction to this message. + /// + /// + Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null); + /// + /// Removes a reaction from message. + /// + /// + /// The following example removes the reaction, 💕, added by the user with ID 84291986575613952 from the message. + /// + /// await msg.RemoveReactionAsync(new Emoji("\U0001f495"), 84291986575613952); + /// + /// + /// The emoji used to react to this message. + /// The ID of the user that added the emoji. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous operation for removing a reaction to this message. + /// + /// + Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions options = null); + /// + /// Removes all reactions from this message. + /// + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous removal operation. + /// + Task RemoveAllReactionsAsync(RequestOptions options = null); + + /// + /// Gets all users that reacted to a message with a given emote. + /// + /// + /// The following example gets the users that have reacted with the emoji 💕 to the message. + /// + /// var emoji = new Emoji("\U0001f495"); + /// var reactedUsers = await message.GetReactionUsersAsync(emoji, 100).FlattenAsync(); + /// + /// + /// The emoji that represents the reaction that you wish to get. + /// The number of users to request. + /// The options to be used when sending the request. + /// + /// A paged collection containing a read-only collection of users that has reacted to this message. + /// Flattening the paginated response into a collection of users with + /// is required if you wish to access the users. + /// + IAsyncEnumerable> GetReactionUsersAsync(IEmote emoji, int limit, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Messages/IReactionMessage.cs b/src/Discord.Net.Core/Entities/Messages/IReactionMessage.cs deleted file mode 100644 index 2919b80d1..000000000 --- a/src/Discord.Net.Core/Entities/Messages/IReactionMessage.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Discord -{ - /// - /// Represents a message where reactions can be added or removed. - /// - public interface IReactionMessage : IMessage - { - /// - /// Gets all reactions included in this message. - /// - IReadOnlyDictionary Reactions { get; } - - /// - /// Adds a reaction to this message. - /// - /// - /// The following example adds the reaction, 💕, to the message. - /// - /// await msg.AddReactionAsync(new Emoji("\U0001f495")); - /// - /// - /// The emoji used to react to this message. - /// The options to be used when sending the request. - /// - /// A task that represents the asynchronous operation for adding a reaction to this message. - /// - /// - Task AddReactionAsync(IEmote emote, RequestOptions options = null); - /// - /// Removes a reaction from message. - /// - /// - /// The following example removes the reaction, 💕, added by the message author from the message. - /// - /// await msg.RemoveReactionAsync(new Emoji("\U0001f495"), msg.Author); - /// - /// - /// The emoji used to react to this message. - /// The user that added the emoji. - /// The options to be used when sending the request. - /// - /// A task that represents the asynchronous operation for removing a reaction to this message. - /// - /// - Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null); - /// - /// Removes a reaction from message. - /// - /// - /// The following example removes the reaction, 💕, added by the user with ID 84291986575613952 from the message. - /// - /// await msg.RemoveReactionAsync(new Emoji("\U0001f495"), 84291986575613952); - /// - /// - /// The emoji used to react to this message. - /// The ID of the user that added the emoji. - /// The options to be used when sending the request. - /// - /// A task that represents the asynchronous operation for removing a reaction to this message. - /// - /// - Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions options = null); - /// - /// Removes all reactions from this message. - /// - /// The options to be used when sending the request. - /// - /// A task that represents the asynchronous removal operation. - /// - Task RemoveAllReactionsAsync(RequestOptions options = null); - - /// - /// Gets all users that reacted to a message with a given emote. - /// - /// - /// The following example gets the users that have reacted with the emoji 💕 to the message. - /// - /// var emoji = new Emoji("\U0001f495"); - /// var reactedUsers = await message.GetReactionUsersAsync(emoji, 100).FlattenAsync(); - /// - /// - /// The emoji that represents the reaction that you wish to get. - /// The number of users to request. - /// The options to be used when sending the request. - /// - /// A paged collection containing a read-only collection of users that has reacted to this message. - /// Flattening the paginated response into a collection of users with - /// is required if you wish to access the users. - /// - IAsyncEnumerable> GetReactionUsersAsync(IEmote emoji, int limit, RequestOptions options = null); - } -} diff --git a/src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs b/src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs index fc65139b1..89cd17a35 100644 --- a/src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs @@ -3,7 +3,7 @@ namespace Discord /// /// Represents a generic message sent by the system. /// - public interface ISystemMessage : IReactionMessage + public interface ISystemMessage : IMessage { } } diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index c8941e961..be2523b21 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -7,7 +7,7 @@ namespace Discord /// /// Represents a generic message sent by a user. /// - public interface IUserMessage : IReactionMessage + public interface IUserMessage : IMessage { /// /// Modifies this message. diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index fa1c91376..29a9c9bd2 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -13,6 +13,7 @@ namespace Discord.Rest public abstract class RestMessage : RestEntity, IMessage, IUpdateable { private long _timestampTicks; + private ImmutableArray _reactions = ImmutableArray.Create(); /// public IMessageChannel Channel { get; } @@ -106,6 +107,22 @@ namespace Discord.Rest PartyId = model.Activity.Value.PartyId.GetValueOrDefault() }; } + + 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(RestReaction.Create(value[i])); + _reactions = reactions.ToImmutable(); + } + else + _reactions = ImmutableArray.Create(); + } + else + _reactions = ImmutableArray.Create(); } /// @@ -135,5 +152,24 @@ namespace Discord.Rest IReadOnlyCollection IMessage.Embeds => Embeds; /// IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); + + /// + public IReadOnlyDictionary Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me }); + + /// + public Task AddReactionAsync(IEmote emote, RequestOptions options = null) + => MessageHelper.AddReactionAsync(this, emote, Discord, options); + /// + public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) + => MessageHelper.RemoveReactionAsync(this, user.Id, emote, Discord, options); + /// + public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions options = null) + => MessageHelper.RemoveReactionAsync(this, userId, emote, Discord, options); + /// + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); + /// + public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestReactionMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestReactionMessage.cs deleted file mode 100644 index 203ef17c5..000000000 --- a/src/Discord.Net.Rest/Entities/Messages/RestReactionMessage.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Model = Discord.API.Message; - -namespace Discord.Rest -{ - /// - /// Represents a REST-based mesage where reactions can be added or removed. - /// - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public abstract class RestReactionMessage : RestMessage, IReactionMessage - { - private ImmutableArray _reactions = ImmutableArray.Create(); - - /// - public IReadOnlyDictionary Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me }); - - internal RestReactionMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source) - : base(discord, id, channel, author, source) - { - } - - internal override void Update(Model model) - { - base.Update(model); - - 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(RestReaction.Create(value[i])); - _reactions = reactions.ToImmutable(); - } - else - _reactions = ImmutableArray.Create(); - } - else - _reactions = ImmutableArray.Create(); - } - - /// - public Task AddReactionAsync(IEmote emote, RequestOptions options = null) - => MessageHelper.AddReactionAsync(this, emote, Discord, options); - /// - public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) - => MessageHelper.RemoveReactionAsync(this, user.Id, emote, Discord, options); - /// - public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions options = null) - => MessageHelper.RemoveReactionAsync(this, userId, emote, Discord, options); - /// - public Task RemoveAllReactionsAsync(RequestOptions options = null) - => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); - /// - public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) - => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); - } -} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs index bfd620698..89a651eb7 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs @@ -7,7 +7,7 @@ namespace Discord.Rest /// Represents a REST-based system message. /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class RestSystemMessage : RestReactionMessage, ISystemMessage + public class RestSystemMessage : RestMessage, ISystemMessage { /// public MessageType Type { get; private set; } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index ed58e7bfd..7d652687a 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -11,7 +11,7 @@ namespace Discord.Rest /// Represents a REST-based message sent by a user. /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class RestUserMessage : RestReactionMessage, IUserMessage + public class RestUserMessage : RestMessage, IUserMessage { private bool _isMentioningEveryone, _isTTS, _isPinned, _isSuppressed; private long? _editedTimestampTicks; diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index 4d3efa656..ae42d9d61 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -14,6 +14,7 @@ namespace Discord.WebSocket public abstract class SocketMessage : SocketEntity, IMessage { private long _timestampTicks; + private readonly List _reactions = new List(); /// /// Gets the author of this message. @@ -89,6 +90,8 @@ namespace Discord.WebSocket public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); /// public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + /// + public IReadOnlyDictionary Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) }); /// public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); @@ -169,5 +172,35 @@ namespace Discord.WebSocket IReadOnlyCollection IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray(); /// IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); + + internal void AddReaction(SocketReaction reaction) + { + _reactions.Add(reaction); + } + internal void RemoveReaction(SocketReaction reaction) + { + if (_reactions.Contains(reaction)) + _reactions.Remove(reaction); + } + internal void ClearReactions() + { + _reactions.Clear(); + } + + /// + public Task AddReactionAsync(IEmote emote, RequestOptions options = null) + => MessageHelper.AddReactionAsync(this, emote, Discord, options); + /// + public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) + => MessageHelper.RemoveReactionAsync(this, user.Id, emote, Discord, options); + /// + public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions options = null) + => MessageHelper.RemoveReactionAsync(this, userId, emote, Discord, options); + /// + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); + /// + public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); } } diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReactionMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReactionMessage.cs deleted file mode 100644 index 43ebaa420..000000000 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReactionMessage.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Discord.Rest; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; - -namespace Discord.WebSocket -{ - /// - /// Represents a WebSocket-based message where reactions can be added or removed. - /// - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public abstract class SocketReactionMessage : SocketMessage, IReactionMessage - { - private readonly List _reactions = new List(); - - internal SocketReactionMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) - : base(discord, id, channel, author, source) - { - } - - /// - public IReadOnlyDictionary Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) }); - - internal void AddReaction(SocketReaction reaction) - { - _reactions.Add(reaction); - } - internal void RemoveReaction(SocketReaction reaction) - { - if (_reactions.Contains(reaction)) - _reactions.Remove(reaction); - } - internal void ClearReactions() - { - _reactions.Clear(); - } - - /// - public Task AddReactionAsync(IEmote emote, RequestOptions options = null) - => MessageHelper.AddReactionAsync(this, emote, Discord, options); - /// - public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) - => MessageHelper.RemoveReactionAsync(this, user.Id, emote, Discord, options); - /// - public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions options = null) - => MessageHelper.RemoveReactionAsync(this, userId, emote, Discord, options); - /// - public Task RemoveAllReactionsAsync(RequestOptions options = null) - => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); - /// - public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) - => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); - } -} diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs index a2bcec3b8..d0ce5025b 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs @@ -7,7 +7,7 @@ namespace Discord.WebSocket /// Represents a WebSocket-based message sent by the system. /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class SocketSystemMessage : SocketReactionMessage, ISystemMessage + public class SocketSystemMessage : SocketMessage, ISystemMessage { /// public MessageType Type { get; private set; } diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 544cebbf4..b26dfe5fb 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -13,7 +13,7 @@ namespace Discord.WebSocket /// Represents a WebSocket-based message sent by a user. /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class SocketUserMessage : SocketReactionMessage, IUserMessage + public class SocketUserMessage : SocketMessage, IUserMessage { private bool _isMentioningEveryone, _isTTS, _isPinned, _isSuppressed; private long? _editedTimestampTicks;