* Add invite events (create and delete) * Removed unused using * Fixing IInviteMetadata properties * Add two new fields to the gateway event * Better event summary and remarks * Change how to assign to target variable Co-Authored-By: Joe4evr <jii.geugten@gmail.com> * Applying suggested changes to TargetUserType * Renaming NotDefined to Undefined * Fixing xml docs * Changed the summary style format Co-authored-by: Joe4evr <jii.geugten@gmail.com>pull/1671/head
@@ -0,0 +1,14 @@ | |||
namespace Discord | |||
{ | |||
public enum TargetUserType | |||
{ | |||
/// <summary> | |||
/// The invite whose target user type is not defined. | |||
/// </summary> | |||
Undefined = 0, | |||
/// <summary> | |||
/// The invite is for a Go Live stream. | |||
/// </summary> | |||
Stream = 1 | |||
} | |||
} |
@@ -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<ulong> GuildId { get; set; } | |||
[JsonProperty("inviter")] | |||
public Optional<User> Inviter { get; set; } | |||
[JsonProperty("max_age")] | |||
public int MaxAge { get; set; } | |||
[JsonProperty("max_uses")] | |||
public int MaxUses { get; set; } | |||
[JsonProperty("target_user")] | |||
public Optional<User> TargetUser { get; set; } | |||
[JsonProperty("target_user_type")] | |||
public Optional<TargetUserType> TargetUserType { get; set; } | |||
[JsonProperty("temporary")] | |||
public bool Temporary { get; set; } | |||
[JsonProperty("uses")] | |||
public int Uses { get; set; } | |||
} | |||
} |
@@ -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<ulong> GuildId { get; set; } | |||
} | |||
} |
@@ -389,5 +389,47 @@ namespace Discord.WebSocket | |||
remove { _recipientRemovedEvent.Remove(value); } | |||
} | |||
internal readonly AsyncEvent<Func<SocketGroupUser, Task>> _recipientRemovedEvent = new AsyncEvent<Func<SocketGroupUser, Task>>(); | |||
//Invites | |||
/// <summary> | |||
/// Fired when an invite is created. | |||
/// </summary> | |||
/// <remarks> | |||
/// <para> | |||
/// This event is fired when an invite is created. The event handler must return a | |||
/// <see cref="Task"/> and accept a <see cref="SocketInvite"/> as its parameter. | |||
/// </para> | |||
/// <para> | |||
/// The invite created will be passed into the <see cref="SocketInvite"/> parameter. | |||
/// </para> | |||
/// </remarks> | |||
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> | |||
/// <remarks> | |||
/// <para> | |||
/// This event is fired when an invite is deleted. The event handler must return | |||
/// a <see cref="Task"/> and accept a <see cref="SocketGuildChannel"/> and | |||
/// <see cref="string"/> as its parameter. | |||
/// </para> | |||
/// <para> | |||
/// The channel where this invite was created will be passed into the <see cref="SocketGuildChannel"/> parameter. | |||
/// </para> | |||
/// <para> | |||
/// The code of the deleted invite will be passed into the <see cref="string"/> parameter. | |||
/// </para> | |||
/// </remarks> | |||
public event Func<SocketGuildChannel, string, Task> InviteDeleted | |||
{ | |||
add { _inviteDeletedEvent.Add(value); } | |||
remove { _inviteDeletedEvent.Remove(value); } | |||
} | |||
internal readonly AsyncEvent<Func<SocketGuildChannel, string, Task>> _inviteDeletedEvent = new AsyncEvent<Func<SocketGuildChannel, string, Task>>(); | |||
} | |||
} |
@@ -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 | |||
@@ -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<API.Gateway.InviteCreateEvent>(_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<API.Gateway.InviteDeleteEvent>(_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); | |||
@@ -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<string>, IInviteMetadata | |||
{ | |||
private long _createdAtTicks; | |||
/// <inheritdoc /> | |||
public ulong ChannelId { get; private set; } | |||
/// <summary> | |||
/// Gets the channel where this invite was created. | |||
/// </summary> | |||
public SocketGuildChannel Channel { get; private set; } | |||
/// <inheritdoc /> | |||
public ulong? GuildId { get; private set; } | |||
/// <summary> | |||
/// Gets the guild where this invite was created. | |||
/// </summary> | |||
public SocketGuild Guild { get; private set; } | |||
/// <inheritdoc /> | |||
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."); | |||
} | |||
} | |||
} | |||
/// <inheritdoc /> | |||
string IInvite.ChannelName => Channel.Name; | |||
/// <inheritdoc /> | |||
string IInvite.GuildName => Guild.Name; | |||
/// <inheritdoc /> | |||
int? IInvite.PresenceCount => throw new NotImplementedException(); | |||
/// <inheritdoc /> | |||
int? IInvite.MemberCount => throw new NotImplementedException(); | |||
/// <inheritdoc /> | |||
bool IInviteMetadata.IsRevoked => throw new NotImplementedException(); | |||
/// <inheritdoc /> | |||
public bool IsTemporary { get; private set; } | |||
/// <inheritdoc /> | |||
int? IInviteMetadata.MaxAge { get => MaxAge; } | |||
/// <inheritdoc /> | |||
int? IInviteMetadata.MaxUses { get => MaxUses; } | |||
/// <inheritdoc /> | |||
int? IInviteMetadata.Uses { get => Uses; } | |||
/// <summary> | |||
/// Gets the time (in seconds) until the invite expires. | |||
/// </summary> | |||
public int MaxAge { get; private set; } | |||
/// <summary> | |||
/// Gets the max number of uses this invite may have. | |||
/// </summary> | |||
public int MaxUses { get; private set; } | |||
/// <summary> | |||
/// Gets the number of times this invite has been used. | |||
/// </summary> | |||
public int Uses { get; private set; } | |||
/// <summary> | |||
/// Gets the user that created this invite if available. | |||
/// </summary> | |||
public SocketGuildUser Inviter { get; private set; } | |||
/// <inheritdoc /> | |||
DateTimeOffset? IInviteMetadata.CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); | |||
/// <summary> | |||
/// Gets when this invite was created. | |||
/// </summary> | |||
public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); | |||
/// <summary> | |||
/// Gets the user targeted by this invite if available. | |||
/// </summary> | |||
public SocketUser TargetUser { get; private set; } | |||
/// <summary> | |||
/// Gets the type of the user targeted by this invite. | |||
/// </summary> | |||
public TargetUserType TargetUserType { get; private set; } | |||
/// <inheritdoc /> | |||
public string Code => Id; | |||
/// <inheritdoc /> | |||
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; | |||
} | |||
/// <inheritdoc /> | |||
public Task DeleteAsync(RequestOptions options = null) | |||
=> InviteHelper.DeleteAsync(this, Discord, options); | |||
/// <summary> | |||
/// Gets the URL of the invite. | |||
/// </summary> | |||
/// <returns> | |||
/// A string that resolves to the Url of the invite. | |||
/// </returns> | |||
public override string ToString() => Url; | |||
private string DebuggerDisplay => $"{Url} ({Guild?.Name} / {Channel.Name})"; | |||
/// <inheritdoc /> | |||
IGuild IInvite.Guild => Guild; | |||
/// <inheritdoc /> | |||
IChannel IInvite.Channel => Channel; | |||
/// <inheritdoc /> | |||
IUser IInviteMetadata.Inviter => Inviter; | |||
} | |||
} |