@@ -63,4 +63,60 @@ namespace Discord | |||||
/// </returns> | /// </returns> | ||||
public async Task<TEntity> GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync().ConfigureAwait(false); | 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; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -397,6 +398,21 @@ namespace Discord.WebSocket | |||||
} | } | ||||
internal readonly AsyncEvent<Func<SocketGuildEvent, Task>> _guildScheduledEventStarted = new AsyncEvent<Func<SocketGuildEvent, Task>>(); | 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 | #endregion | ||||
#region Users | #region Users | ||||
@@ -2597,6 +2597,7 @@ namespace Discord.WebSocket | |||||
} | } | ||||
var before = guild.GetEvent(data.Id); | var before = guild.GetEvent(data.Id); | ||||
var beforeCacheable = new Cacheable<SocketGuildEvent, ulong>(before, data.Id, before != null, () => Task.FromResult((SocketGuildEvent)null)); | var beforeCacheable = new Cacheable<SocketGuildEvent, ulong>(before, data.Id, before != null, () => Task.FromResult((SocketGuildEvent)null)); | ||||
var after = guild.AddOrUpdateEvent(data); | var after = guild.AddOrUpdateEvent(data); | ||||
@@ -2631,6 +2632,44 @@ namespace Discord.WebSocket | |||||
await TimedInvokeAsync(_guildScheduledEventCancelled, nameof(GuildScheduledEventCancelled), guildEvent).ConfigureAwait(false); | await TimedInvokeAsync(_guildScheduledEventCancelled, nameof(GuildScheduledEventCancelled), guildEvent).ConfigureAwait(false); | ||||
} | } | ||||
break; | 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 | #endregion | ||||
@@ -2947,6 +2986,12 @@ namespace Discord.WebSocket | |||||
string details = $"{evnt} Guild={guildId}"; | string details = $"{evnt} Guild={guildId}"; | ||||
await _gatewayLogger.WarningAsync($"Unknown Guild ({details}).").ConfigureAwait(false); | 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) | private async Task UnsyncedGuildAsync(string evnt, ulong guildId) | ||||
{ | { | ||||
string details = $"{evnt} Guild={guildId}"; | string details = $"{evnt} Guild={guildId}"; | ||||