@@ -36,6 +36,7 @@ namespace Discord.Rest | |||||
entity.Update(model); | entity.Update(model); | ||||
return entity; | return entity; | ||||
} | } | ||||
internal void Update(Model model) | internal void Update(Model model) | ||||
{ | { | ||||
base.Update(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>>(); | 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 | //Roles | ||||
/// <summary> Fired when a role is created. </summary> | /// <summary> Fired when a role is created. </summary> | ||||
public event Func<SocketRole, Task> RoleCreated { | 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>>(); | 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 | //Users | ||||
/// <summary> Fired when a user joins a guild. </summary> | /// <summary> Fired when a user joins a guild. </summary> | ||||
public event Func<SocketGuildUser, Task> UserJoined { | public event Func<SocketGuildUser, Task> UserJoined { | ||||
@@ -313,6 +313,7 @@ namespace Discord.WebSocket | |||||
client.ReactionAdded += (cache, channel, reaction) => _reactionAddedEvent.InvokeAsync(cache, channel, reaction); | client.ReactionAdded += (cache, channel, reaction) => _reactionAddedEvent.InvokeAsync(cache, channel, reaction); | ||||
client.ReactionRemoved += (cache, channel, reaction) => _reactionRemovedEvent.InvokeAsync(cache, channel, reaction); | client.ReactionRemoved += (cache, channel, reaction) => _reactionRemovedEvent.InvokeAsync(cache, channel, reaction); | ||||
client.ReactionsCleared += (cache, channel) => _reactionsClearedEvent.InvokeAsync(cache, channel); | 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.RoleCreated += (role) => _roleCreatedEvent.InvokeAsync(role); | ||||
client.RoleDeleted += (role) => _roleDeletedEvent.InvokeAsync(role); | client.RoleDeleted += (role) => _roleDeletedEvent.InvokeAsync(role); | ||||
@@ -325,6 +326,9 @@ namespace Discord.WebSocket | |||||
client.GuildMembersDownloaded += (guild) => _guildMembersDownloadedEvent.InvokeAsync(guild); | client.GuildMembersDownloaded += (guild) => _guildMembersDownloadedEvent.InvokeAsync(guild); | ||||
client.GuildUpdated += (oldGuild, newGuild) => _guildUpdatedEvent.InvokeAsync(oldGuild, newGuild); | 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.UserJoined += (user) => _userJoinedEvent.InvokeAsync(user); | ||||
client.UserLeft += (user) => _userLeftEvent.InvokeAsync(user); | client.UserLeft += (user) => _userLeftEvent.InvokeAsync(user); | ||||
client.UserBanned += (user, guild) => _userBannedEvent.InvokeAsync(user, guild); | client.UserBanned += (user, guild) => _userBannedEvent.InvokeAsync(user, guild); | ||||
@@ -882,7 +882,6 @@ namespace Discord.WebSocket | |||||
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | ||||
return; | return; | ||||
} | } | ||||
if (user != null) | if (user != null) | ||||
{ | { | ||||
var before = user.Clone(); | var before = user.Clone(); | ||||
@@ -1213,7 +1212,6 @@ namespace Discord.WebSocket | |||||
case "MESSAGE_UPDATE": | case "MESSAGE_UPDATE": | ||||
{ | { | ||||
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false); | ||||
var data = (payload as JToken).ToObject<API.Message>(_serializer); | var data = (payload as JToken).ToObject<API.Message>(_serializer); | ||||
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) | if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) | ||||
{ | { | ||||
@@ -1378,6 +1376,41 @@ namespace Discord.WebSocket | |||||
} | } | ||||
} | } | ||||
break; | 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": | case "MESSAGE_DELETE_BULK": | ||||
{ | { | ||||
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); | ||||
@@ -1422,6 +1455,53 @@ namespace Discord.WebSocket | |||||
} | } | ||||
break; | 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 | //Statuses | ||||
case "PRESENCE_UPDATE": | 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); | _reactions.Remove(reaction); | ||||
} | } | ||||
internal void ClearReactions() | internal void ClearReactions() | ||||
{ | |||||
_reactions.Clear(); | |||||
} | |||||
=> _reactions.Clear(); | |||||
internal void ClearReactionsEmoji(SocketReaction reaction) | |||||
=> _reactions.RemoveAll(r => r.Emote.Equals(reaction)); | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public Task AddReactionAsync(IEmote emote, RequestOptions options = null) | public Task AddReactionAsync(IEmote emote, RequestOptions options = null) | ||||