diff --git a/src/Discord.Net.Core/Entities/Invites/TargetUserType.cs b/src/Discord.Net.Core/Entities/Invites/TargetUserType.cs
new file mode 100644
index 000000000..74263b888
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Invites/TargetUserType.cs
@@ -0,0 +1,14 @@
+namespace Discord
+{
+ public enum TargetUserType
+ {
+ ///
+ /// The invite whose target user type is not defined.
+ ///
+ Undefined = 0,
+ ///
+ /// The invite is for a Go Live stream.
+ ///
+ Stream = 1
+ }
+}
diff --git a/src/Discord.Net.WebSocket/API/Gateway/InviteCreateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/InviteCreateEvent.cs
new file mode 100644
index 000000000..e2ddd8816
--- /dev/null
+++ b/src/Discord.Net.WebSocket/API/Gateway/InviteCreateEvent.cs
@@ -0,0 +1,31 @@
+using Newtonsoft.Json;
+using System;
+
+namespace Discord.API.Gateway
+{
+ internal class InviteCreateEvent
+ {
+ [JsonProperty("channel_id")]
+ public ulong ChannelId { get; set; }
+ [JsonProperty("code")]
+ public string Code { get; set; }
+ [JsonProperty("created_at")]
+ public DateTimeOffset CreatedAt { get; set; }
+ [JsonProperty("guild_id")]
+ public Optional GuildId { get; set; }
+ [JsonProperty("inviter")]
+ public Optional Inviter { get; set; }
+ [JsonProperty("max_age")]
+ public int MaxAge { get; set; }
+ [JsonProperty("max_uses")]
+ public int MaxUses { get; set; }
+ [JsonProperty("target_user")]
+ public Optional TargetUser { get; set; }
+ [JsonProperty("target_user_type")]
+ public Optional TargetUserType { get; set; }
+ [JsonProperty("temporary")]
+ public bool Temporary { get; set; }
+ [JsonProperty("uses")]
+ public int Uses { get; set; }
+ }
+}
diff --git a/src/Discord.Net.WebSocket/API/Gateway/InviteDeleteEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/InviteDeleteEvent.cs
new file mode 100644
index 000000000..54bc75595
--- /dev/null
+++ b/src/Discord.Net.WebSocket/API/Gateway/InviteDeleteEvent.cs
@@ -0,0 +1,14 @@
+using Newtonsoft.Json;
+
+namespace Discord.API.Gateway
+{
+ internal class InviteDeleteEvent
+ {
+ [JsonProperty("channel_id")]
+ public ulong ChannelId { get; set; }
+ [JsonProperty("code")]
+ public string Code { get; set; }
+ [JsonProperty("guild_id")]
+ public Optional GuildId { get; set; }
+ }
+}
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
index 2cd62b3e8..966aec7fa 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
@@ -389,5 +389,47 @@ namespace Discord.WebSocket
remove { _recipientRemovedEvent.Remove(value); }
}
internal readonly AsyncEvent> _recipientRemovedEvent = new AsyncEvent>();
+
+ //Invites
+ ///
+ /// Fired when an invite is created.
+ ///
+ ///
+ ///
+ /// This event is fired when an invite is created. The event handler must return a
+ /// and accept a as its parameter.
+ ///
+ ///
+ /// The invite created will be passed into the parameter.
+ ///
+ ///
+ public event Func InviteCreated
+ {
+ add { _inviteCreatedEvent.Add(value); }
+ remove { _inviteCreatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _inviteCreatedEvent = new AsyncEvent>();
+ ///
+ /// Fired when an invite is deleted.
+ ///
+ ///
+ ///
+ /// This event is fired when an invite is deleted. The event handler must return
+ /// a and accept a and
+ /// as its parameter.
+ ///
+ ///
+ /// The channel where this invite was created will be passed into the parameter.
+ ///
+ ///
+ /// The code of the deleted invite will be passed into the parameter.
+ ///
+ ///
+ public event Func InviteDeleted
+ {
+ add { _inviteDeletedEvent.Add(value); }
+ remove { _inviteDeletedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _inviteDeletedEvent = new AsyncEvent>();
}
}
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index 930ea1585..a8780a7b0 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
@@ -338,6 +338,9 @@ namespace Discord.WebSocket
client.UserIsTyping += (oldUser, newUser) => _userIsTypingEvent.InvokeAsync(oldUser, newUser);
client.RecipientAdded += (user) => _recipientAddedEvent.InvokeAsync(user);
client.RecipientRemoved += (user) => _recipientRemovedEvent.InvokeAsync(user);
+
+ client.InviteCreated += (invite) => _inviteCreatedEvent.InvokeAsync(invite);
+ client.InviteDeleted += (channel, invite) => _inviteDeletedEvent.InvokeAsync(channel, invite);
}
//IDiscordClient
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index 24ed8d5ff..dfdad99fc 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -1688,6 +1688,64 @@ namespace Discord.WebSocket
}
break;
+ //Invites
+ case "INVITE_CREATE":
+ {
+ await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_CREATE)").ConfigureAwait(false);
+
+ var data = (payload as JToken).ToObject(_serializer);
+ if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel)
+ {
+ var guild = channel.Guild;
+ if (!guild.IsSynced)
+ {
+ await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
+ return;
+ }
+
+ SocketGuildUser inviter = data.Inviter.IsSpecified
+ ? (guild.GetUser(data.Inviter.Value.Id) ?? guild.AddOrUpdateUser(data.Inviter.Value))
+ : null;
+
+ SocketUser target = data.TargetUser.IsSpecified
+ ? (guild.GetUser(data.TargetUser.Value.Id) ?? (SocketUser)SocketUnknownUser.Create(this, State, data.TargetUser.Value))
+ : null;
+
+ var invite = SocketInvite.Create(this, guild, channel, inviter, target, data);
+
+ await TimedInvokeAsync(_inviteCreatedEvent, nameof(InviteCreated), invite).ConfigureAwait(false);
+ }
+ else
+ {
+ await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
+ return;
+ }
+ }
+ break;
+ case "INVITE_DELETE":
+ {
+ await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_DELETE)").ConfigureAwait(false);
+
+ var data = (payload as JToken).ToObject(_serializer);
+ if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel)
+ {
+ var guild = channel.Guild;
+ if (!guild.IsSynced)
+ {
+ await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
+ return;
+ }
+
+ await TimedInvokeAsync(_inviteDeletedEvent, nameof(InviteDeleted), channel, data.Code).ConfigureAwait(false);
+ }
+ else
+ {
+ await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
+ return;
+ }
+ }
+ break;
+
//Ignored (User only)
case "CHANNEL_PINS_ACK":
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false);
diff --git a/src/Discord.Net.WebSocket/Entities/Invites/SocketInvite.cs b/src/Discord.Net.WebSocket/Entities/Invites/SocketInvite.cs
new file mode 100644
index 000000000..fa8c56599
--- /dev/null
+++ b/src/Discord.Net.WebSocket/Entities/Invites/SocketInvite.cs
@@ -0,0 +1,143 @@
+using Discord.Rest;
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using Model = Discord.API.Gateway.InviteCreateEvent;
+
+namespace Discord.WebSocket
+{
+ [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
+ public class SocketInvite : SocketEntity, IInviteMetadata
+ {
+ private long _createdAtTicks;
+
+ ///
+ public ulong ChannelId { get; private set; }
+ ///
+ /// Gets the channel where this invite was created.
+ ///
+ public SocketGuildChannel Channel { get; private set; }
+ ///
+ public ulong? GuildId { get; private set; }
+ ///
+ /// Gets the guild where this invite was created.
+ ///
+ public SocketGuild Guild { get; private set; }
+ ///
+ ChannelType IInvite.ChannelType
+ {
+ get
+ {
+ switch (Channel)
+ {
+ case IVoiceChannel voiceChannel: return ChannelType.Voice;
+ case ICategoryChannel categoryChannel: return ChannelType.Category;
+ case IDMChannel dmChannel: return ChannelType.DM;
+ case IGroupChannel groupChannel: return ChannelType.Group;
+ case SocketNewsChannel socketNewsChannel: return ChannelType.News;
+ case ITextChannel textChannel: return ChannelType.Text;
+ default: throw new InvalidOperationException("Invalid channel type.");
+ }
+ }
+ }
+ ///
+ string IInvite.ChannelName => Channel.Name;
+ ///
+ string IInvite.GuildName => Guild.Name;
+ ///
+ int? IInvite.PresenceCount => throw new NotImplementedException();
+ ///
+ int? IInvite.MemberCount => throw new NotImplementedException();
+ ///
+ bool IInviteMetadata.IsRevoked => throw new NotImplementedException();
+ ///
+ public bool IsTemporary { get; private set; }
+ ///
+ int? IInviteMetadata.MaxAge { get => MaxAge; }
+ ///
+ int? IInviteMetadata.MaxUses { get => MaxUses; }
+ ///
+ int? IInviteMetadata.Uses { get => Uses; }
+ ///
+ /// Gets the time (in seconds) until the invite expires.
+ ///
+ public int MaxAge { get; private set; }
+ ///
+ /// Gets the max number of uses this invite may have.
+ ///
+ public int MaxUses { get; private set; }
+ ///
+ /// Gets the number of times this invite has been used.
+ ///
+ public int Uses { get; private set; }
+ ///
+ /// Gets the user that created this invite if available.
+ ///
+ public SocketGuildUser Inviter { get; private set; }
+ ///
+ DateTimeOffset? IInviteMetadata.CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks);
+ ///
+ /// Gets when this invite was created.
+ ///
+ public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks);
+ ///
+ /// Gets the user targeted by this invite if available.
+ ///
+ public SocketUser TargetUser { get; private set; }
+ ///
+ /// Gets the type of the user targeted by this invite.
+ ///
+ public TargetUserType TargetUserType { get; private set; }
+
+ ///
+ public string Code => Id;
+ ///
+ public string Url => $"{DiscordConfig.InviteUrl}{Code}";
+
+ internal SocketInvite(DiscordSocketClient discord, SocketGuild guild, SocketGuildChannel channel, SocketGuildUser inviter, SocketUser target, string id)
+ : base(discord, id)
+ {
+ Guild = guild;
+ Channel = channel;
+ Inviter = inviter;
+ TargetUser = target;
+ }
+ internal static SocketInvite Create(DiscordSocketClient discord, SocketGuild guild, SocketGuildChannel channel, SocketGuildUser inviter, SocketUser target, Model model)
+ {
+ var entity = new SocketInvite(discord, guild, channel, inviter, target, model.Code);
+ entity.Update(model);
+ return entity;
+ }
+ internal void Update(Model model)
+ {
+ ChannelId = model.ChannelId;
+ GuildId = model.GuildId.IsSpecified ? model.GuildId.Value : Guild.Id;
+ IsTemporary = model.Temporary;
+ MaxAge = model.MaxAge;
+ MaxUses = model.MaxUses;
+ Uses = model.Uses;
+ _createdAtTicks = model.CreatedAt.UtcTicks;
+ TargetUserType = model.TargetUserType.IsSpecified ? model.TargetUserType.Value : TargetUserType.Undefined;
+ }
+
+ ///
+ public Task DeleteAsync(RequestOptions options = null)
+ => InviteHelper.DeleteAsync(this, Discord, options);
+
+ ///
+ /// Gets the URL of the invite.
+ ///
+ ///
+ /// A string that resolves to the Url of the invite.
+ ///
+ public override string ToString() => Url;
+ private string DebuggerDisplay => $"{Url} ({Guild?.Name} / {Channel.Name})";
+
+ ///
+ IGuild IInvite.Guild => Guild;
+ ///
+ IChannel IInvite.Channel => Channel;
+ ///
+ IUser IInviteMetadata.Inviter => Inviter;
+ }
+}