| @@ -36,6 +36,7 @@ namespace Discord.Rest | |||
| entity.Update(model); | |||
| return entity; | |||
| } | |||
| internal void Update(Model model) | |||
| { | |||
| base.Update(model); | |||
| @@ -0,0 +1,13 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API | |||
| { | |||
| internal class InviteEvent : InviteMetadata | |||
| { | |||
| [JsonProperty("channel_id")] | |||
| public ulong ChannelId { get; set; } | |||
| [JsonProperty("guild_id")] | |||
| public ulong GuildId { get; set; } | |||
| } | |||
| } | |||
| @@ -235,6 +235,14 @@ namespace Discord.WebSocket | |||
| } | |||
| internal readonly AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, Task>>(); | |||
| /// <summary> Fired when all reactions of a specific reaction are removed.</summary> | |||
| public event Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task> ReactionsClearedEmoji { | |||
| add { _reactionsClearedEmojiEvent.Add(value); } | |||
| remove { _reactionsClearedEmojiEvent.Remove(value); } | |||
| } | |||
| internal readonly AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task>> _reactionsClearedEmojiEvent = new AsyncEvent<Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task>>(); | |||
| //Roles | |||
| /// <summary> Fired when a role is created. </summary> | |||
| public event Func<SocketRole, Task> RoleCreated { | |||
| @@ -293,6 +301,21 @@ namespace Discord.WebSocket | |||
| } | |||
| internal readonly AsyncEvent<Func<SocketGuild, SocketGuild, Task>> _guildUpdatedEvent = new AsyncEvent<Func<SocketGuild, SocketGuild, Task>>(); | |||
| //Invites | |||
| /// <summary> Fired when an invite is created.</summary | |||
| public event Func<SocketInvite, Task> InviteCreated { | |||
| add { _inviteCreatedEvent.Add(value); } | |||
| remove { _inviteCreatedEvent.Remove(value); } | |||
| } | |||
| internal readonly AsyncEvent<Func<SocketInvite, Task>> _inviteCreatedEvent = new AsyncEvent<Func<SocketInvite, Task>>(); | |||
| /// <summary>Fired when an invite is deleted.</summary> | |||
| public event Func<SocketInvite, Task> InviteDeleted | |||
| { | |||
| add { _inviteDeletedEvent.Add(value); } | |||
| remove { _inviteDeletedEvent.Remove(value); } | |||
| } | |||
| internal readonly AsyncEvent<Func<SocketInvite, Task>> _inviteDeletedEvent = new AsyncEvent<Func<SocketInvite, Task>>(); | |||
| //Users | |||
| /// <summary> Fired when a user joins a guild. </summary> | |||
| public event Func<SocketGuildUser, Task> UserJoined { | |||
| @@ -313,6 +313,7 @@ namespace Discord.WebSocket | |||
| client.ReactionAdded += (cache, channel, reaction) => _reactionAddedEvent.InvokeAsync(cache, channel, reaction); | |||
| client.ReactionRemoved += (cache, channel, reaction) => _reactionRemovedEvent.InvokeAsync(cache, channel, reaction); | |||
| client.ReactionsCleared += (cache, channel) => _reactionsClearedEvent.InvokeAsync(cache, channel); | |||
| client.ReactionsClearedEmoji += (cache, channel, reaction) => _reactionsClearedEmojiEvent.InvokeAsync(cache, channel, reaction); | |||
| client.RoleCreated += (role) => _roleCreatedEvent.InvokeAsync(role); | |||
| client.RoleDeleted += (role) => _roleDeletedEvent.InvokeAsync(role); | |||
| @@ -325,6 +326,9 @@ namespace Discord.WebSocket | |||
| client.GuildMembersDownloaded += (guild) => _guildMembersDownloadedEvent.InvokeAsync(guild); | |||
| client.GuildUpdated += (oldGuild, newGuild) => _guildUpdatedEvent.InvokeAsync(oldGuild, newGuild); | |||
| client.InviteCreated += (invite) => _inviteCreatedEvent.InvokeAsync(invite); | |||
| client.InviteDeleted += (invite) => _inviteDeletedEvent.InvokeAsync(invite); | |||
| client.UserJoined += (user) => _userJoinedEvent.InvokeAsync(user); | |||
| client.UserLeft += (user) => _userLeftEvent.InvokeAsync(user); | |||
| client.UserBanned += (user, guild) => _userBannedEvent.InvokeAsync(user, guild); | |||
| @@ -882,7 +882,6 @@ namespace Discord.WebSocket | |||
| await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | |||
| return; | |||
| } | |||
| if (user != null) | |||
| { | |||
| var before = user.Clone(); | |||
| @@ -1213,7 +1212,6 @@ namespace Discord.WebSocket | |||
| case "MESSAGE_UPDATE": | |||
| { | |||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false); | |||
| var data = (payload as JToken).ToObject<API.Message>(_serializer); | |||
| if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) | |||
| { | |||
| @@ -1378,6 +1376,41 @@ namespace Discord.WebSocket | |||
| } | |||
| } | |||
| break; | |||
| case "MESSAGE_REACTION_REMOVE_EMOJI": | |||
| { | |||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_EMOJI)").ConfigureAwait(false); | |||
| var data = (payload as JToken).ToObject<API.Gateway.Reaction>(_serializer); | |||
| if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) | |||
| { | |||
| var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; | |||
| bool isCached = cachedMsg != null; | |||
| var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly).ConfigureAwait(false); | |||
| var optionalMsg = !isCached | |||
| ? Optional.Create<SocketUserMessage>() | |||
| : Optional.Create(cachedMsg); | |||
| var optionalUser = user is null | |||
| ? Optional.Create<IUser>() | |||
| : Optional.Create(user); | |||
| var reaction = SocketReaction.Create(data, channel, optionalMsg, optionalUser); | |||
| var cacheable = new Cacheable<IUserMessage, ulong>(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage); | |||
| cachedMsg?.ClearReactionsEmoji(reaction); | |||
| await TimedInvokeAsync(_reactionsClearedEmojiEvent, nameof(ReactionsClearedEmoji), cacheable, channel, reaction).ConfigureAwait(false); | |||
| } | |||
| else | |||
| { | |||
| await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); | |||
| return; | |||
| } | |||
| } | |||
| break; | |||
| case "MESSAGE_DELETE_BULK": | |||
| { | |||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); | |||
| @@ -1422,6 +1455,53 @@ namespace Discord.WebSocket | |||
| } | |||
| break; | |||
| //Invites | |||
| case "INVITE_CREATE": | |||
| { | |||
| await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_CREATE)").ConfigureAwait(false); | |||
| var data = (payload as JToken).ToObject<InviteEvent>(_serializer); | |||
| if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) | |||
| { | |||
| var guild = (channel as SocketGuildChannel)?.Guild; | |||
| if (!(guild?.IsSynced ?? true)) | |||
| { | |||
| await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | |||
| return; | |||
| } | |||
| var inviter = State.GetUser(data.Inviter.Id) as SocketUser; | |||
| var invite = SocketInvite.Create(this, data, inviter, guild, channel); | |||
| await TimedInvokeAsync(_inviteCreatedEvent, nameof(InviteCreated), invite); | |||
| } | |||
| else | |||
| await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); | |||
| } | |||
| break; | |||
| case "INVITE_DELETE": | |||
| { | |||
| await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_DELETE)").ConfigureAwait(false); | |||
| var data = (payload as JToken).ToObject<InviteEvent>(_serializer); | |||
| if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) | |||
| { | |||
| var guild = (channel as SocketGuildChannel)?.Guild; | |||
| if (!(guild?.IsSynced ?? true)) | |||
| { | |||
| await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | |||
| return; | |||
| } | |||
| var invite = SocketInvite.Create(this, data, guild, channel); | |||
| await TimedInvokeAsync(_inviteDeletedEvent, nameof(InviteDeleted), invite).ConfigureAwait(false); | |||
| } | |||
| else | |||
| await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); | |||
| } | |||
| break; | |||
| //Statuses | |||
| case "PRESENCE_UPDATE": | |||
| { | |||
| @@ -0,0 +1,79 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Threading.Tasks; | |||
| using Model = Discord.API.InviteEvent; | |||
| namespace Discord.WebSocket | |||
| { | |||
| public class SocketInvite : SocketEntity<string> | |||
| { | |||
| public ISocketMessageChannel Channel { get; private set; } | |||
| public ulong ChannelId { get; private set; } | |||
| public IGuild Guild { get; private set; } | |||
| public ulong GuildId { get; private set; } | |||
| public string Code { get; private set; } | |||
| public SocketUser Inviter { get; private set; } | |||
| public DateTimeOffset CreatedAt { get; private set; } | |||
| public int MaxAge { get; private set; } | |||
| public int MaxUses { get; private set; } | |||
| public int Uses { get; set; } | |||
| public bool Temporary { get; private set; } | |||
| internal SocketInvite(DiscordSocketClient discord, Model model) | |||
| : base(discord, model.Code) | |||
| { | |||
| } | |||
| internal static SocketInvite Create(DiscordSocketClient discord, Model model, SocketUser inviter, IGuild guild, ISocketMessageChannel channel) | |||
| { | |||
| var entity = new SocketInvite(discord, model); | |||
| entity.Update(model, inviter, guild, channel); | |||
| return entity; | |||
| } | |||
| internal static SocketInvite Create(DiscordSocketClient discord, Model model, IGuild guild, ISocketMessageChannel channel) | |||
| { | |||
| var entity = new SocketInvite(discord, model); | |||
| entity.Update(model.Code, guild, channel); | |||
| return entity; | |||
| } | |||
| internal void Update(Model model, SocketUser inviter, IGuild guild, ISocketMessageChannel channel) | |||
| { | |||
| Channel = channel; | |||
| ChannelId = model.ChannelId; | |||
| Guild = guild; | |||
| GuildId = model.GuildId; | |||
| Code = model.Code; | |||
| Inviter = inviter; | |||
| CreatedAt = model.CreatedAt.Value; | |||
| MaxAge = model.MaxAge.Value; | |||
| MaxUses = model.MaxUses.Value; | |||
| Uses = model.Uses.Value; | |||
| Temporary = model.Temporary; | |||
| } | |||
| internal void Update(string code, IGuild guild, ISocketMessageChannel channel) | |||
| { | |||
| Code = code; | |||
| Guild = guild; | |||
| GuildId = guild.Id; | |||
| Channel = channel; | |||
| ChannelId = channel.Id; | |||
| } | |||
| } | |||
| } | |||
| @@ -197,9 +197,10 @@ namespace Discord.WebSocket | |||
| _reactions.Remove(reaction); | |||
| } | |||
| internal void ClearReactions() | |||
| { | |||
| _reactions.Clear(); | |||
| } | |||
| => _reactions.Clear(); | |||
| internal void ClearReactionsEmoji(SocketReaction reaction) | |||
| => _reactions.RemoveAll(r => r.Emote.Equals(reaction)); | |||
| /// <inheritdoc /> | |||
| public Task AddReactionAsync(IEmote emote, RequestOptions options = null) | |||