Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Add MESSAGE_REACTION_REMOVE_EMOJI and RemoveAllReactionsForEmoteAsync #1544

Merged
merged 2 commits into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/Discord.Net.Core/Entities/Messages/IMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ public interface IMessage : ISnowflakeEntity, IDeletable
/// A task that represents the asynchronous removal operation.
/// </returns>
Task RemoveAllReactionsAsync(RequestOptions options = null);
/// <summary>
/// Removes all reactions with a specific emoji from this message.
/// </summary>
/// <param name="emote">The emoji used to react to this message.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous removal operation.
/// </returns>
Task RemoveAllReactionsForEmoteAsync(IEmote emote, RequestOptions options = null);

/// <summary>
/// Gets all users that reacted to a message with a given emote.
Expand Down
12 changes: 12 additions & 0 deletions src/Discord.Net.Rest/DiscordRestApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,18 @@ public async Task RemoveAllReactionsAsync(ulong channelId, ulong messageId, Requ

await SendAsync("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions", ids, options: options).ConfigureAwait(false);
}
public async Task RemoveAllReactionsForEmoteAsync(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("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}", 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));
Expand Down
5 changes: 5 additions & 0 deletions src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public static async Task RemoveAllReactionsAsync(IMessage msg, BaseDiscordClient
await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false);
}

public static async Task RemoveAllReactionsForEmoteAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options)
{
await client.ApiClient.RemoveAllReactionsForEmoteAsync(msg.Channel.Id, msg.Id, emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name, options).ConfigureAwait(false);
}

public static IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IMessage msg, IEmote emote,
int? limit, BaseDiscordClient client, RequestOptions options)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions optio
public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options);
/// <inheritdoc />
public Task RemoveAllReactionsForEmoteAsync(IEmote emote, RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsForEmoteAsync(this, emote, Discord, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Newtonsoft.Json;

namespace Discord.API.Gateway
{
internal class RemoveAllReactionsForEmoteEvent
{
[JsonProperty("channel_id")]
public ulong ChannelId { get; set; }
[JsonProperty("guild_id")]
public Optional<ulong> GuildId { get; set; }
[JsonProperty("message_id")]
public ulong MessageId { get; set; }
[JsonProperty("emoji")]
public Emoji Emoji { get; set; }
}
}
22 changes: 22 additions & 0 deletions src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,28 @@ public event Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, Task> R
remove { _reactionsClearedEvent.Remove(value); }
}
internal readonly AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, Task>>();
/// <summary>
/// Fired when all reactions to a message with a specific emote are removed.
/// </summary>
/// <remarks>
/// <para>
/// This event is fired when all reactions to a message with a specific emote are removed.
/// The event handler must return a <see cref="Task"/> and accept a <see cref="ISocketMessageChannel"/> and
/// a <see cref="IEmote"/> as its parameters.
/// </para>
/// <para>
/// The channel where this message was sent will be passed into the <see cref="ISocketMessageChannel"/> parameter.
/// </para>
/// <para>
/// The emoji that all reactions had and were removed will be passed into the <see cref="IEmote"/> parameter.
/// </para>
/// </remarks>
public event Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, IEmote, Task> ReactionsRemovedForEmote
{
add { _reactionsRemovedForEmoteEvent.Add(value); }
remove { _reactionsRemovedForEmoteEvent.Remove(value); }
}
internal readonly AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, IEmote, Task>> _reactionsRemovedForEmoteEvent = new AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, IEmote, Task>>();

//Roles
/// <summary> Fired when a role is created. </summary>
Expand Down
1 change: 1 addition & 0 deletions src/Discord.Net.WebSocket/DiscordShardedClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ private void RegisterEvents(DiscordSocketClient client, bool isPrimary)
client.ReactionAdded += (cache, channel, reaction) => _reactionAddedEvent.InvokeAsync(cache, channel, reaction);
client.ReactionRemoved += (cache, channel, reaction) => _reactionRemovedEvent.InvokeAsync(cache, channel, reaction);
client.ReactionsCleared += (cache, channel) => _reactionsClearedEvent.InvokeAsync(cache, channel);
client.ReactionsRemovedForEmote += (cache, channel, emote) => _reactionsRemovedForEmoteEvent.InvokeAsync(cache, channel, emote);

client.RoleCreated += (role) => _roleCreatedEvent.InvokeAsync(role);
client.RoleDeleted += (role) => _roleDeletedEvent.InvokeAsync(role);
Expand Down
28 changes: 28 additions & 0 deletions src/Discord.Net.WebSocket/DiscordSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,34 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty
}
}
break;
case "MESSAGE_REACTION_REMOVE_EMOJI":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_EMOJI)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<API.Gateway.RemoveAllReactionsForEmoteEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
bool isCached = cachedMsg != null;

var optionalMsg = !isCached
? Optional.Create<SocketUserMessage>()
: Optional.Create(cachedMsg);

var cacheable = new Cacheable<IUserMessage, ulong>(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage);
var emote = data.Emoji.ToIEmote();

cachedMsg?.RemoveAllReactionsForEmoteAsync(emote);

await TimedInvokeAsync(_reactionsRemovedForEmoteEvent, nameof(ReactionsRemovedForEmote), cacheable, channel, emote).ConfigureAwait(false);
}
else
{
await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
return;
}
}
break;
case "MESSAGE_DELETE_BULK":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);
Expand Down
7 changes: 7 additions & 0 deletions src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ internal void ClearReactions()
{
_reactions.Clear();
}
internal void RemoveReactionsForEmote(IEmote emote)
{
_reactions.RemoveAll(x => x.Emote.Equals(emote));
}

/// <inheritdoc />
public Task AddReactionAsync(IEmote emote, RequestOptions options = null)
Expand All @@ -214,6 +218,9 @@ public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions optio
public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options);
/// <inheritdoc />
public Task RemoveAllReactionsForEmoteAsync(IEmote emote, RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsForEmoteAsync(this, emote, Discord, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options);
}
Expand Down