diff --git a/src/Discord.Net.Core/Utils/Cacheable.cs b/src/Discord.Net.Core/Utils/Cacheable.cs
new file mode 100644
index 000000000..10b61be90
--- /dev/null
+++ b/src/Discord.Net.Core/Utils/Cacheable.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ ///
+ /// Contains an entity that may be cached.
+ ///
+ /// The type of entity that is cached
+ /// The type of this entity's ID
+ public struct Cacheable
+ where TEntity : IEntity
+ where TId : IEquatable
+ {
+ ///
+ /// Is this entity cached?
+ ///
+ public bool HasValue { get; }
+ ///
+ /// The ID of this entity.
+ ///
+ public TId Id { get; }
+ ///
+ /// The entity, if it could be pulled from cache.
+ ///
+ ///
+ /// This value is not guaranteed to be set; in cases where the entity cannot be pulled from cache, it is null.
+ ///
+ public TEntity Value { get; }
+ private Func> DownloadFunc { get; }
+
+ internal Cacheable(TEntity value, TId id, bool hasValue , Func> downloadFunc)
+ {
+ Value = value;
+ Id = id;
+ HasValue = hasValue;
+ DownloadFunc = downloadFunc;
+ }
+
+ ///
+ /// Downloads this entity to cache.
+ ///
+ /// An awaitable Task containing the downloaded entity.
+ /// Thrown when used from a user account.
+ /// Thrown when the message is deleted.
+ public async Task DownloadAsync()
+ {
+ return await DownloadFunc();
+ }
+
+ ///
+ /// Returns the cached entity if it exists; otherwise downloads it.
+ ///
+ /// An awaitable Task containing a cached or downloaded entity.
+ /// Thrown when used from a user account.
+ /// Thrown when the message is deleted and is not in cache.
+ public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync();
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs
index 449d30599..874062c56 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
+using Discord.Net;
namespace Discord.WebSocket
{
@@ -33,36 +34,36 @@ namespace Discord.WebSocket
remove { _messageReceivedEvent.Remove(value); }
}
private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>();
- public event Func, Task> MessageDeleted
+ public event Func, ISocketMessageChannel, Task> MessageDeleted
{
add { _messageDeletedEvent.Add(value); }
remove { _messageDeletedEvent.Remove(value); }
}
- private readonly AsyncEvent, Task>> _messageDeletedEvent = new AsyncEvent, Task>>();
- public event Func, SocketMessage, Task> MessageUpdated
+ private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
+ public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated
{
add { _messageUpdatedEvent.Add(value); }
remove { _messageUpdatedEvent.Remove(value); }
}
- private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>();
- public event Func, SocketReaction, Task> ReactionAdded
+ private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>();
+ public event Func, ISocketMessageChannel, 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
+ private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved
{
add { _reactionRemovedEvent.Add(value); }
remove { _reactionRemovedEvent.Remove(value); }
}
- private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>();
- public event Func, Task> ReactionsCleared
+ private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ public event Func, ISocketMessageChannel, Task> ReactionsCleared
{
add { _reactionsClearedEvent.Add(value); }
remove { _reactionsClearedEvent.Remove(value); }
}
- private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>();
+ private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
//Roles
public event Func RoleCreated
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index cadbda6d1..06f83c8dc 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
@@ -275,11 +275,11 @@ namespace Discord.WebSocket
client.ChannelUpdated += (oldChannel, newChannel) => _channelUpdatedEvent.InvokeAsync(oldChannel, newChannel);
client.MessageReceived += (msg) => _messageReceivedEvent.InvokeAsync(msg);
- client.MessageDeleted += (id, msg) => _messageDeletedEvent.InvokeAsync(id, msg);
- client.MessageUpdated += (oldMsg, newMsg) => _messageUpdatedEvent.InvokeAsync(oldMsg, newMsg);
- client.ReactionAdded += (id, msg, reaction) => _reactionAddedEvent.InvokeAsync(id, msg, reaction);
- client.ReactionRemoved += (id, msg, reaction) => _reactionRemovedEvent.InvokeAsync(id, msg, reaction);
- client.ReactionsCleared += (id, msg) => _reactionsClearedEvent.InvokeAsync(id, msg);
+ client.MessageDeleted += (cache, channel) => _messageDeletedEvent.InvokeAsync(cache, channel);
+ client.MessageUpdated += (oldMsg, newMsg, channel) => _messageUpdatedEvent.InvokeAsync(oldMsg, newMsg, channel);
+ 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.RoleCreated += (role) => _roleCreatedEvent.InvokeAsync(role);
client.RoleDeleted += (role) => _roleDeletedEvent.InvokeAsync(role);
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs
index 2f952bdaa..313e661f3 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs
@@ -59,36 +59,36 @@ namespace Discord.WebSocket
remove { _messageReceivedEvent.Remove(value); }
}
private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>();
- public event Func, Task> MessageDeleted
+ public event Func, ISocketMessageChannel, Task> MessageDeleted
{
add { _messageDeletedEvent.Add(value); }
remove { _messageDeletedEvent.Remove(value); }
}
- private readonly AsyncEvent, Task>> _messageDeletedEvent = new AsyncEvent, Task>>();
- public event Func, SocketMessage, Task> MessageUpdated
+ private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
+ public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated
{
add { _messageUpdatedEvent.Add(value); }
remove { _messageUpdatedEvent.Remove(value); }
}
- private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>();
- public event Func, SocketReaction, Task> ReactionAdded
+ private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>();
+ public event Func, ISocketMessageChannel, 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
+ private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved
{
add { _reactionRemovedEvent.Add(value); }
remove { _reactionRemovedEvent.Remove(value); }
}
- private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>();
- public event Func, Task> ReactionsCleared
+ private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ public event Func, ISocketMessageChannel, Task> ReactionsCleared
{
add { _reactionsClearedEvent.Add(value); }
remove { _reactionsClearedEvent.Remove(value); }
}
- private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>();
+ private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
//Roles
public event Func RoleCreated
@@ -116,37 +116,37 @@ namespace Discord.WebSocket
add { _joinedGuildEvent.Add(value); }
remove { _joinedGuildEvent.Remove(value); }
}
- private AsyncEvent> _joinedGuildEvent = new AsyncEvent>();
+ private readonly AsyncEvent> _joinedGuildEvent = new AsyncEvent>();
public event Func LeftGuild
{
add { _leftGuildEvent.Add(value); }
remove { _leftGuildEvent.Remove(value); }
}
- private AsyncEvent> _leftGuildEvent = new AsyncEvent>();
+ private readonly AsyncEvent> _leftGuildEvent = new AsyncEvent>();
public event Func GuildAvailable
{
add { _guildAvailableEvent.Add(value); }
remove { _guildAvailableEvent.Remove(value); }
}
- private AsyncEvent> _guildAvailableEvent = new AsyncEvent>();
+ private readonly AsyncEvent> _guildAvailableEvent = new AsyncEvent>();
public event Func GuildUnavailable
{
add { _guildUnavailableEvent.Add(value); }
remove { _guildUnavailableEvent.Remove(value); }
}
- private AsyncEvent> _guildUnavailableEvent = new AsyncEvent>();
+ private readonly AsyncEvent> _guildUnavailableEvent = new AsyncEvent>();
public event Func GuildMembersDownloaded
{
add { _guildMembersDownloadedEvent.Add(value); }
remove { _guildMembersDownloadedEvent.Remove(value); }
}
- private AsyncEvent> _guildMembersDownloadedEvent = new AsyncEvent>();
+ private readonly AsyncEvent> _guildMembersDownloadedEvent = new AsyncEvent>();
public event Func GuildUpdated
{
add { _guildUpdatedEvent.Add(value); }
remove { _guildUpdatedEvent.Remove(value); }
}
- private AsyncEvent> _guildUpdatedEvent = new AsyncEvent>();
+ private readonly AsyncEvent> _guildUpdatedEvent = new AsyncEvent>();
//Users
public event Func UserJoined
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index d52de0af1..52d79a04b 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -1101,13 +1101,8 @@ namespace Discord.WebSocket
return;
}
- SocketUser author;
- if (guild != null)
- author = guild.GetUser(data.Author.Value.Id);
- else
- author = (channel as SocketChannel).GetUser(data.Author.Value.Id);
- if (author == null)
- author = SocketSimpleUser.Create(this, State, data.Author.Value);
+ var author = (guild != null ? guild.GetUser(data.Author.Value.Id) : (channel as SocketChannel).GetUser(data.Author.Value.Id)) ??
+ SocketSimpleUser.Create(this, State, data.Author.Value);
if (author != null)
{
@@ -1138,14 +1133,15 @@ namespace Discord.WebSocket
{
var guild = (channel as SocketGuildChannel)?.Guild;
if (guild != null && !guild.IsSynced)
- {
+ {
await _gatewayLogger.DebugAsync("Ignored MESSAGE_UPDATE, guild is not synced yet.").ConfigureAwait(false);
return;
}
SocketMessage before = null, after = null;
SocketMessage cachedMsg = channel.GetCachedMessage(data.Id);
- if (cachedMsg != null)
+ bool isCached = cachedMsg != null;
+ if (isCached)
{
before = cachedMsg.Clone();
cachedMsg.Update(State, data);
@@ -1164,10 +1160,9 @@ namespace Discord.WebSocket
after = SocketMessage.Create(this, State, author, channel, data);
}
- if (before != null)
- await _messageUpdatedEvent.InvokeAsync(before, after).ConfigureAwait(false);
- else
- await _messageUpdatedEvent.InvokeAsync(Optional.Create(), after).ConfigureAwait(false);
+ var cacheableBefore = new Cacheable(before, data.Id, isCached , async () => await channel.GetMessageAsync(data.Id));
+
+ await _messageUpdatedEvent.InvokeAsync(cacheableBefore, after, channel).ConfigureAwait(false);
}
else
{
@@ -1179,22 +1174,22 @@ namespace Discord.WebSocket
case "MESSAGE_DELETE":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
-
+
var data = (payload as JToken).ToObject(_serializer);
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
if (channel != null)
{
if (!((channel as SocketGuildChannel)?.Guild.IsSynced ?? true))
- {
+ {
await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE, guild is not synced yet.").ConfigureAwait(false);
return;
}
var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id);
- if (msg != null)
- await _messageDeletedEvent.InvokeAsync(data.Id, msg).ConfigureAwait(false);
- else
- await _messageDeletedEvent.InvokeAsync(data.Id, Optional.Create()).ConfigureAwait(false);
+ bool isCached = msg != null;
+ var cacheable = new Cacheable(msg, data.Id, isCached, async () => await channel.GetMessageAsync(data.Id));
+
+ await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false);
}
else
{
@@ -1212,15 +1207,14 @@ namespace Discord.WebSocket
if (channel != null)
{
SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
+ bool isCached = cachedMsg != null;
var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly);
SocketReaction reaction = SocketReaction.Create(data, channel, 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(), reaction).ConfigureAwait(false);
+ var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage);
+
+ cachedMsg?.AddReaction(reaction);
+
+ await _reactionAddedEvent.InvokeAsync(cacheable, channel, reaction).ConfigureAwait(false);
}
else
{
@@ -1238,15 +1232,14 @@ namespace Discord.WebSocket
if (channel != null)
{
SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
+ bool isCached = cachedMsg != null;
var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly);
SocketReaction reaction = SocketReaction.Create(data, channel, 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(), reaction).ConfigureAwait(false);
+ var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage);
+
+ cachedMsg?.RemoveReaction(reaction);
+
+ await _reactionRemovedEvent.InvokeAsync(cacheable, channel, reaction).ConfigureAwait(false);
}
else
{
@@ -1264,20 +1257,18 @@ namespace Discord.WebSocket
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());
+ bool isCached = cachedMsg != null;
+ var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage);
+
+ cachedMsg?.ClearReactions();
+
+ await _reactionsClearedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false);
}
else
{
await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE_ALL referenced an unknown channel.").ConfigureAwait(false);
return;
}
-
break;
}
case "MESSAGE_DELETE_BULK":
@@ -1297,10 +1288,9 @@ namespace Discord.WebSocket
foreach (var id in data.Ids)
{
var msg = SocketChannelHelper.RemoveMessage(channel, this, id);
- if (msg != null)
- await _messageDeletedEvent.InvokeAsync(id, msg).ConfigureAwait(false);
- else
- await _messageDeletedEvent.InvokeAsync(id, Optional.Create()).ConfigureAwait(false);
+ bool isCached = msg != null;
+ var cacheable = new Cacheable(msg, id, isCached, async () => await channel.GetMessageAsync(id));
+ await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false);
}
}
else