diff --git a/src/Discord.Net.Core/Utils/Cacheable.cs b/src/Discord.Net.Core/Utils/Cacheable.cs index 1857ae7a0..4aa768292 100644 --- a/src/Discord.Net.Core/Utils/Cacheable.cs +++ b/src/Discord.Net.Core/Utils/Cacheable.cs @@ -63,4 +63,60 @@ namespace Discord /// public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync().ConfigureAwait(false); } + public struct Cacheable + where TCachedEntity : IEntity, TRelationship + where TDownloadableEntity : IEntity, TRelationship + where TId : IEquatable + { + /// + /// Gets whether this entity is cached. + /// + public bool HasValue { get; } + /// + /// Gets the ID of this entity. + /// + public TId Id { get; } + /// + /// Gets 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 TCachedEntity Value { get; } + private Func> DownloadFunc { get; } + + internal Cacheable(TCachedEntity value, TId id, bool hasValue, Func> downloadFunc) + { + Value = value; + Id = id; + HasValue = hasValue; + DownloadFunc = downloadFunc; + } + + /// + /// Downloads this entity. + /// + /// Thrown when used from a user account. + /// Thrown when the message is deleted. + /// + /// A task that represents the asynchronous download operation. The task result contains the downloaded + /// entity. + /// + public async Task DownloadAsync() + { + return await DownloadFunc().ConfigureAwait(false); + } + + /// + /// Returns the cached entity if it exists; otherwise downloads it. + /// + /// Thrown when used from a user account. + /// Thrown when the message is deleted and is not in cache. + /// + /// 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. + /// + public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync().ConfigureAwait(false); + } } diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildScheduledEventUserAddRemoveEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildScheduledEventUserAddRemoveEvent.cs new file mode 100644 index 000000000..3fc959125 --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildScheduledEventUserAddRemoveEvent.cs @@ -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; } + } +} diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs index 153b320fa..4ad25d4d5 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs @@ -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> _guildScheduledEventStarted = new AsyncEvent>(); + public event Func, SocketGuildEvent, Task> GuildScheduledEventUserAdd + { + add { _guildScheduledEventUserAdd.Add(value); } + remove { _guildScheduledEventUserAdd.Remove(value); } + } + internal readonly AsyncEvent, SocketGuildEvent, Task>> _guildScheduledEventUserAdd = new AsyncEvent, SocketGuildEvent, Task>>(); + + public event Func, SocketGuildEvent, Task> GuildScheduledEventUserRemove + { + add { _guildScheduledEventUserRemove.Add(value); } + remove { _guildScheduledEventUserRemove.Remove(value); } + } + internal readonly AsyncEvent, SocketGuildEvent, Task>> _guildScheduledEventUserRemove = new AsyncEvent, SocketGuildEvent, Task>>(); + + #endregion #region Users diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index f34106a20..d074ebced 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -2597,6 +2597,7 @@ namespace Discord.WebSocket } var before = guild.GetEvent(data.Id); + var beforeCacheable = new Cacheable(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(_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(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}";