* 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>tags/2.3.0
| @@ -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); } | remove { _recipientRemovedEvent.Remove(value); } | ||||
| } | } | ||||
| internal readonly AsyncEvent<Func<SocketGroupUser, Task>> _recipientRemovedEvent = new AsyncEvent<Func<SocketGroupUser, Task>>(); | 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.UserIsTyping += (oldUser, newUser) => _userIsTypingEvent.InvokeAsync(oldUser, newUser); | ||||
| client.RecipientAdded += (user) => _recipientAddedEvent.InvokeAsync(user); | client.RecipientAdded += (user) => _recipientAddedEvent.InvokeAsync(user); | ||||
| client.RecipientRemoved += (user) => _recipientRemovedEvent.InvokeAsync(user); | client.RecipientRemoved += (user) => _recipientRemovedEvent.InvokeAsync(user); | ||||
| client.InviteCreated += (invite) => _inviteCreatedEvent.InvokeAsync(invite); | |||||
| client.InviteDeleted += (channel, invite) => _inviteDeletedEvent.InvokeAsync(channel, invite); | |||||
| } | } | ||||
| //IDiscordClient | //IDiscordClient | ||||
| @@ -1688,6 +1688,64 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| break; | 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) | //Ignored (User only) | ||||
| case "CHANNEL_PINS_ACK": | case "CHANNEL_PINS_ACK": | ||||
| await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); | 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; | |||||
| } | |||||
| } | |||||