Browse Source

Add more methods for application commands, clean up of code, and add proper caching of socket application commands.

pull/1923/head
quin lynch 3 years ago
parent
commit
6326a1efd4
16 changed files with 333 additions and 147 deletions
  1. +34
    -1
      src/Discord.Net.Core/Discord.Net.Core.xml
  2. +14
    -1
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  3. +21
    -0
      src/Discord.Net.Core/IDiscordClient.cs
  4. +8
    -0
      src/Discord.Net.Rest/BaseDiscordClient.cs
  5. +18
    -2
      src/Discord.Net.Rest/ClientHelper.cs
  6. +23
    -0
      src/Discord.Net.Rest/Discord.Net.Rest.xml
  7. +17
    -54
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  8. +7
    -0
      src/Discord.Net.Rest/DiscordRestClient.cs
  9. +21
    -1
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  10. +15
    -7
      src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
  11. +19
    -0
      src/Discord.Net.WebSocket/ClientState.cs
  12. +27
    -0
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
  13. +61
    -2
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  14. +36
    -27
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  15. +0
    -48
      src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommandCache.cs
  16. +12
    -4
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs

+ 34
- 1
src/Discord.Net.Core/Discord.Net.Core.xml View File

@@ -3916,7 +3916,7 @@
</member> </member>
<member name="M:Discord.IGuild.GetApplicationCommandsAsync(Discord.RequestOptions)"> <member name="M:Discord.IGuild.GetApplicationCommandsAsync(Discord.RequestOptions)">
<summary> <summary>
Gets this guilds slash commands commands
Gets this guilds application commands.
</summary> </summary>
<param name="options">The options to be used when sending the request.</param> <param name="options">The options to be used when sending the request.</param>
<returns> <returns>
@@ -3924,6 +3924,18 @@
of application commands found within the guild. of application commands found within the guild.
</returns> </returns>
</member> </member>
<member name="M:Discord.IGuild.GetApplicationCommandAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)">
<summary>
Gets an application command within this guild with the specified id.
</summary>
<param name="id">The id of the application command to get.</param>
<param name="mode">The <see cref="T:Discord.CacheMode" /> that determines whether the object should be fetched from cache.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A ValueTask that represents the asynchronous get operation. The task result contains a <see cref="T:Discord.IApplicationCommand"/>
if found, otherwise <see langword="null"/>.
</returns>
</member>
<member name="T:Discord.IGuildIntegration"> <member name="T:Discord.IGuildIntegration">
<summary> <summary>
Holds information for a guild integration feature. Holds information for a guild integration feature.
@@ -10971,6 +10983,27 @@
A task that represents the asynchronous get operation. The task result contains a read-only collection of connections. A task that represents the asynchronous get operation. The task result contains a read-only collection of connections.
</returns> </returns>
</member> </member>
<member name="M:Discord.IDiscordClient.GetGlobalApplicationCommandAsync(System.UInt64,Discord.RequestOptions)">
<summary>
Gets a global application command.
</summary>
<param name="id">The id of the command.</param>
<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 the application command if found, otherwise
<see langword="null"/>.
</returns>
</member>
<member name="M:Discord.IDiscordClient.GetGlobalApplicationCommandsAsync(Discord.RequestOptions)">
<summary>
Gets a collection of all global commands.
</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 global
application commands.
</returns>
</member>
<member name="M:Discord.IDiscordClient.GetGuildAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> <member name="M:Discord.IDiscordClient.GetGuildAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)">
<summary> <summary>
Gets a guild. Gets a guild.


+ 14
- 1
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -943,7 +943,7 @@ namespace Discord
Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null); Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null);


/// <summary> /// <summary>
/// Gets this guilds slash commands commands
/// Gets this guilds application commands.
/// </summary> /// </summary>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>
@@ -951,5 +951,18 @@ namespace Discord
/// of application commands found within the guild. /// of application commands found within the guild.
/// </returns> /// </returns>
Task<IReadOnlyCollection<IApplicationCommand>> GetApplicationCommandsAsync (RequestOptions options = null); Task<IReadOnlyCollection<IApplicationCommand>> GetApplicationCommandsAsync (RequestOptions options = null);

/// <summary>
/// Gets an application command within this guild with the specified id.
/// </summary>
/// <param name="id">The id of the application command to get.</param>
/// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A ValueTask that represents the asynchronous get operation. The task result contains a <see cref="IApplicationCommand"/>
/// if found, otherwise <see langword="null"/>.
/// </returns>
Task<IApplicationCommand> GetApplicationCommandAsync(ulong id, CacheMode mode = CacheMode.AllowDownload,
RequestOptions options = null);
} }
} }

+ 21
- 0
src/Discord.Net.Core/IDiscordClient.cs View File

@@ -141,6 +141,27 @@ namespace Discord
/// </returns> /// </returns>
Task<IReadOnlyCollection<IConnection>> GetConnectionsAsync(RequestOptions options = null); Task<IReadOnlyCollection<IConnection>> GetConnectionsAsync(RequestOptions options = null);


/// <summary>
/// Gets a global application command.
/// </summary>
/// <param name="id">The id of the command.</param>
/// <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 the application command if found, otherwise
/// <see langword="null"/>.
/// </returns>
Task<IApplicationCommand> GetGlobalApplicationCommandAsync(ulong id, RequestOptions options = null);

/// <summary>
/// Gets a collection of all global commands.
/// </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 global
/// application commands.
/// </returns>
Task<IReadOnlyCollection<IApplicationCommand>> GetGlobalApplicationCommandsAsync(RequestOptions options = null);

/// <summary> /// <summary>
/// Gets a guild. /// Gets a guild.
/// </summary> /// </summary>


+ 8
- 0
src/Discord.Net.Rest/BaseDiscordClient.cs View File

@@ -216,6 +216,14 @@ namespace Discord.Rest
Task<IWebhook> IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options) Task<IWebhook> IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options)
=> Task.FromResult<IWebhook>(null); => Task.FromResult<IWebhook>(null);


/// <inheritdoc />
Task<IApplicationCommand> IDiscordClient.GetGlobalApplicationCommandAsync(ulong id, RequestOptions options)
=> Task.FromResult<IApplicationCommand>(null);

/// <inheritdoc />
Task<IReadOnlyCollection<IApplicationCommand>> IDiscordClient.GetGlobalApplicationCommandsAsync(RequestOptions options)
=> Task.FromResult<IReadOnlyCollection<IApplicationCommand>>(ImmutableArray.Create<IApplicationCommand>());

/// <inheritdoc /> /// <inheritdoc />
Task IDiscordClient.StartAsync() Task IDiscordClient.StartAsync()
=> Task.Delay(0); => Task.Delay(0);


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

@@ -194,7 +194,8 @@ namespace Discord.Rest
}; };
} }


public static async Task<IReadOnlyCollection<RestGlobalCommand>> GetGlobalApplicationCommands(BaseDiscordClient client, RequestOptions options)
public static async Task<IReadOnlyCollection<RestGlobalCommand>> GetGlobalApplicationCommands(BaseDiscordClient client,
RequestOptions options = null)
{ {
var response = await client.ApiClient.GetGlobalApplicationCommandsAsync(options).ConfigureAwait(false); var response = await client.ApiClient.GetGlobalApplicationCommandsAsync(options).ConfigureAwait(false);


@@ -203,8 +204,16 @@ namespace Discord.Rest


return response.Select(x => RestGlobalCommand.Create(client, x)).ToArray(); return response.Select(x => RestGlobalCommand.Create(client, x)).ToArray();
} }
public static async Task<RestGlobalCommand> GetGlobalApplicationCommand(BaseDiscordClient client, ulong id,
RequestOptions options = null)
{
var model = await client.ApiClient.GetGlobalApplicationCommandAsync(id, options);

return model != null ? RestGlobalCommand.Create(client, model) : null;
}


public static async Task<IReadOnlyCollection<RestGuildCommand>> GetGuildApplicationCommands(BaseDiscordClient client, ulong guildId, RequestOptions options)
public static async Task<IReadOnlyCollection<RestGuildCommand>> GetGuildApplicationCommands(BaseDiscordClient client, ulong guildId,
RequestOptions options = null)
{ {
var response = await client.ApiClient.GetGuildApplicationCommandsAsync(guildId, options).ConfigureAwait(false); var response = await client.ApiClient.GetGuildApplicationCommandsAsync(guildId, options).ConfigureAwait(false);


@@ -213,6 +222,13 @@ namespace Discord.Rest


return response.Select(x => RestGuildCommand.Create(client, x, guildId)).ToImmutableArray(); return response.Select(x => RestGuildCommand.Create(client, x, guildId)).ToImmutableArray();
} }
public static async Task<RestGuildCommand> GetGuildApplicationCommand(BaseDiscordClient client, ulong id, ulong guildId,
RequestOptions options = null)
{
var model = await client.ApiClient.GetGuildApplicationCommandAsync(guildId, id, options);

return model != null ? RestGuildCommand.Create(client, model, guildId) : null;
}




public static Task AddRoleAsync(BaseDiscordClient client, ulong guildId, ulong userId, ulong roleId, RequestOptions options = null) public static Task AddRoleAsync(BaseDiscordClient client, ulong guildId, ulong userId, ulong roleId, RequestOptions options = null)


+ 23
- 0
src/Discord.Net.Rest/Discord.Net.Rest.xml View File

@@ -226,6 +226,12 @@
<member name="M:Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetWebhookAsync(System.UInt64,Discord.RequestOptions)"> <member name="M:Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetWebhookAsync(System.UInt64,Discord.RequestOptions)">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="M:Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetGlobalApplicationCommandAsync(System.UInt64,Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetGlobalApplicationCommandsAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#StartAsync"> <member name="M:Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#StartAsync">
<inheritdoc /> <inheritdoc />
</member> </member>
@@ -299,6 +305,12 @@
<member name="M:Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetWebhookAsync(System.UInt64,Discord.RequestOptions)"> <member name="M:Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetWebhookAsync(System.UInt64,Discord.RequestOptions)">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="M:Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetGlobalApplicationCommandsAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetGlobalApplicationCommandAsync(System.UInt64,Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="T:Discord.Rest.DiscordRestConfig"> <member name="T:Discord.Rest.DiscordRestConfig">
<summary> <summary>
Represents a configuration class for <see cref="T:Discord.Rest.DiscordRestClient"/>. Represents a configuration class for <see cref="T:Discord.Rest.DiscordRestClient"/>.
@@ -3503,6 +3515,17 @@
of application commands found within the guild. of application commands found within the guild.
</returns> </returns>
</member> </member>
<member name="M:Discord.Rest.RestGuild.GetApplicationCommandAsync(System.UInt64,Discord.RequestOptions)">
<summary>
Gets an application command within this guild with the specified id.
</summary>
<param name="id">The id of the application command to get.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A ValueTask that represents the asynchronous get operation. The task result contains a <see cref="T:Discord.IApplicationCommand"/>
if found, otherwise <see langword="null"/>.
</returns>
</member>
<member name="M:Discord.Rest.RestGuild.ToString"> <member name="M:Discord.Rest.RestGuild.ToString">
<summary> <summary>
Returns the name of the guild. Returns the name of the guild.


+ 17
- 54
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -1063,6 +1063,18 @@ namespace Discord.API
return await SendAsync<ApplicationCommand[]>("GET", () => $"applications/{this.CurrentUserId}/commands", new BucketIds(), options: options).ConfigureAwait(false); return await SendAsync<ApplicationCommand[]>("GET", () => $"applications/{this.CurrentUserId}/commands", new BucketIds(), options: options).ConfigureAwait(false);
} }


public async Task<ApplicationCommand> GetGlobalApplicationCommandAsync(ulong id, RequestOptions options = null)
{
Preconditions.NotEqual(id, 0, nameof(id));

options = RequestOptions.CreateOrClone(options);

try
{
return await SendAsync<ApplicationCommand>("GET", () => $"applications/{this.CurrentUserId}/commands/{id}", new BucketIds(), options: options).ConfigureAwait(false);
}
catch(HttpException x) when (x.HttpCode == HttpStatusCode.NotFound) { return null; }
}


public async Task<ApplicationCommand> CreateGlobalApplicationCommandAsync(CreateApplicationCommandParams command, RequestOptions options = null) public async Task<ApplicationCommand> CreateGlobalApplicationCommandAsync(CreateApplicationCommandParams command, RequestOptions options = null)
{ {
@@ -1074,7 +1086,6 @@ namespace Discord.API


options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);



return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/commands", command, new BucketIds(), options: options)).ConfigureAwait(false); return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/commands", command, new BucketIds(), options: options)).ConfigureAwait(false);
} }
public async Task<ApplicationCommand> ModifyGlobalApplicationCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null) public async Task<ApplicationCommand> ModifyGlobalApplicationCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null)
@@ -1158,7 +1169,11 @@ namespace Discord.API


var bucket = new BucketIds(guildId: guildId); var bucket = new BucketIds(guildId: guildId);


return await SendAsync<ApplicationCommand>("GET", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", bucket, options: options);
try
{
return await SendAsync<ApplicationCommand>("GET", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", bucket, options: options);
}
catch(HttpException x) when (x.HttpCode == HttpStatusCode.NotFound) { return null; }
} }


public async Task<ApplicationCommand> CreateGuildApplicationCommandAsync(CreateApplicationCommandParams command, ulong guildId, RequestOptions options = null) public async Task<ApplicationCommand> CreateGuildApplicationCommandAsync(CreateApplicationCommandParams command, ulong guildId, RequestOptions options = null)
@@ -1195,58 +1210,6 @@ namespace Discord.API
return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", commands, bucket, options: options)).ConfigureAwait(false); return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", commands, bucket, options: options)).ConfigureAwait(false);
} }


public async Task<ApplicationCommand> CreateGuildApplicationUserCommandAsync(CreateApplicationCommandParams command, ulong guildId, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);

var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options)).ConfigureAwait(false);
}

public async Task<ApplicationCommand> ModifyGuildApplicationUserCommandAsync(ModifyApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);

var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options)).ConfigureAwait(false);
}
public async Task<ApplicationCommand[]> BulkOverwriteGuildApplicationUserCommands(ulong guildId, CreateApplicationCommandParams[] commands, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);

var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", commands, bucket, options: options)).ConfigureAwait(false);
}

public async Task<ApplicationCommand> CreateGuildApplicationMessageCommandAsync(CreateApplicationCommandParams command, ulong guildId, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);

var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options)).ConfigureAwait(false);
}
public async Task<ApplicationCommand> ModifyGuildApplicationMessageCommandAsync(ModifyApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);

var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options)).ConfigureAwait(false);
}

public async Task<ApplicationCommand[]> BulkOverwriteGuildApplicationMessageCommands(ulong guildId, CreateApplicationCommandParams[] commands, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);

var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", commands, bucket, options: options)).ConfigureAwait(false);
}

//Interaction Responses //Interaction Responses
public async Task CreateInteractionResponse(InteractionResponse response, ulong interactionId, string interactionToken, RequestOptions options = null) public async Task CreateInteractionResponse(InteractionResponse response, ulong interactionId, string interactionToken, RequestOptions options = null)
{ {


+ 7
- 0
src/Discord.Net.Rest/DiscordRestClient.cs View File

@@ -226,5 +226,12 @@ namespace Discord.Rest
/// <inheritdoc /> /// <inheritdoc />
async Task<IWebhook> IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options) async Task<IWebhook> IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options)
=> await GetWebhookAsync(id, options).ConfigureAwait(false); => await GetWebhookAsync(id, options).ConfigureAwait(false);

/// <inheritdoc />
async Task<IReadOnlyCollection<IApplicationCommand>> IDiscordClient.GetGlobalApplicationCommandsAsync(RequestOptions options)
=> await GetGlobalApplicationCommands(options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IApplicationCommand> IDiscordClient.GetGlobalApplicationCommandAsync(ulong id, RequestOptions options)
=> await ClientHelper.GetGlobalApplicationCommand(this, id, options).ConfigureAwait(false);
} }
} }

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

@@ -878,8 +878,19 @@ namespace Discord.Rest
/// A task that represents the asynchronous get operation. The task result contains a read-only collection /// A task that represents the asynchronous get operation. The task result contains a read-only collection
/// of application commands found within the guild. /// of application commands found within the guild.
/// </returns> /// </returns>
public async Task<IReadOnlyCollection<RestApplicationCommand>> GetApplicationCommandsAsync (RequestOptions options = null)
public async Task<IReadOnlyCollection<RestGuildCommand>> GetApplicationCommandsAsync (RequestOptions options = null)
=> await ClientHelper.GetGuildApplicationCommands(Discord, Id, options).ConfigureAwait(false); => await ClientHelper.GetGuildApplicationCommands(Discord, Id, options).ConfigureAwait(false);
/// <summary>
/// Gets an application command within this guild with the specified id.
/// </summary>
/// <param name="id">The id of the application command to get.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A ValueTask that represents the asynchronous get operation. The task result contains a <see cref="IApplicationCommand"/>
/// if found, otherwise <see langword="null"/>.
/// </returns>
public async Task<RestGuildCommand> GetApplicationCommandAsync(ulong id, RequestOptions options = null)
=> await ClientHelper.GetGuildApplicationCommand(Discord, id, this.Id, options);


/// <summary> /// <summary>
/// Returns the name of the guild. /// Returns the name of the guild.
@@ -1169,5 +1180,14 @@ namespace Discord.Rest
/// <inheritdoc /> /// <inheritdoc />
async Task<IReadOnlyCollection<IApplicationCommand>> IGuild.GetApplicationCommandsAsync (RequestOptions options) async Task<IReadOnlyCollection<IApplicationCommand>> IGuild.GetApplicationCommandsAsync (RequestOptions options)
=> await GetApplicationCommandsAsync(options).ConfigureAwait(false); => await GetApplicationCommandsAsync(options).ConfigureAwait(false);
async Task<IApplicationCommand> IGuild.GetApplicationCommandAsync(ulong id, CacheMode mode, RequestOptions options)
{
if (mode == CacheMode.AllowDownload)
{
return await GetApplicationCommandAsync(id, options);
}
else
return null;
}
} }
} }

+ 15
- 7
src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs View File

@@ -44,12 +44,20 @@ namespace Discord.Rest
} }


// Global commands // Global commands
public static async Task<RestGlobalCommand> CreateGlobalCommand<TArg>(BaseDiscordClient client,
Action<TArg> func, RequestOptions options) where TArg : ApplicationCommandProperties
public static async Task<RestGlobalCommand> GetGlobalCommandAsync(BaseDiscordClient client, ulong id,
RequestOptions options = null)
{
var model = await client.ApiClient.GetGlobalApplicationCommandAsync(id, options).ConfigureAwait(false);

return RestGlobalCommand.Create(client, model);
}
public static Task<RestGlobalCommand> CreateGlobalCommand<TArg>(BaseDiscordClient client,
Action<TArg> func, RequestOptions options = null) where TArg : ApplicationCommandProperties
{ {
var args = Activator.CreateInstance(typeof(TArg)); var args = Activator.CreateInstance(typeof(TArg));
func((TArg)args); func((TArg)args);
return await CreateGlobalCommand(client, (TArg)args, options);
return CreateGlobalCommand(client, (TArg)args, options);
} }
public static async Task<RestGlobalCommand> CreateGlobalCommand(BaseDiscordClient client, public static async Task<RestGlobalCommand> CreateGlobalCommand(BaseDiscordClient client,
ApplicationCommandProperties arg, RequestOptions options = null) ApplicationCommandProperties arg, RequestOptions options = null)
@@ -116,7 +124,7 @@ namespace Discord.Rest
models.Add(model); models.Add(model);
} }


var apiModels = await client.ApiClient.BulkOverwriteGlobalApplicationCommands(models.ToArray(), options);
var apiModels = await client.ApiClient.BulkOverwriteGlobalApplicationCommands(models.ToArray(), options).ConfigureAwait(false);


return apiModels.Select(x => RestGlobalCommand.Create(client, x)).ToArray(); return apiModels.Select(x => RestGlobalCommand.Create(client, x)).ToArray();
} }
@@ -156,7 +164,7 @@ namespace Discord.Rest
models.Add(model); models.Add(model);
} }


var apiModels = await client.ApiClient.BulkOverwriteGuildApplicationCommands(guildId, models.ToArray(), options);
var apiModels = await client.ApiClient.BulkOverwriteGuildApplicationCommands(guildId, models.ToArray(), options).ConfigureAwait(false);


return apiModels.Select(x => RestGuildCommand.Create(client, x, guildId)).ToArray(); return apiModels.Select(x => RestGuildCommand.Create(client, x, guildId)).ToArray();
} }
@@ -221,12 +229,12 @@ namespace Discord.Rest
} }


// Guild Commands // Guild Commands
public static async Task<RestGuildCommand> CreateGuildCommand<TArg>(BaseDiscordClient client, ulong guildId,
public static Task<RestGuildCommand> CreateGuildCommand<TArg>(BaseDiscordClient client, ulong guildId,
Action<TArg> func, RequestOptions options) where TArg : ApplicationCommandProperties Action<TArg> func, RequestOptions options) where TArg : ApplicationCommandProperties
{ {
var args = Activator.CreateInstance(typeof(TArg)); var args = Activator.CreateInstance(typeof(TArg));
func((TArg)args); func((TArg)args);
return await CreateGuildCommand(client, guildId, (TArg)args, options);
return CreateGuildCommand(client, guildId, (TArg)args, options);
} }


public static async Task<RestGuildCommand> CreateGuildCommand(BaseDiscordClient client, ulong guildId, public static async Task<RestGuildCommand> CreateGuildCommand(BaseDiscordClient client, ulong guildId,


+ 19
- 0
src/Discord.Net.WebSocket/ClientState.cs View File

@@ -16,12 +16,14 @@ namespace Discord.WebSocket
private readonly ConcurrentDictionary<ulong, SocketGuild> _guilds; private readonly ConcurrentDictionary<ulong, SocketGuild> _guilds;
private readonly ConcurrentDictionary<ulong, SocketGlobalUser> _users; private readonly ConcurrentDictionary<ulong, SocketGlobalUser> _users;
private readonly ConcurrentHashSet<ulong> _groupChannels; private readonly ConcurrentHashSet<ulong> _groupChannels;
private readonly ConcurrentDictionary<ulong, SocketApplicationCommand> _commands;


internal IReadOnlyCollection<SocketChannel> Channels => _channels.ToReadOnlyCollection(); internal IReadOnlyCollection<SocketChannel> Channels => _channels.ToReadOnlyCollection();
internal IReadOnlyCollection<SocketDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection(); internal IReadOnlyCollection<SocketDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection();
internal IReadOnlyCollection<SocketGroupChannel> GroupChannels => _groupChannels.Select(x => GetChannel(x) as SocketGroupChannel).ToReadOnlyCollection(_groupChannels); internal IReadOnlyCollection<SocketGroupChannel> GroupChannels => _groupChannels.Select(x => GetChannel(x) as SocketGroupChannel).ToReadOnlyCollection(_groupChannels);
internal IReadOnlyCollection<SocketGuild> Guilds => _guilds.ToReadOnlyCollection(); internal IReadOnlyCollection<SocketGuild> Guilds => _guilds.ToReadOnlyCollection();
internal IReadOnlyCollection<SocketGlobalUser> Users => _users.ToReadOnlyCollection(); internal IReadOnlyCollection<SocketGlobalUser> Users => _users.ToReadOnlyCollection();
internal IReadOnlyCollection<SocketApplicationCommand> Commands => _commands.ToReadOnlyCollection();


internal IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => internal IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels =>
_dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat( _dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat(
@@ -139,5 +141,22 @@ namespace Discord.WebSocket
foreach (var guild in _guilds.Values) foreach (var guild in _guilds.Values)
guild.PurgeGuildUserCache(); guild.PurgeGuildUserCache();
} }

internal SocketApplicationCommand GetCommand(ulong id)
{
if (_commands.TryGetValue(id, out SocketApplicationCommand command))
return command;
return null;
}
internal void AddCommand(SocketApplicationCommand command)
{
_commands[command.Id] = command;
}
internal SocketApplicationCommand RemoveCommand(ulong id)
{
if (_commands.TryRemove(id, out SocketApplicationCommand command))
return command;
return null;
}
} }
} }

+ 27
- 0
src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml View File

@@ -1078,6 +1078,27 @@
<member name="M:Discord.WebSocket.DiscordSocketClient.GetUser(System.String,System.String)"> <member name="M:Discord.WebSocket.DiscordSocketClient.GetUser(System.String,System.String)">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="M:Discord.WebSocket.DiscordSocketClient.GetGlobalApplicationCommandAsync(System.UInt64,Discord.RequestOptions)">
<summary>
Gets a global application command.
</summary>
<param name="id">The id of the command.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A ValueTask that represents the asynchronous get operation. The task result contains the application command if found, otherwise
<see langword="null"/>.
</returns>
</member>
<member name="M:Discord.WebSocket.DiscordSocketClient.GetGlobalApplicationCommandsAsync(Discord.RequestOptions)">
<summary>
Gets a collection of all global commands.
</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 global
application commands.
</returns>
</member>
<member name="M:Discord.WebSocket.DiscordSocketClient.PurgeUserCache"> <member name="M:Discord.WebSocket.DiscordSocketClient.PurgeUserCache">
<summary> <summary>
Clears cached users from the client. Clears cached users from the client.
@@ -1166,6 +1187,12 @@
<member name="M:Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String,Discord.RequestOptions)"> <member name="M:Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String,Discord.RequestOptions)">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="M:Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetGlobalApplicationCommandAsync(System.UInt64,Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetGlobalApplicationCommandsAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#StartAsync"> <member name="M:Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#StartAsync">
<inheritdoc /> <inheritdoc />
</member> </member>


+ 61
- 2
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -343,6 +343,54 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public override SocketUser GetUser(string username, string discriminator) public override SocketUser GetUser(string username, string discriminator)
=> State.Users.FirstOrDefault(x => x.Discriminator == discriminator && x.Username == username); => State.Users.FirstOrDefault(x => x.Discriminator == discriminator && x.Username == username);

/// <summary>
/// Gets a global application command.
/// </summary>
/// <param name="id">The id of the command.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A ValueTask that represents the asynchronous get operation. The task result contains the application command if found, otherwise
/// <see langword="null"/>.
/// </returns>
public async ValueTask<SocketApplicationCommand> GetGlobalApplicationCommandAsync(ulong id, RequestOptions options = null)
{
var command = State.GetCommand(id);

if (command != null)
return command;

var model = await ApiClient.GetGlobalApplicationCommandAsync(id, options);

if (model == null)
return null;

command = SocketApplicationCommand.Create(this, model);

State.AddCommand(command);

return command;
}
/// <summary>
/// Gets a collection of all global commands.
/// </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 global
/// application commands.
/// </returns>
public async Task<IReadOnlyCollection<SocketApplicationCommand>> GetGlobalApplicationCommandsAsync(RequestOptions options = null)
{
var commands = (await ApiClient.GetGlobalApplicationCommandsAsync(options)).Select(x => SocketApplicationCommand.Create(this, x));

foreach(var command in commands)
{
State.AddCommand(command);
}

return commands.ToImmutableArray();
}

/// <summary> /// <summary>
/// Clears cached users from the client. /// Clears cached users from the client.
/// </summary> /// </summary>
@@ -1891,8 +1939,6 @@ namespace Discord.WebSocket
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (INTERACTION_CREATE)").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Received Dispatch (INTERACTION_CREATE)").ConfigureAwait(false);


// 0x546861742062696720656e6469616e20656e636f64696e67206d616b6573206d79316d687a20636c6f636b207469636b

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


SocketChannel channel = null; SocketChannel channel = null;
@@ -1958,6 +2004,8 @@ namespace Discord.WebSocket


var applicationCommand = SocketApplicationCommand.Create(this, data); var applicationCommand = SocketApplicationCommand.Create(this, data);


State.AddCommand(applicationCommand);

await TimedInvokeAsync(_applicationCommandCreated, nameof(ApplicationCommandCreated), applicationCommand).ConfigureAwait(false); await TimedInvokeAsync(_applicationCommandCreated, nameof(ApplicationCommandCreated), applicationCommand).ConfigureAwait(false);
} }
break; break;
@@ -1979,6 +2027,8 @@ namespace Discord.WebSocket


var applicationCommand = SocketApplicationCommand.Create(this, data); var applicationCommand = SocketApplicationCommand.Create(this, data);


State.AddCommand(applicationCommand);

await TimedInvokeAsync(_applicationCommandUpdated, nameof(ApplicationCommandUpdated), applicationCommand).ConfigureAwait(false); await TimedInvokeAsync(_applicationCommandUpdated, nameof(ApplicationCommandUpdated), applicationCommand).ConfigureAwait(false);
} }
break; break;
@@ -2000,6 +2050,8 @@ namespace Discord.WebSocket


var applicationCommand = SocketApplicationCommand.Create(this, data); var applicationCommand = SocketApplicationCommand.Create(this, data);


State.RemoveCommand(applicationCommand.Id);

await TimedInvokeAsync(_applicationCommandDeleted, nameof(ApplicationCommandDeleted), applicationCommand).ConfigureAwait(false); await TimedInvokeAsync(_applicationCommandDeleted, nameof(ApplicationCommandDeleted), applicationCommand).ConfigureAwait(false);
} }
break; break;
@@ -2611,6 +2663,13 @@ namespace Discord.WebSocket
async Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) async Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
=> await GetVoiceRegionAsync(id, options).ConfigureAwait(false); => await GetVoiceRegionAsync(id, options).ConfigureAwait(false);


/// <inheritdoc />
async Task<IApplicationCommand> IDiscordClient.GetGlobalApplicationCommandAsync(ulong id, RequestOptions options)
=> await GetGlobalApplicationCommandAsync(id, options);
/// <inheritdoc />
async Task<IReadOnlyCollection<IApplicationCommand>> IDiscordClient.GetGlobalApplicationCommandsAsync(RequestOptions options)
=> await GetGlobalApplicationCommandsAsync(options);

/// <inheritdoc /> /// <inheritdoc />
async Task IDiscordClient.StartAsync() async Task IDiscordClient.StartAsync()
=> await StartAsync().ConfigureAwait(false); => await StartAsync().ConfigureAwait(false);


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

@@ -785,13 +785,13 @@ namespace Discord.WebSocket


//Interactions //Interactions
/// <summary> /// <summary>
/// Deletes all slash commands in the current guild.
/// Deletes all application commands in the current guild.
/// </summary> /// </summary>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>
/// A task that represents the asynchronous delete operation. /// A task that represents the asynchronous delete operation.
/// </returns> /// </returns>
public Task DeleteSlashCommandsAsync(RequestOptions options = null)
public Task DeleteApplicationCommandsAsync(RequestOptions options = null)
=> InteractionHelper.DeleteAllGuildCommandsAsync(Discord, this.Id, options); => InteractionHelper.DeleteAllGuildCommandsAsync(Discord, this.Id, options);


/// <summary> /// <summary>
@@ -802,20 +802,39 @@ namespace Discord.WebSocket
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of /// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// slash commands created by the current user. /// slash commands created by the current user.
/// </returns> /// </returns>
public Task<IReadOnlyCollection<RestGuildCommand>> GetSlashCommandsAsync(RequestOptions options = null)
=> GuildHelper.GetSlashCommandsAsync(this, Discord, options);
public async Task<IReadOnlyCollection<SocketApplicationCommand>> GetApplicationCommandsAsync(RequestOptions options = null)
{
var commands = (await Discord.ApiClient.GetGuildApplicationCommandsAsync(this.Id, options)).Select(x => SocketApplicationCommand.Create(Discord, x));


/// <summary>
/// Gets a slash command in the current guild.
/// </summary>
/// <param name="id">The unique identifier of the slash command.</param>
/// <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
/// slash command created by the current user.
/// </returns>
public Task<RestGuildCommand> GetSlashCommandAsync(ulong id, RequestOptions options = null)
=> GuildHelper.GetSlashCommandAsync(this, id, Discord, options);
foreach (var command in commands)
{
Discord.State.AddCommand(command);
}

return commands.ToImmutableArray();
}

public async ValueTask<SocketApplicationCommand> GetApplicationCommandAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
var command = Discord.State.GetCommand(id);

if (command != null)
return command;

if (mode == CacheMode.CacheOnly)
return null;

var model = await Discord.ApiClient.GetGlobalApplicationCommandAsync(id, options);

if (model == null)
return null;

command = SocketApplicationCommand.Create(Discord, model);

Discord.State.AddCommand(command);

return command;
}


//Invites //Invites
/// <summary> /// <summary>
@@ -1079,18 +1098,6 @@ namespace Discord.WebSocket
public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null)
=> GuildHelper.GetWebhooksAsync(this, Discord, options); => GuildHelper.GetWebhooksAsync(this, Discord, options);


//Interactions
/// <summary>
/// Gets this guilds slash commands commands
/// </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 application commands found within the guild.
/// </returns>
public async Task<IReadOnlyCollection<RestApplicationCommand>> GetApplicationCommandsAsync(RequestOptions options = null)
=> await Discord.Rest.GetGuildApplicationCommands(this.Id, options);

//Emotes //Emotes
/// <inheritdoc /> /// <inheritdoc />
public Task<IReadOnlyCollection<GuildEmote>> GetEmotesAsync(RequestOptions options = null) public Task<IReadOnlyCollection<GuildEmote>> GetEmotesAsync(RequestOptions options = null)
@@ -1481,6 +1488,8 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
async Task<IReadOnlyCollection<IApplicationCommand>> IGuild.GetApplicationCommandsAsync (RequestOptions options) async Task<IReadOnlyCollection<IApplicationCommand>> IGuild.GetApplicationCommandsAsync (RequestOptions options)
=> await GetApplicationCommandsAsync(options).ConfigureAwait(false); => await GetApplicationCommandsAsync(options).ConfigureAwait(false);
async Task<IApplicationCommand> IGuild.GetApplicationCommandAsync(ulong id, CacheMode mode, RequestOptions options)
=> await GetApplicationCommandAsync(id, mode, options);


void IDisposable.Dispose() void IDisposable.Dispose()
{ {


+ 0
- 48
src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommandCache.cs View File

@@ -1,48 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord.WebSocket.Entities.Interaction
{
internal class SlashCommandCache
{
private readonly ConcurrentDictionary<ulong, SocketSlashCommand> _slashCommands;
private readonly ConcurrentQueue<ulong> _orderedSlashCommands;
private readonly int _size;

public IReadOnlyCollection<SocketSlashCommand> Messages => _slashCommands.ToReadOnlyCollection();

public SlashCommandCache(DiscordSocketClient client)
{
_size = 256;
_slashCommands = new ConcurrentDictionary<ulong, SocketSlashCommand>();

}

public void Add(SocketSlashCommand slashCommand)
{
if (_slashCommands.TryAdd(slashCommand.Id, slashCommand))
{
_orderedSlashCommands.Enqueue(slashCommand.Id);

while (_orderedSlashCommands.Count > _size && _orderedSlashCommands.TryDequeue(out ulong msgId))
_slashCommands.TryRemove(msgId, out _);
}
}

public SocketSlashCommand Remove(ulong id)
{
_slashCommands.TryRemove(id, out var slashCommand);
return slashCommand;
}

public SocketSlashCommand Get(ulong id)
{
_slashCommands.TryGetValue(id, out var slashCommands);
return slashCommands;
}
}
}

+ 12
- 4
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs View File

@@ -5,7 +5,8 @@ using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Model = Discord.API.Gateway.ApplicationCommandCreatedUpdatedEvent;
using GatewayModel = Discord.API.Gateway.ApplicationCommandCreatedUpdatedEvent;
using Model = Discord.API.ApplicationCommand;


namespace Discord.WebSocket namespace Discord.WebSocket
{ {
@@ -60,14 +61,21 @@ namespace Discord.WebSocket
{ {
this.GuildId = guildId; this.GuildId = guildId;
} }
internal static SocketApplicationCommand Create(DiscordSocketClient client, Model model)
internal static SocketApplicationCommand Create(DiscordSocketClient client, GatewayModel model)
{ {
var entity = new SocketApplicationCommand(client, model.Id, model.GuildId.ToNullable()); var entity = new SocketApplicationCommand(client, model.Id, model.GuildId.ToNullable());
entity.Update(model); entity.Update(model);
return entity; return entity;
} }


internal void Update(API.ApplicationCommand model)
internal static SocketApplicationCommand Create(DiscordSocketClient client, Model model, ulong? guildId = null)
{
var entity = new SocketApplicationCommand(client, model.Id, guildId);
entity.Update(model);
return entity;
}

internal void Update(Model model)
{ {
this.ApplicationId = model.ApplicationId; this.ApplicationId = model.ApplicationId;
this.Description = model.Description; this.Description = model.Description;
@@ -102,7 +110,7 @@ namespace Discord.WebSocket
throw new InvalidOperationException($"Cannot modify this application command with the parameter type {nameof(TArg)}"); throw new InvalidOperationException($"Cannot modify this application command with the parameter type {nameof(TArg)}");
} }


API.ApplicationCommand command = null;
Model command = null;


if (this.IsGlobalCommand) if (this.IsGlobalCommand)
{ {


Loading…
Cancel
Save