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
This commit is contained in:
FeroxFoxxo
2022-03-27 01:52:31 +13:00
committed by GitHub
parent d656722bd9
commit 305d7f9e13
26 changed files with 598 additions and 261 deletions

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; }
}
}

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; }
}
}

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; }
}

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; }
}
}

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,

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)
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>("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)
{
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

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

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)

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;
}
}

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.");
}
}
}
}

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;
}
}
}

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);
}
}
}

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)
{
return new RestConnection(model.Id, model.Type, model.Name, model.Revoked, model.Integrations.ToImmutableArray());
var entity = new RestConnection(discord);
entity.Update(model);
return entity;
}
internal void Update(Model model)
{
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" : "")})";
}
}