Browse Source

Fix: Integration model from GuildIntegration and added INTEGRATION gateway events (#2168)

* fix integration models; add integration events

* fix description on IGUILD for integration

* fix typo in integration documentation

* fix documentation in connection visibility

* removed public identitiers from app and connection

* Removed REST endpoints that are not part of the API.

* Added documentation for rest integrations

* added optional types

* Fixed rest interaction field with not being IsSpecified
tags/3.5.0
FeroxFoxxo GitHub 3 years ago
parent
commit
305d7f9e13
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 596 additions and 259 deletions
  1. +0
    -21
      src/Discord.Net.Core/Entities/Guilds/GuildIntegrationProperties.cs
  2. +19
    -2
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  3. +0
    -18
      src/Discord.Net.Core/Entities/Guilds/IntegrationAccount.cs
  4. +35
    -12
      src/Discord.Net.Core/Entities/Integrations/IIntegration.cs
  5. +23
    -0
      src/Discord.Net.Core/Entities/Integrations/IIntegrationAccount.cs
  6. +33
    -0
      src/Discord.Net.Core/Entities/Integrations/IIntegrationApplication.cs
  7. +17
    -0
      src/Discord.Net.Core/Entities/Integrations/IntegrationExpireBehavior.cs
  8. +17
    -0
      src/Discord.Net.Core/Entities/Users/ConnectionVisibility.cs
  9. +44
    -15
      src/Discord.Net.Core/Entities/Users/IConnection.cs
  10. +13
    -5
      src/Discord.Net.Rest/API/Common/Connection.cs
  11. +18
    -7
      src/Discord.Net.Rest/API/Common/Integration.cs
  12. +1
    -1
      src/Discord.Net.Rest/API/Common/IntegrationAccount.cs
  13. +20
    -0
      src/Discord.Net.Rest/API/Common/IntegrationApplication.cs
  14. +1
    -1
      src/Discord.Net.Rest/ClientHelper.cs
  15. +3
    -36
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  16. +6
    -10
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  17. +6
    -6
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  18. +0
    -104
      src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs
  19. +102
    -0
      src/Discord.Net.Rest/Entities/Integrations/RestIntegration.cs
  20. +29
    -0
      src/Discord.Net.Rest/Entities/Integrations/RestIntegrationAccount.cs
  21. +39
    -0
      src/Discord.Net.Rest/Entities/Integrations/RestIntegrationApplication.cs
  22. +38
    -15
      src/Discord.Net.Rest/Entities/Users/RestConnection.cs
  23. +14
    -0
      src/Discord.Net.WebSocket/API/Gateway/IntegrationDeletedEvent.cs
  24. +26
    -0
      src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
  25. +86
    -0
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  26. +6
    -6
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs

+ 0
- 21
src/Discord.Net.Core/Entities/Guilds/GuildIntegrationProperties.cs View File

@@ -1,21 +0,0 @@
namespace Discord
{
/// <summary>
/// Provides properties used to modify an <see cref="IGuildIntegration" /> with the specified changes.
/// </summary>
public class GuildIntegrationProperties
{
/// <summary>
/// Gets or sets the behavior when an integration subscription lapses.
/// </summary>
public Optional<int> ExpireBehavior { get; set; }
/// <summary>
/// Gets or sets the period (in seconds) where the integration will ignore lapsed subscriptions.
/// </summary>
public Optional<int> ExpireGracePeriod { get; set; }
/// <summary>
/// Gets or sets whether emoticons should be synced for this integration.
/// </summary>
public Optional<bool> EnableEmoticons { get; set; }
}
}

+ 19
- 2
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -718,8 +718,25 @@ namespace Discord
/// </returns>
Task<IReadOnlyCollection<IVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null);

Task<IReadOnlyCollection<IGuildIntegration>> GetIntegrationsAsync(RequestOptions options = null);
Task<IGuildIntegration> CreateIntegrationAsync(ulong id, string type, RequestOptions options = null);
/// <summary>
/// Gets a collection of all the integrations this guild contains.
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// integrations the guild can has.
/// </returns>
Task<IReadOnlyCollection<IIntegration>> GetIntegrationsAsync(RequestOptions options = null);

/// <summary>
/// Deletes an integration.
/// </summary>
/// <param name="id">The id for the integration.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous removal operation.
/// </returns>
Task DeleteIntegrationAsync(ulong id, RequestOptions options = null);

/// <summary>
/// Gets a collection of all invites in this guild.


+ 0
- 18
src/Discord.Net.Core/Entities/Guilds/IntegrationAccount.cs View File

@@ -1,18 +0,0 @@
using System.Diagnostics;

namespace Discord
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public struct IntegrationAccount
{
/// <summary> Gets the ID of the account. </summary>
/// <returns> A <see cref="string"/> unique identifier of this integration account. </returns>
public string Id { get; }
/// <summary> Gets the name of the account. </summary>
/// <returns> A string containing the name of this integration account. </returns>
public string Name { get; private set; }

public override string ToString() => Name;
private string DebuggerDisplay => $"{Name} ({Id})";
}
}

src/Discord.Net.Core/Entities/Guilds/IGuildIntegration.cs → src/Discord.Net.Core/Entities/Integrations/IIntegration.cs View File

@@ -3,15 +3,16 @@ using System;
namespace Discord
{
/// <summary>
/// Holds information for a guild integration feature.
/// Holds information for an integration feature.
/// Nullable fields not provided for Discord bot integrations, but are for Twitch etc.
/// </summary>
public interface IGuildIntegration
public interface IIntegration
{
/// <summary>
/// Gets the integration ID.
/// </summary>
/// <returns>
/// An <see cref="UInt64"/> representing the unique identifier value of this integration.
/// A <see cref="ulong"/> representing the unique identifier value of this integration.
/// </returns>
ulong Id { get; }
/// <summary>
@@ -45,30 +46,52 @@ namespace Discord
/// <returns>
/// <c>true</c> if this integration is syncing; otherwise <c>false</c>.
/// </returns>
bool IsSyncing { get; }
bool? IsSyncing { get; }
/// <summary>
/// Gets the ID that this integration uses for "subscribers".
/// </summary>
ulong ExpireBehavior { get; }
ulong? RoleId { get; }
/// <summary>
/// Gets whether emoticons should be synced for this integration (twitch only currently).
/// </summary>
bool? HasEnabledEmoticons { get; }
/// <summary>
/// Gets the behavior of expiring subscribers.
/// </summary>
IntegrationExpireBehavior? ExpireBehavior { get; }
/// <summary>
/// Gets the grace period before expiring "subscribers".
/// </summary>
ulong ExpireGracePeriod { get; }
int? ExpireGracePeriod { get; }
/// <summary>
/// Gets the user for this integration.
/// </summary>
IUser User { get; }
/// <summary>
/// Gets integration account information.
/// </summary>
IIntegrationAccount Account { get; }
/// <summary>
/// Gets when this integration was last synced.
/// </summary>
/// <returns>
/// A <see cref="DateTimeOffset"/> containing a date and time of day when the integration was last synced.
/// </returns>
DateTimeOffset SyncedAt { get; }
DateTimeOffset? SyncedAt { get; }
/// <summary>
/// Gets integration account information.
/// Gets how many subscribers this integration has.
/// </summary>
IntegrationAccount Account { get; }

int? SubscriberCount { get; }
/// <summary>
/// Gets whether this integration been revoked.
/// </summary>
bool? IsRevoked { get; }
/// <summary>
/// Gets the bot/OAuth2 application for a discord integration.
/// </summary>
IIntegrationApplication Application { get; }
IGuild Guild { get; }
ulong GuildId { get; }
ulong RoleId { get; }
IUser User { get; }
}
}

+ 23
- 0
src/Discord.Net.Core/Entities/Integrations/IIntegrationAccount.cs View File

@@ -0,0 +1,23 @@
namespace Discord
{
/// <summary>
/// Provides the account information for an <see cref="IIntegration" />.
/// </summary>
public interface IIntegrationAccount
{
/// <summary>
/// Gets the ID of the account.
/// </summary>
/// <returns>
/// A <see cref="string"/> unique identifier of this integration account.
/// </returns>
string Id { get; }
/// <summary>
/// Gets the name of the account.
/// </summary>
/// <returns>
/// A string containing the name of this integration account.
/// </returns>
string Name { get; }
}
}

+ 33
- 0
src/Discord.Net.Core/Entities/Integrations/IIntegrationApplication.cs View File

@@ -0,0 +1,33 @@
namespace Discord
{
/// <summary>
/// Provides the bot/OAuth2 application for an <see cref="IIntegration" />.
/// </summary>
public interface IIntegrationApplication
{
/// <summary>
/// Gets the id of the app.
/// </summary>
ulong Id { get; }
/// <summary>
/// Gets the name of the app.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the icon hash of the app.
/// </summary>
string Icon { get; }
/// <summary>
/// Gets the description of the app.
/// </summary>
string Description { get; }
/// <summary>
/// Gets the summary of the app.
/// </summary>
string Summary { get; }
/// <summary>
/// Gets the bot associated with this application.
/// </summary>
IUser Bot { get; }
}
}

+ 17
- 0
src/Discord.Net.Core/Entities/Integrations/IntegrationExpireBehavior.cs View File

@@ -0,0 +1,17 @@
namespace Discord
{
/// <summary>
/// The behavior of expiring subscribers for an <see cref="IIntegration" />.
/// </summary>
public enum IntegrationExpireBehavior
{
/// <summary>
/// Removes a role from an expired subscriber.
/// </summary>
RemoveRole = 0,
/// <summary>
/// Kicks an expired subscriber from the guild.
/// </summary>
Kick = 1
}
}

+ 17
- 0
src/Discord.Net.Core/Entities/Users/ConnectionVisibility.cs View File

@@ -0,0 +1,17 @@
namespace Discord
{
/// <summary>
/// The visibility of the connected account.
/// </summary>
public enum ConnectionVisibility
{
/// <summary>
/// Invisible to everyone except the user themselves.
/// </summary>
None = 0,
/// <summary>
/// Visible to everyone.
/// </summary>
Everyone = 1
}
}

+ 44
- 15
src/Discord.Net.Core/Entities/Users/IConnection.cs View File

@@ -4,24 +4,53 @@ namespace Discord
{
public interface IConnection
{
/// <summary> Gets the ID of the connection account. </summary>
/// <returns> A <see cref="string"/> representing the unique identifier value of this connection. </returns>
/// <summary>
/// Gets the ID of the connection account.
/// </summary>
/// <returns>
/// A <see cref="string"/> representing the unique identifier value of this connection.
/// </returns>
string Id { get; }
/// <summary> Gets the service of the connection (twitch, youtube). </summary>
/// <returns> A string containing the name of this type of connection. </returns>
string Type { get; }
/// <summary> Gets the username of the connection account. </summary>
/// <returns> A string containing the name of this connection. </returns>
/// <summary>
/// Gets the username of the connection account.
/// </summary>
/// <returns>
/// A string containing the name of this connection.
/// </returns>
string Name { get; }
/// <summary> Gets whether the connection is revoked. </summary>
/// <returns> A value which if true indicates that this connection has been revoked, otherwise false. </returns>
bool IsRevoked { get; }

/// <summary> Gets a <see cref="IReadOnlyCollection{T}"/> of integration IDs. </summary>
/// <summary>
/// Gets the service of the connection (twitch, youtube).
/// </summary>
/// <returns>
/// A string containing the name of this type of connection.
/// </returns>
string Type { get; }
/// <summary>
/// Gets whether the connection is revoked.
/// </summary>
/// <returns>
/// An <see cref="IReadOnlyCollection{T}"/> containing <see cref="ulong"/>
/// representations of unique identifier values of integrations.
/// A value which if true indicates that this connection has been revoked, otherwise false.
/// </returns>
IReadOnlyCollection<ulong> IntegrationIds { get; }
bool? IsRevoked { get; }
/// <summary>
/// Gets a <see cref="IReadOnlyCollection{T}"/> of integration parials.
/// </summary>
IReadOnlyCollection<IIntegration> Integrations { get; }
/// <summary>
/// Gets whether the connection is verified.
/// </summary>
bool Verified { get; }
/// <summary>
/// Gets whether friend sync is enabled for this connection.
/// </summary>
bool FriendSync { get; }
/// <summary>
/// Gets whether activities related to this connection will be shown in presence updates.
/// </summary>
bool ShowActivity { get; }
/// <summary>
/// Visibility of this connection.
/// </summary>
ConnectionVisibility Visibility { get; }
}
}

+ 13
- 5
src/Discord.Net.Rest/API/Common/Connection.cs View File

@@ -7,14 +7,22 @@ namespace Discord.API
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("revoked")]
public bool Revoked { get; set; }

public Optional<bool> Revoked { get; set; }
[JsonProperty("integrations")]
public IReadOnlyCollection<ulong> Integrations { get; set; }
public Optional<IReadOnlyCollection<Integration>> Integrations { get; set; }
[JsonProperty("verified")]
public bool Verified { get; set; }
[JsonProperty("friend_sync")]
public bool FriendSync { get; set; }
[JsonProperty("show_activity")]
public bool ShowActivity { get; set; }
[JsonProperty("visibility")]
public ConnectionVisibility Visibility { get; set; }

}
}

+ 18
- 7
src/Discord.Net.Rest/API/Common/Integration.cs View File

@@ -5,6 +5,9 @@ namespace Discord.API
{
internal class Integration
{
[JsonProperty("guild_id")]
public Optional<ulong> GuildId { get; set; }

[JsonProperty("id")]
public ulong Id { get; set; }
[JsonProperty("name")]
@@ -14,18 +17,26 @@ namespace Discord.API
[JsonProperty("enabled")]
public bool Enabled { get; set; }
[JsonProperty("syncing")]
public bool Syncing { get; set; }
public Optional<bool?> Syncing { get; set; }
[JsonProperty("role_id")]
public ulong RoleId { get; set; }
public Optional<ulong?> RoleId { get; set; }
[JsonProperty("enable_emoticons")]
public Optional<bool?> EnableEmoticons { get; set; }
[JsonProperty("expire_behavior")]
public ulong ExpireBehavior { get; set; }
public Optional<IntegrationExpireBehavior> ExpireBehavior { get; set; }
[JsonProperty("expire_grace_period")]
public ulong ExpireGracePeriod { get; set; }
public Optional<int?> ExpireGracePeriod { get; set; }
[JsonProperty("user")]
public User User { get; set; }
public Optional<User> User { get; set; }
[JsonProperty("account")]
public IntegrationAccount Account { get; set; }
public Optional<IntegrationAccount> Account { get; set; }
[JsonProperty("synced_at")]
public DateTimeOffset SyncedAt { get; set; }
public Optional<DateTimeOffset> SyncedAt { get; set; }
[JsonProperty("subscriber_count")]
public Optional<int?> SubscriberAccount { get; set; }
[JsonProperty("revoked")]
public Optional<bool?> Revoked { get; set; }
[JsonProperty("application")]
public Optional<IntegrationApplication> Application { get; set; }
}
}

+ 1
- 1
src/Discord.Net.Rest/API/Common/IntegrationAccount.cs View File

@@ -5,7 +5,7 @@ namespace Discord.API
internal class IntegrationAccount
{
[JsonProperty("id")]
public ulong Id { get; set; }
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}


+ 20
- 0
src/Discord.Net.Rest/API/Common/IntegrationApplication.cs View File

@@ -0,0 +1,20 @@
using Newtonsoft.Json;

namespace Discord.API
{
internal class IntegrationApplication
{
[JsonProperty("id")]
public ulong Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("icon")]
public Optional<string> Icon { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("summary")]
public string Summary { get; set; }
[JsonProperty("bot")]
public Optional<User> Bot { get; set; }
}
}

+ 1
- 1
src/Discord.Net.Rest/ClientHelper.cs View File

@@ -49,7 +49,7 @@ namespace Discord.Rest
public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(BaseDiscordClient client, RequestOptions options)
{
var models = await client.ApiClient.GetMyConnectionsAsync(options).ConfigureAwait(false);
return models.Select(RestConnection.Create).ToImmutableArray();
return models.Select(model => RestConnection.Create(client, model)).ToImmutableArray();
}

public static async Task<RestInviteMetadata> GetInviteAsync(BaseDiscordClient client,


+ 3
- 36
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -1626,7 +1626,7 @@ namespace Discord.API

#region Guild Integrations
/// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception>
public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null)
public async Task<IReadOnlyCollection<Integration>> GetIntegrationsAsync(ulong guildId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);
@@ -1634,47 +1634,14 @@ namespace Discord.API
var ids = new BucketIds(guildId: guildId);
return await SendAsync<IReadOnlyCollection<Integration>>("GET", () => $"guilds/{guildId}/integrations", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException"><paramref name="guildId"/> and <paramref name="args.Id"/> must not be equal to zero.</exception>
/// <exception cref="ArgumentNullException"><paramref name="args"/> must not be <see langword="null"/>.</exception>
public async Task<Integration> CreateGuildIntegrationAsync(ulong guildId, CreateGuildIntegrationParams args, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args));
Preconditions.NotEqual(args.Id, 0, nameof(args.Id));
options = RequestOptions.CreateOrClone(options);

var ids = new BucketIds(guildId: guildId);
return await SendAsync<Integration>("POST", () => $"guilds/{guildId}/integrations", ids, options: options).ConfigureAwait(false);
}
public async Task<Integration> DeleteGuildIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(integrationId, 0, nameof(integrationId));
options = RequestOptions.CreateOrClone(options);

var ids = new BucketIds(guildId: guildId);
return await SendAsync<Integration>("DELETE", () => $"guilds/{guildId}/integrations/{integrationId}", ids, options: options).ConfigureAwait(false);
}
public async Task<Integration> ModifyGuildIntegrationAsync(ulong guildId, ulong integrationId, Rest.ModifyGuildIntegrationParams args, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(integrationId, 0, nameof(integrationId));
Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior));
Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod));
options = RequestOptions.CreateOrClone(options);

var ids = new BucketIds(guildId: guildId);
return await SendJsonAsync<Integration>("PATCH", () => $"guilds/{guildId}/integrations/{integrationId}", args, ids, options: options).ConfigureAwait(false);
}
public async Task<Integration> SyncGuildIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null)
public async Task DeleteIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(integrationId, 0, nameof(integrationId));
options = RequestOptions.CreateOrClone(options);

var ids = new BucketIds(guildId: guildId);
return await SendAsync<Integration>("POST", () => $"guilds/{guildId}/integrations/{integrationId}/sync", ids, options: options).ConfigureAwait(false);
await SendAsync("DELETE", () => $"guilds/{guildId}/integrations/{integrationId}", ids, options: options).ConfigureAwait(false);
}
#endregion



+ 6
- 10
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -305,19 +305,15 @@ namespace Discord.Rest
#endregion

#region Integrations
public static async Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client,
public static async Task<IReadOnlyCollection<RestIntegration>> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client,
RequestOptions options)
{
var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id, options).ConfigureAwait(false);
return models.Select(x => RestGuildIntegration.Create(client, guild, x)).ToImmutableArray();
}
public static async Task<RestGuildIntegration> CreateIntegrationAsync(IGuild guild, BaseDiscordClient client,
ulong id, string type, RequestOptions options)
{
var args = new CreateGuildIntegrationParams(id, type);
var model = await client.ApiClient.CreateGuildIntegrationAsync(guild.Id, args, options).ConfigureAwait(false);
return RestGuildIntegration.Create(client, guild, model);
var models = await client.ApiClient.GetIntegrationsAsync(guild.Id, options).ConfigureAwait(false);
return models.Select(x => RestIntegration.Create(client, guild, x)).ToImmutableArray();
}
public static async Task DeleteIntegrationAsync(IGuild guild, BaseDiscordClient client, ulong id,
RequestOptions options) =>
await client.ApiClient.DeleteIntegrationAsync(guild.Id, id, options).ConfigureAwait(false);
#endregion

#region Interactions


+ 6
- 6
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -720,10 +720,10 @@ namespace Discord.Rest
#endregion

#region Integrations
public Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(RequestOptions options = null)
public Task<IReadOnlyCollection<RestIntegration>> GetIntegrationsAsync(RequestOptions options = null)
=> GuildHelper.GetIntegrationsAsync(this, Discord, options);
public Task<RestGuildIntegration> CreateIntegrationAsync(ulong id, string type, RequestOptions options = null)
=> GuildHelper.CreateIntegrationAsync(this, Discord, id, type, options);
public Task DeleteIntegrationAsync(ulong id, RequestOptions options = null)
=> GuildHelper.DeleteIntegrationAsync(this, Discord, id, options);
#endregion

#region Invites
@@ -1370,11 +1370,11 @@ namespace Discord.Rest
=> await GetVoiceRegionsAsync(options).ConfigureAwait(false);

/// <inheritdoc />
async Task<IReadOnlyCollection<IGuildIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options)
async Task<IReadOnlyCollection<IIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options)
=> await GetIntegrationsAsync(options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IGuildIntegration> IGuild.CreateIntegrationAsync(ulong id, string type, RequestOptions options)
=> await CreateIntegrationAsync(id, type, options).ConfigureAwait(false);
async Task IGuild.DeleteIntegrationAsync(ulong id, RequestOptions options)
=> await DeleteIntegrationAsync(id, options).ConfigureAwait(false);

/// <inheritdoc />
async Task<IReadOnlyCollection<IInviteMetadata>> IGuild.GetInvitesAsync(RequestOptions options)


+ 0
- 104
src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs View File

@@ -1,104 +0,0 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Model = Discord.API.Integration;

namespace Discord.Rest
{
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestGuildIntegration : RestEntity<ulong>, IGuildIntegration
{
private long _syncedAtTicks;

/// <inheritdoc />
public string Name { get; private set; }
/// <inheritdoc />
public string Type { get; private set; }
/// <inheritdoc />
public bool IsEnabled { get; private set; }
/// <inheritdoc />
public bool IsSyncing { get; private set; }
/// <inheritdoc />
public ulong ExpireBehavior { get; private set; }
/// <inheritdoc />
public ulong ExpireGracePeriod { get; private set; }
/// <inheritdoc />
public ulong GuildId { get; private set; }
/// <inheritdoc />
public ulong RoleId { get; private set; }
public RestUser User { get; private set; }
/// <inheritdoc />
public IntegrationAccount Account { get; private set; }
internal IGuild Guild { get; private set; }

/// <inheritdoc />
public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks);

internal RestGuildIntegration(BaseDiscordClient discord, IGuild guild, ulong id)
: base(discord, id)
{
Guild = guild;
}
internal static RestGuildIntegration Create(BaseDiscordClient discord, IGuild guild, Model model)
{
var entity = new RestGuildIntegration(discord, guild, model.Id);
entity.Update(model);
return entity;
}

internal void Update(Model model)
{
Name = model.Name;
Type = model.Type;
IsEnabled = model.Enabled;
IsSyncing = model.Syncing;
ExpireBehavior = model.ExpireBehavior;
ExpireGracePeriod = model.ExpireGracePeriod;
_syncedAtTicks = model.SyncedAt.UtcTicks;

RoleId = model.RoleId;
User = RestUser.Create(Discord, model.User);
}
public async Task DeleteAsync()
{
await Discord.ApiClient.DeleteGuildIntegrationAsync(GuildId, Id).ConfigureAwait(false);
}
public async Task ModifyAsync(Action<GuildIntegrationProperties> func)
{
if (func == null) throw new NullReferenceException(nameof(func));

var args = new GuildIntegrationProperties();
func(args);
var apiArgs = new API.Rest.ModifyGuildIntegrationParams
{
EnableEmoticons = args.EnableEmoticons,
ExpireBehavior = args.ExpireBehavior,
ExpireGracePeriod = args.ExpireGracePeriod
};
var model = await Discord.ApiClient.ModifyGuildIntegrationAsync(GuildId, Id, apiArgs).ConfigureAwait(false);

Update(model);
}
public async Task SyncAsync()
{
await Discord.ApiClient.SyncGuildIntegrationAsync(GuildId, Id).ConfigureAwait(false);
}

public override string ToString() => Name;
private string DebuggerDisplay => $"{Name} ({Id}{(IsEnabled ? ", Enabled" : "")})";

/// <inheritdoc />
IGuild IGuildIntegration.Guild
{
get
{
if (Guild != null)
return Guild;
throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object.");
}
}
/// <inheritdoc />
IUser IGuildIntegration.User => User;
}
}

+ 102
- 0
src/Discord.Net.Rest/Entities/Integrations/RestIntegration.cs View File

@@ -0,0 +1,102 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Model = Discord.API.Integration;

namespace Discord.Rest
{
/// <summary>
/// Represents a Rest-based implementation of <see cref="IIntegration"/>.
/// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestIntegration : RestEntity<ulong>, IIntegration
{
private long? _syncedAtTicks;

/// <inheritdoc />
public string Name { get; private set; }
/// <inheritdoc />
public string Type { get; private set; }
/// <inheritdoc />
public bool IsEnabled { get; private set; }
/// <inheritdoc />
public bool? IsSyncing { get; private set; }
/// <inheritdoc />
public ulong? RoleId { get; private set; }
/// <inheritdoc />
public bool? HasEnabledEmoticons { get; private set; }
/// <inheritdoc />
public IntegrationExpireBehavior? ExpireBehavior { get; private set; }
/// <inheritdoc />
public int? ExpireGracePeriod { get; private set; }
/// <inheritdoc />
IUser IIntegration.User => User;
/// <inheritdoc />
public IIntegrationAccount Account { get; private set; }
/// <inheritdoc />
public DateTimeOffset? SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks);
/// <inheritdoc />
public int? SubscriberCount { get; private set; }
/// <inheritdoc />
public bool? IsRevoked { get; private set; }
/// <inheritdoc />
public IIntegrationApplication Application { get; private set; }

internal IGuild Guild { get; private set; }
public RestUser User { get; private set; }

internal RestIntegration(BaseDiscordClient discord, IGuild guild, ulong id)
: base(discord, id)
{
Guild = guild;
}
internal static RestIntegration Create(BaseDiscordClient discord, IGuild guild, Model model)
{
var entity = new RestIntegration(discord, guild, model.Id);
entity.Update(model);
return entity;
}

internal void Update(Model model)
{
Name = model.Name;
Type = model.Type;
IsEnabled = model.Enabled;

IsSyncing = model.Syncing.IsSpecified ? model.Syncing.Value : null;
RoleId = model.RoleId.IsSpecified ? model.RoleId.Value : null;
HasEnabledEmoticons = model.EnableEmoticons.IsSpecified ? model.EnableEmoticons.Value : null;
ExpireBehavior = model.ExpireBehavior.IsSpecified ? model.ExpireBehavior.Value : null;
ExpireGracePeriod = model.ExpireGracePeriod.IsSpecified ? model.ExpireGracePeriod.Value : null;
User = model.User.IsSpecified ? RestUser.Create(Discord, model.User.Value) : null;
Account = model.Account.IsSpecified ? RestIntegrationAccount.Create(model.Account.Value) : null;
SubscriberCount = model.SubscriberAccount.IsSpecified ? model.SubscriberAccount.Value : null;
IsRevoked = model.Revoked.IsSpecified ? model.Revoked.Value : null;
Application = model.Application.IsSpecified ? RestIntegrationApplication.Create(Discord, model.Application.Value) : null;

_syncedAtTicks = model.SyncedAt.IsSpecified ? model.SyncedAt.Value.UtcTicks : null;
}
public async Task DeleteAsync()
{
await Discord.ApiClient.DeleteIntegrationAsync(GuildId, Id).ConfigureAwait(false);
}

public override string ToString() => Name;
private string DebuggerDisplay => $"{Name} ({Id}{(IsEnabled ? ", Enabled" : "")})";

/// <inheritdoc />
public ulong GuildId { get; private set; }

/// <inheritdoc />
IGuild IIntegration.Guild
{
get
{
if (Guild != null)
return Guild;
throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object.");
}
}
}
}

+ 29
- 0
src/Discord.Net.Rest/Entities/Integrations/RestIntegrationAccount.cs View File

@@ -0,0 +1,29 @@
using Model = Discord.API.IntegrationAccount;

namespace Discord.Rest
{
/// <summary>
/// Represents a Rest-based implementation of <see cref="IIntegrationAccount"/>.
/// </summary>
public class RestIntegrationAccount : IIntegrationAccount
{
internal RestIntegrationAccount() { }

public string Id { get; private set; }

public string Name { get; private set; }

internal static RestIntegrationAccount Create(Model model)
{
var entity = new RestIntegrationAccount();
entity.Update(model);
return entity;
}

internal void Update(Model model)
{
model.Name = Name;
model.Id = Id;
}
}
}

+ 39
- 0
src/Discord.Net.Rest/Entities/Integrations/RestIntegrationApplication.cs View File

@@ -0,0 +1,39 @@
using Model = Discord.API.IntegrationApplication;

namespace Discord.Rest
{
/// <summary>
/// Represents a Rest-based implementation of <see cref="IIntegrationApplication"/>.
/// </summary>
public class RestIntegrationApplication : RestEntity<ulong>, IIntegrationApplication
{
public string Name { get; private set; }

public string Icon { get; private set; }

public string Description { get; private set; }

public string Summary { get; private set; }

public IUser Bot { get; private set; }

internal RestIntegrationApplication(BaseDiscordClient discord, ulong id)
: base(discord, id) { }

internal static RestIntegrationApplication Create(BaseDiscordClient discord, Model model)
{
var entity = new RestIntegrationApplication(discord, model.Id);
entity.Update(model);
return entity;
}

internal void Update(Model model)
{
Name = model.Name;
Icon = model.Icon.IsSpecified ? model.Icon.Value : null;
Description = model.Description;
Summary = model.Summary;
Bot = RestUser.Create(Discord, model.Bot.Value);
}
}
}

+ 38
- 15
src/Discord.Net.Rest/Entities/Users/RestConnection.cs View File

@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Model = Discord.API.Connection;

namespace Discord.Rest
@@ -9,28 +11,49 @@ namespace Discord.Rest
public class RestConnection : IConnection
{
/// <inheritdoc />
public string Id { get; }
public string Id { get; private set; }
/// <inheritdoc />
public string Type { get; }
public string Name { get; private set; }
/// <inheritdoc />
public string Name { get; }
public string Type { get; private set; }
/// <inheritdoc />
public bool IsRevoked { get; }
public bool? IsRevoked { get; private set; }
/// <inheritdoc />
public IReadOnlyCollection<ulong> IntegrationIds { get; }
public IReadOnlyCollection<IIntegration> Integrations { get; private set; }
/// <inheritdoc />
public bool Verified { get; private set; }
/// <inheritdoc />
public bool FriendSync { get; private set; }
/// <inheritdoc />
public bool ShowActivity { get; private set; }
/// <inheritdoc />
public ConnectionVisibility Visibility { get; private set; }

internal RestConnection(string id, string type, string name, bool isRevoked, IReadOnlyCollection<ulong> integrationIds)
{
Id = id;
Type = type;
Name = name;
IsRevoked = isRevoked;
internal BaseDiscordClient Discord { get; }

IntegrationIds = integrationIds;
internal RestConnection(BaseDiscordClient discord) {
Discord = discord;
}
internal static RestConnection Create(Model model)

internal static RestConnection Create(BaseDiscordClient discord, Model model)
{
var entity = new RestConnection(discord);
entity.Update(model);
return entity;
}

internal void Update(Model model)
{
return new RestConnection(model.Id, model.Type, model.Name, model.Revoked, model.Integrations.ToImmutableArray());
Id = model.Id;
Name = model.Name;
Type = model.Type;
IsRevoked = model.Revoked.IsSpecified ? model.Revoked.Value : null;
Integrations = model.Integrations.IsSpecified ?model.Integrations.Value
.Select(intergration => RestIntegration.Create(Discord, null, intergration)).ToImmutableArray() : null;
Verified = model.Verified;
FriendSync = model.FriendSync;
ShowActivity = model.ShowActivity;
Visibility = model.Visibility;
}

/// <summary>
@@ -40,6 +63,6 @@ namespace Discord.Rest
/// Name of the connection.
/// </returns>
public override string ToString() => Name;
private string DebuggerDisplay => $"{Name} ({Id}, {Type}{(IsRevoked ? ", Revoked" : "")})";
private string DebuggerDisplay => $"{Name} ({Id}, {Type}{(IsRevoked.GetValueOrDefault() ? ", Revoked" : "")})";
}
}

+ 14
- 0
src/Discord.Net.WebSocket/API/Gateway/IntegrationDeletedEvent.cs View File

@@ -0,0 +1,14 @@
using Newtonsoft.Json;

namespace Discord.API.Gateway
{
internal class IntegrationDeletedEvent
{
[JsonProperty("id")]
public ulong Id { get; set; }
[JsonProperty("guild_id")]
public ulong GuildId { get; set; }
[JsonProperty("application_id")]
public Optional<ulong> ApplicationID { get; set; }
}
}

+ 26
- 0
src/Discord.Net.WebSocket/BaseSocketClient.Events.cs View File

@@ -415,6 +415,32 @@ namespace Discord.WebSocket

#endregion

#region Integrations
/// <summary> Fired when an integration is created. </summary>
public event Func<IIntegration, Task> IntegrationCreated
{
add { _integrationCreated.Add(value); }
remove { _integrationCreated.Remove(value); }
}
internal readonly AsyncEvent<Func<IIntegration, Task>> _integrationCreated = new AsyncEvent<Func<IIntegration, Task>>();

/// <summary> Fired when an integration is updated. </summary>
public event Func<IIntegration, Task> IntegrationUpdated
{
add { _integrationUpdated.Add(value); }
remove { _integrationUpdated.Remove(value); }
}
internal readonly AsyncEvent<Func<IIntegration, Task>> _integrationUpdated = new AsyncEvent<Func<IIntegration, Task>>();

/// <summary> Fired when an integration is deleted. </summary>
public event Func<IGuild, ulong, Optional<ulong>, Task> IntegrationDeleted
{
add { _integrationDeleted.Add(value); }
remove { _integrationDeleted.Remove(value); }
}
internal readonly AsyncEvent<Func<IGuild, ulong, Optional<ulong>, Task>> _integrationDeleted = new AsyncEvent<Func<IGuild, ulong, Optional<ulong>, Task>>();
#endregion

#region Users
/// <summary> Fired when a user joins a guild. </summary>
public event Func<SocketGuildUser, Task> UserJoined


+ 86
- 0
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -2017,6 +2017,92 @@ namespace Discord.WebSocket
break;
#endregion

#region Integrations
case "INTEGRATION_CREATE":
{
await _gatewayLogger.DebugAsync("Received Dispatch (INTEGRATION_CREATE)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<Integration>(_serializer);

// Integrations from Gateway should always have guild IDs specified.
if (!data.GuildId.IsSpecified)
return;

var guild = State.GetGuild(data.GuildId.Value);

if (guild != null)
{
if (!guild.IsSynced)
{
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}

await TimedInvokeAsync(_integrationCreated, nameof(IntegrationCreated), RestIntegration.Create(this, guild, data)).ConfigureAwait(false);
}
else
{
await UnknownGuildAsync(type, data.GuildId.Value).ConfigureAwait(false);
return;
}
}
break;
case "INTEGRATION_UPDATE":
{
await _gatewayLogger.DebugAsync("Received Dispatch (INTEGRATION_UPDATE)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<Integration>(_serializer);

// Integrations from Gateway should always have guild IDs specified.
if (!data.GuildId.IsSpecified)
return;

var guild = State.GetGuild(data.GuildId.Value);

if (guild != null)
{
if (!guild.IsSynced)
{
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}

await TimedInvokeAsync(_integrationUpdated, nameof(IntegrationUpdated), RestIntegration.Create(this, guild, data)).ConfigureAwait(false);
}
else
{
await UnknownGuildAsync(type, data.GuildId.Value).ConfigureAwait(false);
return;
}
}
break;
case "INTEGRATION_DELETE":
{
await _gatewayLogger.DebugAsync("Received Dispatch (INTEGRATION_DELETE)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<IntegrationDeletedEvent>(_serializer);

var guild = State.GetGuild(data.GuildId);

if (guild != null)
{
if (!guild.IsSynced)
{
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}

await TimedInvokeAsync(_integrationDeleted, nameof(IntegrationDeleted), guild, data.Id, data.ApplicationID).ConfigureAwait(false);
}
else
{
await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false);
return;
}
}
break;
#endregion

#region Users
case "USER_UPDATE":
{


+ 6
- 6
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -847,10 +847,10 @@ namespace Discord.WebSocket
#endregion

#region Integrations
public Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(RequestOptions options = null)
public Task<IReadOnlyCollection<RestIntegration>> GetIntegrationsAsync(RequestOptions options = null)
=> GuildHelper.GetIntegrationsAsync(this, Discord, options);
public Task<RestGuildIntegration> CreateIntegrationAsync(ulong id, string type, RequestOptions options = null)
=> GuildHelper.CreateIntegrationAsync(this, Discord, id, type, options);
public Task DeleteIntegrationAsync(ulong id, RequestOptions options = null)
=> GuildHelper.DeleteIntegrationAsync(this, Discord, id, options);
#endregion

#region Interactions
@@ -1888,11 +1888,11 @@ namespace Discord.WebSocket
=> await GetVoiceRegionsAsync(options).ConfigureAwait(false);

/// <inheritdoc />
async Task<IReadOnlyCollection<IGuildIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options)
async Task<IReadOnlyCollection<IIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options)
=> await GetIntegrationsAsync(options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IGuildIntegration> IGuild.CreateIntegrationAsync(ulong id, string type, RequestOptions options)
=> await CreateIntegrationAsync(id, type, options).ConfigureAwait(false);
async Task IGuild.DeleteIntegrationAsync(ulong id, RequestOptions options)
=> await DeleteIntegrationAsync(id, options).ConfigureAwait(false);

/// <inheritdoc />
async Task<IReadOnlyCollection<IInviteMetadata>> IGuild.GetInvitesAsync(RequestOptions options)


Loading…
Cancel
Save