@@ -0,0 +1,30 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
public struct Cacheable<TEntity, TId> | |||
where TEntity : IEntity<TId> | |||
where TId : IEquatable<TId> | |||
{ | |||
public bool HasValue => !EqualityComparer<TEntity>.Default.Equals(Value, default(TEntity)); | |||
public ulong Id { get; } | |||
public TEntity Value { get; } | |||
private Func<Task<TEntity>> DownloadFunc { get; } | |||
internal Cacheable(TEntity value, ulong id, Func<Task<TEntity>> downloadFunc) | |||
{ | |||
Value = value; | |||
Id = id; | |||
DownloadFunc = downloadFunc; | |||
} | |||
public async Task<TEntity> DownloadAsync() | |||
{ | |||
return await DownloadFunc(); | |||
} | |||
public async Task<TEntity> GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync(); | |||
} | |||
} |
@@ -1,33 +0,0 @@ | |||
using Discord.WebSocket; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
public struct Cached<T> where T : IMessage | |||
{ | |||
public bool IsCached => !EqualityComparer<T>.Default.Equals(Value, default(T)); | |||
public bool IsDownloadable { get; } | |||
public ulong Id { get; } | |||
public T Value { get; } | |||
public ISocketMessageChannel Channel { get; } | |||
public Cached(ulong id, T value, ISocketMessageChannel channel, bool isDownloadable = true) | |||
{ | |||
Id = id; | |||
Value = value; | |||
Channel = channel; | |||
IsDownloadable = isDownloadable; | |||
} | |||
public async Task<T> DownloadAsync() | |||
{ | |||
if (IsDownloadable) | |||
return (T) await Channel.GetMessageAsync(Id); | |||
throw new InvalidOperationException("This message cannot be downloaded."); | |||
} | |||
public async Task<T> GetOrDownloadAsync() => IsCached ? Value : await DownloadAsync(); | |||
} | |||
} |
@@ -59,36 +59,36 @@ namespace Discord.WebSocket | |||
remove { _messageReceivedEvent.Remove(value); } | |||
} | |||
private readonly AsyncEvent<Func<SocketMessage, Task>> _messageReceivedEvent = new AsyncEvent<Func<SocketMessage, Task>>(); | |||
public event Func<Cached<SocketMessage>, Task> MessageDeleted | |||
public event Func<Cacheable<SocketMessage, ulong>, ISocketMessageChannel, Task> MessageDeleted | |||
{ | |||
add { _messageDeletedEvent.Add(value); } | |||
remove { _messageDeletedEvent.Remove(value); } | |||
} | |||
private readonly AsyncEvent<Func<Cached<SocketMessage>, Task>> _messageDeletedEvent = new AsyncEvent<Func<Cached<SocketMessage>, Task>>(); | |||
public event Func<Cached<SocketMessage>, SocketMessage, Task> MessageUpdated | |||
private readonly AsyncEvent<Func<Cacheable<SocketMessage, ulong>, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent<Func<Cacheable<SocketMessage, ulong>, ISocketMessageChannel, Task>>(); | |||
public event Func<Optional<SocketMessage>, SocketMessage, ISocketMessageChannel, Task> MessageUpdated | |||
{ | |||
add { _messageUpdatedEvent.Add(value); } | |||
remove { _messageUpdatedEvent.Remove(value); } | |||
} | |||
private readonly AsyncEvent<Func<Cached<SocketMessage>, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Cached<SocketMessage>, SocketMessage, Task>>(); | |||
public event Func<Cached<SocketUserMessage>, SocketReaction, Task> ReactionAdded | |||
private readonly AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, ISocketMessageChannel, Task>>(); | |||
public event Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task> ReactionAdded | |||
{ | |||
add { _reactionAddedEvent.Add(value); } | |||
remove { _reactionAddedEvent.Remove(value); } | |||
} | |||
private readonly AsyncEvent<Func<Cached<SocketUserMessage>, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent<Func<Cached<SocketUserMessage>, SocketReaction, Task>>(); | |||
public event Func<Cached<SocketUserMessage>, SocketReaction, Task> ReactionRemoved | |||
private readonly AsyncEvent<Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent<Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task>>(); | |||
public event Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved | |||
{ | |||
add { _reactionRemovedEvent.Add(value); } | |||
remove { _reactionRemovedEvent.Remove(value); } | |||
} | |||
private readonly AsyncEvent<Func<Cached<SocketUserMessage>, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent<Func<Cached<SocketUserMessage>, SocketReaction, Task>>(); | |||
public event Func<Cached<SocketUserMessage>, Task> ReactionsCleared | |||
private readonly AsyncEvent<Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent<Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task>>(); | |||
public event Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, Task> ReactionsCleared | |||
{ | |||
add { _reactionsClearedEvent.Add(value); } | |||
remove { _reactionsClearedEvent.Remove(value); } | |||
} | |||
private readonly AsyncEvent<Func<Cached<SocketUserMessage>, Task>> _reactionsClearedEvent = new AsyncEvent<Func<Cached<SocketUserMessage>, Task>>(); | |||
private readonly AsyncEvent<Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent<Func<Cacheable<SocketUserMessage, ulong>, ISocketMessageChannel, Task>>(); | |||
//Roles | |||
public event Func<SocketRole, Task> RoleCreated | |||
@@ -1265,7 +1265,7 @@ 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; | |||
} | |||
@@ -1281,13 +1281,20 @@ namespace Discord.WebSocket | |||
else if (data.Author.IsSpecified) | |||
{ | |||
//Edited message isnt in cache, create a detached one | |||
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); | |||
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); | |||
after = SocketMessage.Create(this, State, author, channel, data); | |||
} | |||
var cached = new Cached<SocketMessage>(data.Id, before, channel); | |||
await _messageUpdatedEvent.InvokeAsync(cached, after).ConfigureAwait(false); | |||
if (before != null) | |||
await _messageUpdatedEvent.InvokeAsync(before, after, channel).ConfigureAwait(false); | |||
else | |||
await _messageUpdatedEvent.InvokeAsync(Optional.Create<SocketMessage>(), after, channel).ConfigureAwait(false); | |||
} | |||
else | |||
{ | |||
@@ -1299,25 +1306,26 @@ namespace Discord.WebSocket | |||
case "MESSAGE_DELETE": | |||
{ | |||
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false); | |||
var data = (payload as JToken).ToObject<API.Message>(_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); | |||
var cached = new Cached<SocketMessage>(data.Id, msg, channel, isDownloadable: false); | |||
var cacheable = new Cacheable<SocketMessage, ulong>(msg, data.Id, async () => await channel.GetMessageAsync(data.Id) as SocketUserMessage); | |||
await _messageDeletedEvent.InvokeAsync(cached).ConfigureAwait(false); | |||
await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); | |||
} | |||
else | |||
{ | |||
await _gatewayLogger.WarningAsync("MESSAGE_DELETE referenced an unknown channel.").ConfigureAwait(false); | |||
return; | |||
} | |||
} | |||
break; | |||
@@ -1332,11 +1340,11 @@ namespace Discord.WebSocket | |||
SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; | |||
var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); | |||
SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); | |||
var cached = new Cached<SocketUserMessage>(data.MessageId, cachedMsg, channel); | |||
var cacheable = new Cacheable<SocketUserMessage, ulong>(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); | |||
cachedMsg?.AddReaction(reaction); | |||
await _reactionAddedEvent.InvokeAsync(cached, reaction).ConfigureAwait(false); | |||
await _reactionAddedEvent.InvokeAsync(cacheable, channel, reaction).ConfigureAwait(false); | |||
} | |||
else | |||
{ | |||
@@ -1356,15 +1364,16 @@ namespace Discord.WebSocket | |||
SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; | |||
var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); | |||
SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); | |||
var cached = new Cached<SocketUserMessage>(data.MessageId, cachedMsg, channel); | |||
var cacheable = new Cacheable<SocketUserMessage, ulong>(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); | |||
cachedMsg?.RemoveReaction(reaction); | |||
await _reactionRemovedEvent.InvokeAsync(cached, reaction).ConfigureAwait(false); | |||
await _reactionRemovedEvent.InvokeAsync(cacheable, channel, reaction).ConfigureAwait(false); | |||
} | |||
else | |||
{ | |||
await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE referenced an unknown channel.").ConfigureAwait(false); | |||
return; | |||
} | |||
break; | |||
} | |||
@@ -1377,11 +1386,11 @@ namespace Discord.WebSocket | |||
if (channel != null) | |||
{ | |||
SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; | |||
var cached = new Cached<SocketUserMessage>(data.MessageId, cachedMsg, channel); | |||
var cacheable = new Cacheable<SocketUserMessage, ulong>(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); | |||
cachedMsg?.ClearReactions(); | |||
await _reactionsClearedEvent.InvokeAsync(cached); | |||
await _reactionsClearedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); | |||
} | |||
else | |||
{ | |||
@@ -1407,13 +1416,14 @@ namespace Discord.WebSocket | |||
foreach (var id in data.Ids) | |||
{ | |||
var msg = SocketChannelHelper.RemoveMessage(channel, this, id); | |||
var cached = new Cached<SocketMessage>(id, msg, channel, false); | |||
await _messageDeletedEvent.InvokeAsync(cached).ConfigureAwait(false); | |||
var cacheable = new Cacheable<SocketMessage, ulong>(msg, id, async () => await channel.GetMessageAsync(id) as SocketMessage); | |||
await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); | |||
} | |||
} | |||
else | |||
{ | |||
await _gatewayLogger.WarningAsync("MESSAGE_DELETE_BULK referenced an unknown channel.").ConfigureAwait(false); | |||
return; | |||
} | |||
} | |||
break; | |||