| @@ -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}"; | ||||