@@ -63,4 +63,60 @@ namespace Discord | |||
/// </returns> | |||
public async Task<TEntity> GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync().ConfigureAwait(false); | |||
} | |||
public struct Cacheable<TCachedEntity, TDownloadableEntity, TRelationship, TId> | |||
where TCachedEntity : IEntity<TId>, TRelationship | |||
where TDownloadableEntity : IEntity<TId>, TRelationship | |||
where TId : IEquatable<TId> | |||
{ | |||
/// <summary> | |||
/// Gets whether this entity is cached. | |||
/// </summary> | |||
public bool HasValue { get; } | |||
/// <summary> | |||
/// Gets the ID of this entity. | |||
/// </summary> | |||
public TId Id { get; } | |||
/// <summary> | |||
/// Gets the entity if it could be pulled from cache. | |||
/// </summary> | |||
/// <remarks> | |||
/// This value is not guaranteed to be set; in cases where the entity cannot be pulled from cache, it is | |||
/// <c>null</c>. | |||
/// </remarks> | |||
public TCachedEntity Value { get; } | |||
private Func<Task<TDownloadableEntity>> DownloadFunc { get; } | |||
internal Cacheable(TCachedEntity value, TId id, bool hasValue, Func<Task<TDownloadableEntity>> downloadFunc) | |||
{ | |||
Value = value; | |||
Id = id; | |||
HasValue = hasValue; | |||
DownloadFunc = downloadFunc; | |||
} | |||
/// <summary> | |||
/// Downloads this entity. | |||
/// </summary> | |||
/// <exception cref="Discord.Net.HttpException">Thrown when used from a user account.</exception> | |||
/// <exception cref="NullReferenceException">Thrown when the message is deleted.</exception> | |||
/// <returns> | |||
/// A task that represents the asynchronous download operation. The task result contains the downloaded | |||
/// entity. | |||
/// </returns> | |||
public async Task<TDownloadableEntity> DownloadAsync() | |||
{ | |||
return await DownloadFunc().ConfigureAwait(false); | |||
} | |||
/// <summary> | |||
/// Returns the cached entity if it exists; otherwise downloads it. | |||
/// </summary> | |||
/// <exception cref="Discord.Net.HttpException">Thrown when used from a user account.</exception> | |||
/// <exception cref="NullReferenceException">Thrown when the message is deleted and is not in cache.</exception> | |||
/// <returns> | |||
/// A task that represents the asynchronous operation that attempts to get the message via cache or to | |||
/// download the message. The task result contains the downloaded entity. | |||
/// </returns> | |||
public async Task<TRelationship> GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync().ConfigureAwait(false); | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.API.Gateway | |||
{ | |||
internal class GuildScheduledEventUserAddRemoveEvent | |||
{ | |||
[JsonProperty("guild_scheduled_event_id")] | |||
public ulong EventId { get; set; } | |||
[JsonProperty("guild_id")] | |||
public ulong GuildId { get; set; } | |||
[JsonProperty("user_id")] | |||
public ulong UserId { get; set; } | |||
} | |||
} |
@@ -1,3 +1,4 @@ | |||
using Discord.Rest; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
@@ -397,6 +398,21 @@ namespace Discord.WebSocket | |||
} | |||
internal readonly AsyncEvent<Func<SocketGuildEvent, Task>> _guildScheduledEventStarted = new AsyncEvent<Func<SocketGuildEvent, Task>>(); | |||
public event Func<Cacheable<SocketUser, RestUser, IUser, ulong>, SocketGuildEvent, Task> GuildScheduledEventUserAdd | |||
{ | |||
add { _guildScheduledEventUserAdd.Add(value); } | |||
remove { _guildScheduledEventUserAdd.Remove(value); } | |||
} | |||
internal readonly AsyncEvent<Func<Cacheable<SocketUser, RestUser, IUser, ulong>, SocketGuildEvent, Task>> _guildScheduledEventUserAdd = new AsyncEvent<Func<Cacheable<SocketUser, RestUser, IUser, ulong>, SocketGuildEvent, Task>>(); | |||
public event Func<Cacheable<SocketUser, RestUser, IUser, ulong>, SocketGuildEvent, Task> GuildScheduledEventUserRemove | |||
{ | |||
add { _guildScheduledEventUserRemove.Add(value); } | |||
remove { _guildScheduledEventUserRemove.Remove(value); } | |||
} | |||
internal readonly AsyncEvent<Func<Cacheable<SocketUser, RestUser, IUser, ulong>, SocketGuildEvent, Task>> _guildScheduledEventUserRemove = new AsyncEvent<Func<Cacheable<SocketUser, RestUser, IUser, ulong>, SocketGuildEvent, Task>>(); | |||
#endregion | |||
#region Users | |||
@@ -2597,6 +2597,7 @@ namespace Discord.WebSocket | |||
} | |||
var before = guild.GetEvent(data.Id); | |||
var beforeCacheable = new Cacheable<SocketGuildEvent, ulong>(before, data.Id, before != null, () => Task.FromResult((SocketGuildEvent)null)); | |||
var after = guild.AddOrUpdateEvent(data); | |||
@@ -2631,6 +2632,44 @@ namespace Discord.WebSocket | |||
await TimedInvokeAsync(_guildScheduledEventCancelled, nameof(GuildScheduledEventCancelled), guildEvent).ConfigureAwait(false); | |||
} | |||
break; | |||
case "GUILD_SCHEDULED_EVENT_USER_ADD" or "GUILD_SCHEDULED_EVENT_USER_REMOVE": | |||
{ | |||
await _gatewayLogger.DebugAsync($"Received Dispatch ({type})").ConfigureAwait(false); | |||
var data = (payload as JToken).ToObject<GuildScheduledEventUserAddRemoveEvent>(_serializer); | |||
var guild = State.GetGuild(data.GuildId); | |||
if(guild == null) | |||
{ | |||
await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false); | |||
return; | |||
} | |||
var guildEvent = guild.GetEvent(data.EventId); | |||
if (guildEvent == null) | |||
{ | |||
await UnknownGuildEventAsync(type, data.EventId, data.GuildId).ConfigureAwait(false); | |||
return; | |||
} | |||
var user = (SocketUser)guild.GetUser(data.UserId) ?? State.GetUser(data.UserId); | |||
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(user, data.UserId, user != null, () => Rest.GetUserAsync(data.UserId)); | |||
switch (type) | |||
{ | |||
case "GUILD_SCHEDULED_EVENT_USER_ADD": | |||
await TimedInvokeAsync(_guildScheduledEventUserAdd, nameof(GuildScheduledEventUserAdd), cacheableUser, guildEvent).ConfigureAwait(false); | |||
break; | |||
case "GUILD_SCHEDULED_EVENT_USER_REMOVE": | |||
await TimedInvokeAsync(_guildScheduledEventUserRemove, nameof(GuildScheduledEventUserRemove), cacheableUser, guildEvent).ConfigureAwait(false); | |||
break; | |||
} | |||
} | |||
break; | |||
#endregion | |||
@@ -2947,6 +2986,12 @@ namespace Discord.WebSocket | |||
string details = $"{evnt} Guild={guildId}"; | |||
await _gatewayLogger.WarningAsync($"Unknown Guild ({details}).").ConfigureAwait(false); | |||
} | |||
private async Task UnknownGuildEventAsync(string evnt, ulong eventId, ulong guildId) | |||
{ | |||
string details = $"{evnt} Event={eventId} Guild={guildId}"; | |||
await _gatewayLogger.WarningAsync($"Unknown Guild Event ({details}).").ConfigureAwait(false); | |||
} | |||
private async Task UnsyncedGuildAsync(string evnt, ulong guildId) | |||
{ | |||
string details = $"{evnt} Guild={guildId}"; | |||