* 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 IsSpecifiedpull/2209/head
@@ -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; } | |||
} | |||
} |
@@ -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. | |||
@@ -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})"; | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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; } | |||
} | |||
@@ -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; } | |||
} | |||
} |
@@ -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, | |||
@@ -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 | |||
@@ -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 | |||
@@ -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) | |||
@@ -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; | |||
} | |||
} |
@@ -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."); | |||
} | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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" : "")})"; | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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 | |||
@@ -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": | |||
{ | |||
@@ -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) | |||