Browse Source

Merge branch 'feature/reactions' into dev

tags/1.0-rc
RogueException 8 years ago
parent
commit
6100aa93ab
25 changed files with 417 additions and 7 deletions
  1. +1
    -1
      src/Discord.Net.Core/API/Common/Emoji.cs
  2. +2
    -0
      src/Discord.Net.Core/API/Common/Message.cs
  3. +18
    -0
      src/Discord.Net.Core/API/Common/Reaction.cs
  4. +53
    -0
      src/Discord.Net.Core/API/DiscordRestApiClient.cs
  5. +8
    -0
      src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs
  6. +1
    -1
      src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs
  7. +5
    -0
      src/Discord.Net.Core/Entities/Messages/Emoji.cs
  8. +12
    -0
      src/Discord.Net.Core/Entities/Messages/IReaction.cs
  9. +17
    -1
      src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
  10. +1
    -0
      src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs
  11. +3
    -1
      src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
  12. +1
    -0
      src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
  13. +4
    -2
      src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
  14. +1
    -1
      src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs
  15. +1
    -0
      src/Discord.Net.Core/Utils/Preconditions.cs
  16. +28
    -0
      src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
  17. +22
    -0
      src/Discord.Net.Rest/Entities/Messages/RestReaction.cs
  18. +33
    -0
      src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
  19. +17
    -0
      src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs
  20. +20
    -0
      src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs
  21. +12
    -0
      src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs
  22. +18
    -0
      src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs
  23. +78
    -0
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  24. +28
    -0
      src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs
  25. +33
    -0
      src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs

+ 1
- 1
src/Discord.Net.Core/API/Common/Emoji.cs View File

@@ -6,7 +6,7 @@ namespace Discord.API
public class Emoji public class Emoji
{ {
[JsonProperty("id")] [JsonProperty("id")]
public ulong Id { get; set; }
public ulong? Id { get; set; }
[JsonProperty("name")] [JsonProperty("name")]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty("roles")] [JsonProperty("roles")]


+ 2
- 0
src/Discord.Net.Core/API/Common/Message.cs View File

@@ -36,5 +36,7 @@ namespace Discord.API
public Optional<Embed[]> Embeds { get; set; } public Optional<Embed[]> Embeds { get; set; }
[JsonProperty("pinned")] [JsonProperty("pinned")]
public Optional<bool> Pinned { get; set; } public Optional<bool> Pinned { get; set; }
[JsonProperty("reactions")]
public Optional<Reaction[]> Reactions { get; set; }
} }
} }

+ 18
- 0
src/Discord.Net.Core/API/Common/Reaction.cs View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Discord.API
{
public class Reaction
{
[JsonProperty("count")]
public int Count { get; set; }
[JsonProperty("me")]
public bool Me { get; set; }
[JsonProperty("emoji")]
public Emoji Emoji { get; set; }
}
}

+ 53
- 0
src/Discord.Net.Core/API/DiscordRestApiClient.cs View File

@@ -515,6 +515,59 @@ namespace Discord.API
var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);
return await SendJsonAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); return await SendJsonAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, 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 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<IReadOnlyCollection<User>> 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<Func<string>> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}";
return await SendAsync<IReadOnlyCollection<User>>("GET", endpoint, ids, options: options).ConfigureAwait(false);
}
public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));


+ 8
- 0
src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs View File

@@ -0,0 +1,8 @@
namespace Discord.API.Rest
{
public class GetReactionUsersParams
{
public Optional<int> Limit { get; set; }
public Optional<ulong> AfterUserId { get; set; }
}
}

+ 1
- 1
src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs View File

@@ -24,7 +24,7 @@ namespace Discord
} }
internal static GuildEmoji Create(Model model) 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; public override string ToString() => Name;


+ 5
- 0
src/Discord.Net.Core/Entities/Messages/Emoji.cs View File

@@ -19,6 +19,11 @@ namespace Discord
Name = name; Name = name;
} }


internal static Emoji FromApi(API.Emoji emoji)
{
return new Emoji(emoji.Id.GetValueOrDefault(), emoji.Name);
}

public static Emoji Parse(string text) public static Emoji Parse(string text)
{ {
Emoji result; Emoji result;


+ 12
- 0
src/Discord.Net.Core/Entities/Messages/IReaction.cs View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Discord
{
public interface IReaction
{
Emoji Emoji { get; }
}
}

+ 17
- 1
src/Discord.Net.Core/Entities/Messages/IUserMessage.cs View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;


namespace Discord namespace Discord
@@ -11,7 +12,22 @@ namespace Discord
Task PinAsync(RequestOptions options = null); Task PinAsync(RequestOptions options = null);
/// <summary> Removes this message from its channel's pinned messages. </summary> /// <summary> Removes this message from its channel's pinned messages. </summary>
Task UnpinAsync(RequestOptions options = null); Task UnpinAsync(RequestOptions options = null);

/// <summary> Returns all reactions included in this message. </summary>
IReadOnlyDictionary<Emoji, int> Reactions { get; }

/// <summary> Adds a reaction to this message. </summary>
Task AddReactionAsync(Emoji emoji, RequestOptions options = null);
/// <summary> Adds a reaction to this message. </summary>
Task AddReactionAsync(string emoji, RequestOptions options = null);
/// <summary> Removes a reaction from message. </summary>
Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null);
/// <summary> Removes a reaction from this message. </summary>
Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null);
/// <summary> Removes all reactions from this message. </summary>
Task RemoveAllReactionsAsync(RequestOptions options = null);
Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null);

/// <summary> Transforms this message's text into a human readable form by resolving its tags. </summary> /// <summary> Transforms this message's text into a human readable form by resolving its tags. </summary>
string Resolve( string Resolve(
TagHandling userHandling = TagHandling.Name, TagHandling userHandling = TagHandling.Name,


+ 1
- 0
src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs View File

@@ -11,6 +11,7 @@
//ManageGuild = 5, //ManageGuild = 5,


//Text //Text
AddReactions = 6,
ReadMessages = 10, ReadMessages = 10,
SendMessages = 11, SendMessages = 11,
SendTTSMessages = 12, SendTTSMessages = 12,


+ 3
- 1
src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs View File

@@ -10,7 +10,7 @@ namespace Discord
//TODO: C#7 Candidate for binary literals //TODO: C#7 Candidate for binary literals
private static ChannelPermissions _allDM { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001011100110000000000", 2)); 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 _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)); private static ChannelPermissions _allGroup { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001111110110000000000", 2));


/// <summary> Gets a blank ChannelPermissions that grants no permissions. </summary> /// <summary> Gets a blank ChannelPermissions that grants no permissions. </summary>
@@ -35,6 +35,8 @@ namespace Discord
/// <summary> If True, a user may create, delete and modify this channel. </summary> /// <summary> If True, a user may create, delete and modify this channel. </summary>
public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannel); public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannel);


/// <summary> If true, a user may add reactions. </summary>
public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions);
/// <summary> If True, a user may join channels. </summary> /// <summary> If True, a user may join channels. </summary>
public bool ReadMessages => Permissions.GetValue(RawValue, ChannelPermission.ReadMessages); public bool ReadMessages => Permissions.GetValue(RawValue, ChannelPermission.ReadMessages);
/// <summary> If True, a user may send messages. </summary> /// <summary> If True, a user may send messages. </summary>


+ 1
- 0
src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs View File

@@ -11,6 +11,7 @@
ManageGuild = 5, ManageGuild = 5,


//Text //Text
AddReactions = 6,
ReadMessages = 10, ReadMessages = 10,
SendMessages = 11, SendMessages = 11,
SendTTSMessages = 12, SendTTSMessages = 12,


+ 4
- 2
src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs View File

@@ -11,7 +11,7 @@ namespace Discord
public static readonly GuildPermissions None = new GuildPermissions(); public static readonly GuildPermissions None = new GuildPermissions();
/// <summary> Gets a GuildPermissions that grants all permissions. </summary> /// <summary> Gets a GuildPermissions that grants all permissions. </summary>
//TODO: C#7 Candidate for binary literals //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));


/// <summary> Gets a packed value representing all the permissions in this GuildPermissions. </summary> /// <summary> Gets a packed value representing all the permissions in this GuildPermissions. </summary>
public ulong RawValue { get; } public ulong RawValue { get; }
@@ -28,7 +28,9 @@ namespace Discord
public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels); public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels);
/// <summary> If True, a user may adjust guild properties. </summary> /// <summary> If True, a user may adjust guild properties. </summary>
public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild); public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild);

/// <summary> If true, a user may add reactions. </summary>
public bool AddReactions => Permissions.GetValue(RawValue, GuildPermission.AddReactions);
/// <summary> If True, a user may join channels. </summary> /// <summary> If True, a user may join channels. </summary>
public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages); public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages);
/// <summary> If True, a user may send messages. </summary> /// <summary> If True, a user may send messages. </summary>


+ 1
- 1
src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs View File

@@ -16,7 +16,7 @@ namespace Discord.Net.Converters
{ {
object value = reader.Value; object value = reader.Value;
if (value != null) if (value != null)
return ulong.Parse((string)value, NumberStyles.None, CultureInfo.InvariantCulture);
return ulong.Parse(value.ToString(), NumberStyles.None, CultureInfo.InvariantCulture);
else else
return null; return null;
} }


+ 1
- 0
src/Discord.Net.Core/Utils/Preconditions.cs View File

@@ -43,6 +43,7 @@ namespace Discord
if (obj.Value.Trim().Length == 0) throw CreateNotEmptyException(name, msg); if (obj.Value.Trim().Length == 0) throw CreateNotEmptyException(name, msg);
} }
} }


private static ArgumentException CreateNotEmptyException(string name, string msg) private static ArgumentException CreateNotEmptyException(string name, string msg)
{ {


+ 28
- 0
src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs View File

@@ -28,6 +28,34 @@ namespace Discord.Rest
await client.ApiClient.DeleteMessageAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); 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).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<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IMessage msg, string emoji,
Action<GetReactionUsersParams> 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, public static async Task PinAsync(IMessage msg, BaseDiscordClient client,
RequestOptions options) RequestOptions options)
{ {


+ 22
- 0
src/Discord.Net.Rest/Entities/Messages/RestReaction.cs View File

@@ -0,0 +1,22 @@
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 = Emoji.FromApi(model.Emoji);
Count = model.Count;
Me = model.Me;
}

public Emoji Emoji { get; private set; }
public int Count { get; private set; }
public bool Me { get; private set; }
}
}

+ 33
- 0
src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs View File

@@ -17,6 +17,7 @@ namespace Discord.Rest
private ImmutableArray<Attachment> _attachments; private ImmutableArray<Attachment> _attachments;
private ImmutableArray<Embed> _embeds; private ImmutableArray<Embed> _embeds;
private ImmutableArray<ITag> _tags; private ImmutableArray<ITag> _tags;
private ImmutableArray<RestReaction> _reactions;
public override bool IsTTS => _isTTS; public override bool IsTTS => _isTTS;
public override bool IsPinned => _isPinned; public override bool IsPinned => _isPinned;
@@ -28,6 +29,7 @@ namespace Discord.Rest
public override IReadOnlyCollection<ulong> MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection<ulong> MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags);
public override IReadOnlyCollection<RestUser> MentionedUsers => MessageHelper.FilterTagsByValue<RestUser>(TagType.UserMention, _tags); public override IReadOnlyCollection<RestUser> MentionedUsers => MessageHelper.FilterTagsByValue<RestUser>(TagType.UserMention, _tags);
public override IReadOnlyCollection<ITag> Tags => _tags; public override IReadOnlyCollection<ITag> Tags => _tags;
public IReadOnlyDictionary<Emoji, int> Reactions => _reactions.ToDictionary(x => x.Emoji, x => x.Count);


internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author) internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author)
: base(discord, id, channel, author) : base(discord, id, channel, author)
@@ -100,6 +102,20 @@ namespace Discord.Rest
} }
} }


if (model.Reactions.IsSpecified)
{
var value = model.Reactions.Value;
if (value.Length > 0)
{
var reactions = ImmutableArray.CreateBuilder<RestReaction>(value.Length);
for (int i = 0; i < value.Length; i++)
reactions.Add(new RestReaction(value[i]));
_reactions = reactions.ToImmutable();
}
else
_reactions = ImmutableArray.Create<RestReaction>();
}

if (model.Content.IsSpecified) if (model.Content.IsSpecified)
{ {
var text = model.Content.Value; var text = model.Content.Value;
@@ -116,6 +132,23 @@ namespace Discord.Rest
Update(model); Update(model);
} }


public Task AddReactionAsync(Emoji emoji, RequestOptions options = null)
=> MessageHelper.AddReactionAsync(this, emoji, Discord, options);
public Task AddReactionAsync(string emoji, RequestOptions options = null)
=> MessageHelper.AddReactionAsync(this, emoji, Discord, 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 = null)
=> MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options);

public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options);
public Task<IReadOnlyCollection<IUser>> 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<ulong>(); }, Discord, options);

public Task PinAsync(RequestOptions options) public Task PinAsync(RequestOptions options)
=> MessageHelper.PinAsync(this, Discord, options); => MessageHelper.PinAsync(this, Discord, options);
public Task UnpinAsync(RequestOptions options) public Task UnpinAsync(RequestOptions options)


+ 17
- 0
src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs View File

@@ -30,6 +30,7 @@ namespace Discord.Rpc
public override IReadOnlyCollection<ulong> MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection<ulong> MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags);
public override IReadOnlyCollection<ulong> MentionedUserIds => MessageHelper.FilterTagsByKey(TagType.UserMention, _tags); public override IReadOnlyCollection<ulong> MentionedUserIds => MessageHelper.FilterTagsByKey(TagType.UserMention, _tags);
public override IReadOnlyCollection<ITag> Tags => _tags; public override IReadOnlyCollection<ITag> Tags => _tags;
public IReadOnlyDictionary<Emoji, int> Reactions => ImmutableDictionary.Create<Emoji, int>();


internal RpcUserMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) internal RpcUserMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author)
: base(discord, id, channel, author) : base(discord, id, channel, author)
@@ -101,6 +102,22 @@ namespace Discord.Rpc
public Task ModifyAsync(Action<ModifyMessageParams> func, RequestOptions options) public Task ModifyAsync(Action<ModifyMessageParams> func, RequestOptions options)
=> MessageHelper.ModifyAsync(this, Discord, func, options); => MessageHelper.ModifyAsync(this, Discord, func, options);


public Task AddReactionAsync(Emoji emoji, RequestOptions options = null)
=> MessageHelper.AddReactionAsync(this, emoji, Discord, options);
public Task AddReactionAsync(string emoji, RequestOptions options = null)
=> MessageHelper.AddReactionAsync(this, emoji, Discord, 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 = null)
=> MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options);

public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options);
public Task<IReadOnlyCollection<IUser>> 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<ulong>(); }, Discord, options);

public Task PinAsync(RequestOptions options) public Task PinAsync(RequestOptions options)
=> MessageHelper.PinAsync(this, Discord, options); => MessageHelper.PinAsync(this, Discord, options);
public Task UnpinAsync(RequestOptions options) public Task UnpinAsync(RequestOptions options)


+ 20
- 0
src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs View File

@@ -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
{
[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 Emoji Emoji { get; set; }
}
}

+ 12
- 0
src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs View File

@@ -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; }
}
}

+ 18
- 0
src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs View File

@@ -71,6 +71,24 @@ namespace Discord.WebSocket
remove { _messageUpdatedEvent.Remove(value); } remove { _messageUpdatedEvent.Remove(value); }
} }
private readonly AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, Task>>(); private readonly AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, Task>>();
public event Func<ulong, Optional<SocketUserMessage>, SocketReaction, Task> ReactionAdded
{
add { _reactionAddedEvent.Add(value); }
remove { _reactionAddedEvent.Remove(value); }
}
private readonly AsyncEvent<Func<ulong, Optional<SocketUserMessage>, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent<Func<ulong, Optional<SocketUserMessage>, SocketReaction, Task>>();
public event Func<ulong, Optional<SocketUserMessage>, SocketReaction, Task> ReactionRemoved
{
add { _reactionRemovedEvent.Add(value); }
remove { _reactionRemovedEvent.Remove(value); }
}
private readonly AsyncEvent<Func<ulong, Optional<SocketUserMessage>, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent<Func<ulong, Optional<SocketUserMessage>, SocketReaction, Task>>();
public event Func<ulong, Optional<SocketUserMessage>, Task> ReactionsCleared
{
add { _reactionsClearedEvent.Add(value); }
remove { _reactionsClearedEvent.Remove(value); }
}
private readonly AsyncEvent<Func<ulong, Optional<SocketUserMessage>, Task>> _reactionsClearedEvent = new AsyncEvent<Func<ulong, Optional<SocketUserMessage>, Task>>();


//Roles //Roles
public event Func<SocketRole, Task> RoleCreated public event Func<SocketRole, Task> RoleCreated


+ 78
- 0
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -1308,6 +1308,84 @@ namespace Discord.WebSocket
} }
} }
break; break;
case "MESSAGE_REACTION_ADD":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<GatewayReaction>(_serializer);
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
if (channel != null)
{
SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
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);
await _reactionAddedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false);
return;
}
await _reactionAddedEvent.InvokeAsync(data.MessageId, Optional.Create<SocketUserMessage>(), 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 Dispatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<GatewayReaction>(_serializer);
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
if (channel != null)
{
SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
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);
await _reactionRemovedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false);
return;
}
await _reactionRemovedEvent.InvokeAsync(data.MessageId, Optional.Create<SocketUserMessage>(), reaction).ConfigureAwait(false);
}
else
{
await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE referenced an unknown channel.").ConfigureAwait(false);
return;
}
break;
}
case "MESSAGE_REACTION_REMOVE_ALL":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_ALL)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<RemoveAllReactionsEvent>(_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<SocketUserMessage>());
}
else
{
await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE_ALL referenced an unknown channel.").ConfigureAwait(false);
return;
}

break;
}
case "MESSAGE_DELETE_BULK": case "MESSAGE_DELETE_BULK":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);


+ 28
- 0
src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs View File

@@ -0,0 +1,28 @@
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, ISocketMessageChannel channel, Optional<SocketUserMessage> message, Optional<IUser> user)
{
Channel = channel;
Message = message;
MessageId = model.MessageId;
User = user;
UserId = model.UserId;
Emoji = Emoji.FromApi(model.Emoji);
}

public ulong UserId { get; private set; }
public Optional<IUser> User { get; private set; }
public ulong MessageId { get; private set; }
public Optional<SocketUserMessage> Message { get; private set; }
public ISocketMessageChannel Channel { get; private set; }
public Emoji Emoji { get; private set; }
}
}

+ 33
- 0
src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs View File

@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq;
using Discord.API.Gateway;
using Model = Discord.API.Message; using Model = Discord.API.Message;


namespace Discord.WebSocket namespace Discord.WebSocket
@@ -18,6 +20,7 @@ namespace Discord.WebSocket
private ImmutableArray<Attachment> _attachments; private ImmutableArray<Attachment> _attachments;
private ImmutableArray<Embed> _embeds; private ImmutableArray<Embed> _embeds;
private ImmutableArray<ITag> _tags; private ImmutableArray<ITag> _tags;
private List<SocketReaction> _reactions = new List<SocketReaction>();
public override bool IsTTS => _isTTS; public override bool IsTTS => _isTTS;
public override bool IsPinned => _isPinned; public override bool IsPinned => _isPinned;
@@ -29,6 +32,7 @@ namespace Discord.WebSocket
public override IReadOnlyCollection<SocketGuildChannel> MentionedChannels => MessageHelper.FilterTagsByValue<SocketGuildChannel>(TagType.ChannelMention, _tags); public override IReadOnlyCollection<SocketGuildChannel> MentionedChannels => MessageHelper.FilterTagsByValue<SocketGuildChannel>(TagType.ChannelMention, _tags);
public override IReadOnlyCollection<SocketRole> MentionedRoles => MessageHelper.FilterTagsByValue<SocketRole>(TagType.RoleMention, _tags); public override IReadOnlyCollection<SocketRole> MentionedRoles => MessageHelper.FilterTagsByValue<SocketRole>(TagType.RoleMention, _tags);
public override IReadOnlyCollection<SocketUser> MentionedUsers => MessageHelper.FilterTagsByValue<SocketUser>(TagType.UserMention, _tags); public override IReadOnlyCollection<SocketUser> MentionedUsers => MessageHelper.FilterTagsByValue<SocketUser>(TagType.UserMention, _tags);
public IReadOnlyDictionary<Emoji, int> Reactions => _reactions.GroupBy(r => r.Emoji).ToDictionary(x => x.Key, x => x.Count());


internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author)
: base(discord, id, channel, author) : base(discord, id, channel, author)
@@ -109,10 +113,39 @@ namespace Discord.WebSocket
model.Content = text; model.Content = text;
} }
} }
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 ModifyAsync(Action<ModifyMessageParams> func, RequestOptions options = null) public Task ModifyAsync(Action<ModifyMessageParams> func, RequestOptions options = null)
=> MessageHelper.ModifyAsync(this, Discord, func, options); => MessageHelper.ModifyAsync(this, Discord, func, options);


public Task AddReactionAsync(Emoji emoji, RequestOptions options = null)
=> MessageHelper.AddReactionAsync(this, emoji, Discord, options);
public Task AddReactionAsync(string emoji, RequestOptions options = null)
=> MessageHelper.AddReactionAsync(this, emoji, Discord, 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 = null)
=> MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options);

public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options);

public Task<IReadOnlyCollection<IUser>> 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<ulong>(); }, Discord, options);

public Task PinAsync(RequestOptions options = null) public Task PinAsync(RequestOptions options = null)
=> MessageHelper.PinAsync(this, Discord, options); => MessageHelper.PinAsync(this, Discord, options);
public Task UnpinAsync(RequestOptions options = null) public Task UnpinAsync(RequestOptions options = null)


Loading…
Cancel
Save