Update to Labs 3.5.0 (#1971)
* Merge https://github.com/Discord-Net-Labs/Discord.Net-Labs into patch/labs3.5.0 * Add missing periods
This commit is contained in:
@@ -643,12 +643,12 @@ namespace Discord.Commands
|
||||
var bestMatch = parseResults
|
||||
.FirstOrDefault(x => !x.Value.IsSuccess);
|
||||
|
||||
return MatchResult.FromSuccess(bestMatch.Key,bestMatch.Value);
|
||||
return MatchResult.FromSuccess(bestMatch.Key, bestMatch.Value);
|
||||
}
|
||||
|
||||
var chosenOverload = successfulParses[0];
|
||||
|
||||
return MatchResult.FromSuccess(chosenOverload.Key,chosenOverload.Value);
|
||||
return MatchResult.FromSuccess(chosenOverload.Key, chosenOverload.Value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../../Discord.Net.targets" />
|
||||
<Import Project="../../StyleAnalyzer.targets"/>
|
||||
<Import Project="../../StyleAnalyzer.targets" />
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Discord.Net.Commands</AssemblyName>
|
||||
<RootNamespace>Discord.Commands</RootNamespace>
|
||||
@@ -11,5 +11,4 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -9,11 +9,20 @@
|
||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">net6.0;net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
|
||||
<PackageReference Include="System.Interactive.Async" Version="4.0.0" />
|
||||
<PackageReference Include="IDisposableAnalyzers" Version="2.1.2">
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.Interactive.Async" Version="5.0.0" />
|
||||
<PackageReference Include="IDisposableAnalyzers" Version="3.4.15">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
|
||||
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard2.1' ">
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -150,7 +150,7 @@ namespace Discord
|
||||
ServerLocaleUnavailable = 50095,
|
||||
ServerRequiresMonetization = 50097,
|
||||
ServerRequiresBoosts = 50101,
|
||||
|
||||
RequestBodyContainsInvalidJSON = 50109,
|
||||
#endregion
|
||||
|
||||
#region 2FA (60XXX)
|
||||
|
||||
@@ -19,6 +19,9 @@ namespace Discord
|
||||
/// Gets the RPC origins of the application.
|
||||
/// </summary>
|
||||
IReadOnlyCollection<string> RPCOrigins { get; }
|
||||
/// <summary>
|
||||
/// Gets the application's public flags.
|
||||
/// </summary>
|
||||
ApplicationFlags Flags { get; }
|
||||
/// <summary>
|
||||
/// Gets a collection of install parameters for this application.
|
||||
@@ -44,10 +47,18 @@ namespace Discord
|
||||
/// Gets the team associated with this application if there is one.
|
||||
/// </summary>
|
||||
ITeam Team { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the partial user object containing info on the owner of the application.
|
||||
/// </summary>
|
||||
IUser Owner { get; }
|
||||
/// <summary>
|
||||
/// Gets the url of the app's terms of service.
|
||||
/// </summary>
|
||||
public string TermsOfService { get; }
|
||||
/// <summary>
|
||||
/// Gets the the url of the app's privacy policy.
|
||||
/// </summary>
|
||||
public string PrivacyPolicy { get; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,56 @@ namespace Discord
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false,
|
||||
bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a file attachment.
|
||||
/// </summary>
|
||||
/// <param name="fileStream">The file to upload.</param>
|
||||
/// <param name="fileName">The file name of the attachment.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
async Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
{
|
||||
using(var file = new FileAttachment(fileStream, fileName))
|
||||
{
|
||||
await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a file attachment.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file to upload.</param>
|
||||
/// <param name="fileName">The file name of the attachment.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
@@ -55,9 +105,61 @@ namespace Discord
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
Task<IUserMessage> RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false,
|
||||
bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
async Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
{
|
||||
using (var file = new FileAttachment(filePath, fileName))
|
||||
{
|
||||
await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a file attachment.
|
||||
/// </summary>
|
||||
/// <param name="attachment">The attachment containing the file and description.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> RespondWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
#else
|
||||
Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a collection of file attachments.
|
||||
/// </summary>
|
||||
/// <param name="attachments">A collection of attachments to upload.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
/// </summary>
|
||||
@@ -75,13 +177,12 @@ namespace Discord
|
||||
/// </returns>
|
||||
Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="fileStream">The file to upload.</param>
|
||||
/// <param name="fileName">The file name of the attachment.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
@@ -93,15 +194,25 @@ namespace Discord
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
public Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
async Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
{
|
||||
using(var file = new FileAttachment(fileStream, fileName))
|
||||
{
|
||||
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="filePath">The file to upload.</param>
|
||||
/// <param name="fileName">The file name of the attachment.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
@@ -113,8 +224,19 @@ namespace Discord
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
public Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
async Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
{
|
||||
using (var file = new FileAttachment(filePath, fileName))
|
||||
{
|
||||
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
/// </summary>
|
||||
@@ -131,8 +253,14 @@ namespace Discord
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
#else
|
||||
Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
/// </summary>
|
||||
@@ -151,14 +279,12 @@ namespace Discord
|
||||
/// </returns>
|
||||
Task<IUserMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original response for this interaction.
|
||||
/// </summary>
|
||||
/// <param name="options">The request options for this <see langword="async"/> request.</param>
|
||||
/// <returns>A <see cref="IUserMessage"/> that represents the initial response.</returns>
|
||||
Task<IUserMessage> GetOriginalResponseAsync(RequestOptions options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Edits original response for this interaction.
|
||||
/// </summary>
|
||||
@@ -169,7 +295,14 @@ namespace Discord
|
||||
/// contains the updated message.
|
||||
/// </returns>
|
||||
Task<IUserMessage> ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the original response to this interaction.
|
||||
/// </summary>
|
||||
/// <param name="options">The request options for this <see langword="async"/> request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous deletion operation.
|
||||
/// </returns>
|
||||
Task DeleteOriginalResponseAsync(RequestOptions options = null);
|
||||
/// <summary>
|
||||
/// Acknowledges this interaction.
|
||||
/// </summary>
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Discord
|
||||
/// <param name="stream">The stream to create the attachment from.</param>
|
||||
/// <param name="fileName">The name of the attachment.</param>
|
||||
/// <param name="description">The description of the attachment.</param>
|
||||
/// <param name="isSpoiler">Whether or not the attachment is a spoiler.</param>
|
||||
public FileAttachment(Stream stream, string fileName, string description = null, bool isSpoiler = false)
|
||||
{
|
||||
_isDisposed = false;
|
||||
@@ -42,6 +43,9 @@ namespace Discord
|
||||
/// <see cref="File.OpenRead"/>.
|
||||
/// </remarks>
|
||||
/// <param name="path">The path to the file.</param>
|
||||
/// <param name="fileName">The name of the attachment.</param>
|
||||
/// <param name="description">The description of the attachment.</param>
|
||||
/// <param name="isSpoiler">Whether or not the attachment is a spoiler.</param>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid
|
||||
/// characters as defined by <see cref="Path.GetInvalidPathChars"/>.
|
||||
@@ -62,11 +66,11 @@ namespace Discord
|
||||
/// <exception cref="FileNotFoundException">The file specified in <paramref name="path" /> was not found.
|
||||
/// </exception>
|
||||
/// <exception cref="IOException">An I/O error occurred while opening the file. </exception>
|
||||
public FileAttachment(string path, string description = null, bool isSpoiler = false)
|
||||
public FileAttachment(string path, string fileName = null, string description = null, bool isSpoiler = false)
|
||||
{
|
||||
_isDisposed = false;
|
||||
Stream = File.OpenRead(path);
|
||||
FileName = Path.GetFileName(path);
|
||||
FileName = fileName ?? Path.GetFileName(path);
|
||||
Description = description;
|
||||
IsSpoiler = isSpoiler;
|
||||
}
|
||||
|
||||
19
src/Discord.Net.Core/Interactions/IRestInteractionContext.cs
Normal file
19
src/Discord.Net.Core/Interactions/IRestInteractionContext.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public interface IRestInteractionContext : IInteractionContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the callback to use when the service has outgoing json for the rest webhook.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this property is <see langword="null"/> the default callback will be used.
|
||||
/// </remarks>
|
||||
Func<string, Task> InteractionResponseCallback { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Requires the bot to have a specific permission in the channel a command is invoked in.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireBotPermissionAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.GuildPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public GuildPermission? GuildPermission { get; }
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.ChannelPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public ChannelPermission? ChannelPermission { get; }
|
||||
/// <summary>
|
||||
/// Gets or sets the error message if the precondition
|
||||
/// fails due to being run outside of a Guild channel.
|
||||
/// </summary>
|
||||
public string NotAGuildErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Requires the bot account to have a specific <see cref="Discord.GuildPermission"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This precondition will always fail if the command is being invoked in a <see cref="IPrivateChannel"/>.
|
||||
/// </remarks>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.GuildPermission"/> that the bot must have. Multiple permissions can be specified
|
||||
/// by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireBotPermissionAttribute(GuildPermission permission)
|
||||
{
|
||||
GuildPermission = permission;
|
||||
ChannelPermission = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Requires that the bot account to have a specific <see cref="Discord.ChannelPermission"/>.
|
||||
/// </summary>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.ChannelPermission"/> that the bot must have. Multiple permissions can be
|
||||
/// specified by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireBotPermissionAttribute(ChannelPermission permission)
|
||||
{
|
||||
ChannelPermission = permission;
|
||||
GuildPermission = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo command, IServiceProvider services)
|
||||
{
|
||||
IGuildUser guildUser = null;
|
||||
if (context.Guild != null)
|
||||
guildUser = await context.Guild.GetCurrentUserAsync().ConfigureAwait(false);
|
||||
|
||||
if (GuildPermission.HasValue)
|
||||
{
|
||||
if (guildUser == null)
|
||||
return PreconditionResult.FromError(NotAGuildErrorMessage ?? "Command must be used in a guild channel.");
|
||||
if (!guildUser.GuildPermissions.Has(GuildPermission.Value))
|
||||
return PreconditionResult.FromError(ErrorMessage ?? $"Bot requires guild permission {GuildPermission.Value}.");
|
||||
}
|
||||
|
||||
if (ChannelPermission.HasValue)
|
||||
{
|
||||
ChannelPermissions perms;
|
||||
if (context.Channel is IGuildChannel guildChannel)
|
||||
perms = guildUser.GetPermissions(guildChannel);
|
||||
else
|
||||
perms = ChannelPermissions.All(context.Channel);
|
||||
|
||||
if (!perms.Has(ChannelPermission.Value))
|
||||
return PreconditionResult.FromError(ErrorMessage ?? $"Bot requires channel permission {ChannelPermission.Value}.");
|
||||
}
|
||||
|
||||
return PreconditionResult.FromSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the type of command context (i.e. where the command is being executed).
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ContextType
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the command to be executed within a guild.
|
||||
/// </summary>
|
||||
Guild = 0x01,
|
||||
/// <summary>
|
||||
/// Specifies the command to be executed within a DM.
|
||||
/// </summary>
|
||||
DM = 0x02,
|
||||
/// <summary>
|
||||
/// Specifies the command to be executed within a group.
|
||||
/// </summary>
|
||||
Group = 0x04
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requires the command to be invoked in a specified context (e.g. in guild, DM).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireContextAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the context required to execute the command.
|
||||
/// </summary>
|
||||
public ContextType Contexts { get; }
|
||||
|
||||
/// <summary> Requires the command to be invoked in the specified context. </summary>
|
||||
/// <param name="contexts">The type of context the command can be invoked in. Multiple contexts can be specified by ORing the contexts together.</param>
|
||||
/// <example>
|
||||
/// <code language="cs">
|
||||
/// [Command("secret")]
|
||||
/// [RequireContext(ContextType.DM | ContextType.Group)]
|
||||
/// public Task PrivateOnlyAsync()
|
||||
/// {
|
||||
/// return ReplyAsync("shh, this command is a secret");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public RequireContextAttribute(ContextType contexts)
|
||||
{
|
||||
Contexts = contexts;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo command, IServiceProvider services)
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
if ((Contexts & ContextType.Guild) != 0)
|
||||
isValid = context.Channel is IGuildChannel;
|
||||
if ((Contexts & ContextType.DM) != 0)
|
||||
isValid = isValid || context.Channel is IDMChannel;
|
||||
if ((Contexts & ContextType.Group) != 0)
|
||||
isValid = isValid || context.Channel is IGroupChannel;
|
||||
|
||||
if (isValid)
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
else
|
||||
return Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? $"Invalid context for command; accepted contexts: {Contexts}."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Requires the command to be invoked in a channel marked NSFW.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The precondition will restrict the access of the command or module to be accessed within a guild channel
|
||||
/// that has been marked as mature or NSFW. If the channel is not of type <see cref="ITextChannel"/> or the
|
||||
/// channel is not marked as NSFW, the precondition will fail with an erroneous <see cref="PreconditionResult"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example restricts the command <c>too-cool</c> to an NSFW-enabled channel only.
|
||||
/// <code language="cs">
|
||||
/// public class DankModule : ModuleBase
|
||||
/// {
|
||||
/// [Command("cool")]
|
||||
/// public Task CoolAsync()
|
||||
/// => ReplyAsync("I'm cool for everyone.");
|
||||
///
|
||||
/// [RequireNsfw]
|
||||
/// [Command("too-cool")]
|
||||
/// public Task TooCoolAsync()
|
||||
/// => ReplyAsync("You can only see this if you're cool enough.");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireNsfwAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo command, IServiceProvider services)
|
||||
{
|
||||
if (context.Channel is ITextChannel text && text.IsNsfw)
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
else
|
||||
return Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? "This command may only be invoked in an NSFW channel."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Requires the command to be invoked by the owner of the bot.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This precondition will restrict the access of the command or module to the owner of the Discord application.
|
||||
/// If the precondition fails to be met, an erroneous <see cref="PreconditionResult"/> will be returned with the
|
||||
/// message "Command can only be run by the owner of the bot."
|
||||
/// <note>
|
||||
/// This precondition will only work if the account has a <see cref="TokenType"/> of <see cref="TokenType.Bot"/>
|
||||
/// ;otherwise, this precondition will always fail.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireOwnerAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo command, IServiceProvider services)
|
||||
{
|
||||
switch (context.Client.TokenType)
|
||||
{
|
||||
case TokenType.Bot:
|
||||
var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false);
|
||||
if (context.User.Id != application.Owner.Id)
|
||||
return PreconditionResult.FromError(ErrorMessage ?? "Command can only be run by the owner of the bot.");
|
||||
return PreconditionResult.FromSuccess();
|
||||
default:
|
||||
return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Requires the user invoking the command to have a specified role.
|
||||
/// </summary>
|
||||
public class RequireRoleAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified Role name of the precondition.
|
||||
/// </summary>
|
||||
public string RoleName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified Role ID of the precondition.
|
||||
/// </summary>
|
||||
public ulong? RoleId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the error message if the precondition
|
||||
/// fails due to being run outside of a Guild channel.
|
||||
/// </summary>
|
||||
public string NotAGuildErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Requires that the user invoking the command to have a specific Role.
|
||||
/// </summary>
|
||||
/// <param name="roleId">Id of the role that the user must have.</param>
|
||||
public RequireRoleAttribute(ulong roleId)
|
||||
{
|
||||
RoleId = roleId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requires that the user invoking the command to have a specific Role.
|
||||
/// </summary>
|
||||
/// <param name="roleName">Name of the role that the user must have.</param>
|
||||
public RequireRoleAttribute(string roleName)
|
||||
{
|
||||
RoleName = roleName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services)
|
||||
{
|
||||
if (context.User is not IGuildUser guildUser)
|
||||
return Task.FromResult(PreconditionResult.FromError(NotAGuildErrorMessage ?? "Command must be used in a guild channel."));
|
||||
|
||||
if (RoleId.HasValue)
|
||||
{
|
||||
if (guildUser.RoleIds.Contains(RoleId.Value))
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
else
|
||||
Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? $"User requires guild role {context.Guild.GetRole(RoleId.Value).Name}."));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(RoleName))
|
||||
{
|
||||
if (guildUser.Guild.Roles.Any(x => x.Name == RoleName))
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
else
|
||||
Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? $"User requires guild role {RoleName}."));
|
||||
}
|
||||
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Requires the user invoking the command to have a specified permission.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireUserPermissionAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.GuildPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public GuildPermission? GuildPermission { get; }
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.ChannelPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public ChannelPermission? ChannelPermission { get; }
|
||||
/// <summary>
|
||||
/// Gets or sets the error message if the precondition
|
||||
/// fails due to being run outside of a Guild channel.
|
||||
/// </summary>
|
||||
public string NotAGuildErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Requires that the user invoking the command to have a specific <see cref="Discord.GuildPermission"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This precondition will always fail if the command is being invoked in a <see cref="IPrivateChannel"/>.
|
||||
/// </remarks>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.GuildPermission" /> that the user must have. Multiple permissions can be
|
||||
/// specified by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireUserPermissionAttribute(GuildPermission guildPermission)
|
||||
{
|
||||
GuildPermission = guildPermission;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requires that the user invoking the command to have a specific <see cref="Discord.ChannelPermission"/>.
|
||||
/// </summary>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.ChannelPermission"/> that the user must have. Multiple permissions can be
|
||||
/// specified by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireUserPermissionAttribute(ChannelPermission channelPermission)
|
||||
{
|
||||
ChannelPermission = channelPermission;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services)
|
||||
{
|
||||
var guildUser = context.User as IGuildUser;
|
||||
|
||||
if (GuildPermission.HasValue)
|
||||
{
|
||||
if (guildUser == null)
|
||||
return Task.FromResult(PreconditionResult.FromError(NotAGuildErrorMessage ?? "Command must be used in a guild channel."));
|
||||
if (!guildUser.GuildPermissions.Has(GuildPermission.Value))
|
||||
return Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? $"User requires guild permission {GuildPermission.Value}."));
|
||||
}
|
||||
|
||||
if (ChannelPermission.HasValue)
|
||||
{
|
||||
ChannelPermissions perms;
|
||||
if (context.Channel is IGuildChannel guildChannel)
|
||||
perms = guildUser.GetPermissions(guildChannel);
|
||||
else
|
||||
perms = ChannelPermissions.All(context.Channel);
|
||||
|
||||
if (!perms.Has(ChannelPermission.Value))
|
||||
return Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? $"User requires channel permission {ChannelPermission.Value}."));
|
||||
}
|
||||
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,11 @@ namespace Discord.Interactions
|
||||
{
|
||||
case RestAutocompleteInteraction restAutocomplete:
|
||||
var payload = restAutocomplete.Respond(result.Suggestions);
|
||||
await InteractionService._restResponseCallback(context, payload).ConfigureAwait(false);
|
||||
|
||||
if (context is IRestInteractionContext restContext && restContext.InteractionResponseCallback != null)
|
||||
await restContext.InteractionResponseCallback.Invoke(payload).ConfigureAwait(false);
|
||||
else
|
||||
await InteractionService._restResponseCallback(context, payload).ConfigureAwait(false);
|
||||
break;
|
||||
case SocketAutocompleteInteraction socketAutocomplete:
|
||||
await socketAutocomplete.RespondAsync(result.Suggestions).ConfigureAwait(false);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">net6.0;net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<RootNamespace>Discord.Interactions</RootNamespace>
|
||||
<AssemblyName>Discord.Net.Interactions</AssemblyName>
|
||||
<PackageId>Discord.Net.Interactions</PackageId>
|
||||
<Description>A Discord.Net extension adding support for Application Commands.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -30,7 +30,12 @@ namespace Discord.Interactions
|
||||
if (Context.Interaction is not RestInteraction restInteraction)
|
||||
throw new InvalidOperationException($"Invalid interaction type. Interaction must be a type of {nameof(RestInteraction)} in order to execute this method");
|
||||
|
||||
await InteractionService._restResponseCallback(Context, restInteraction.Defer(ephemeral, options)).ConfigureAwait(false);
|
||||
var payload = restInteraction.Defer(ephemeral, options);
|
||||
|
||||
if (Context is IRestInteractionContext restContext && restContext.InteractionResponseCallback != null)
|
||||
await restContext.InteractionResponseCallback.Invoke(payload).ConfigureAwait(false);
|
||||
else
|
||||
await InteractionService._restResponseCallback(Context, payload).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,7 +58,12 @@ namespace Discord.Interactions
|
||||
if (Context.Interaction is not RestInteraction restInteraction)
|
||||
throw new InvalidOperationException($"Invalid interaction type. Interaction must be a type of {nameof(RestInteraction)} in order to execute this method");
|
||||
|
||||
await InteractionService._restResponseCallback(Context, restInteraction.Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options)).ConfigureAwait(false);
|
||||
var payload = restInteraction.Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
|
||||
if (Context is IRestInteractionContext restContext && restContext.InteractionResponseCallback != null)
|
||||
await restContext.InteractionResponseCallback.Invoke(payload).ConfigureAwait(false);
|
||||
else
|
||||
await InteractionService._restResponseCallback(Context, payload).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,17 @@ namespace Discord.API
|
||||
public bool BotRequiresCodeGrant { get; set; }
|
||||
[JsonProperty("install_params")]
|
||||
public Optional<InstallParams> InstallParams { get; set; }
|
||||
|
||||
[JsonProperty("team")]
|
||||
public Team Team { get; set; }
|
||||
|
||||
[JsonProperty("flags"), Int53]
|
||||
public Optional<ApplicationFlags> Flags { get; set; }
|
||||
[JsonProperty("owner")]
|
||||
public Optional<User> Owner { get; set; }
|
||||
[JsonProperty("tags")]
|
||||
public Optional<string[]> Tags { get; set; }
|
||||
[JsonProperty("terms_of_service_url")]
|
||||
public string TermsOfService { get; set; }
|
||||
[JsonProperty("privacy_policy_url")]
|
||||
public string PrivacyPolicy { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
99
src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
Normal file
99
src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using Discord.Net.Converters;
|
||||
using Discord.Net.Rest;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.API.Rest
|
||||
{
|
||||
internal class UploadInteractionFileParams
|
||||
{
|
||||
private static JsonSerializer _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
|
||||
|
||||
public FileAttachment[] Files { get; }
|
||||
|
||||
public InteractionResponseType Type { get; set; }
|
||||
public Optional<string> Content { get; set; }
|
||||
public Optional<bool> IsTTS { get; set; }
|
||||
public Optional<Embed[]> Embeds { get; set; }
|
||||
public Optional<AllowedMentions> AllowedMentions { get; set; }
|
||||
public Optional<ActionRowComponent[]> MessageComponents { get; set; }
|
||||
public Optional<MessageFlags> Flags { get; set; }
|
||||
|
||||
public bool HasData
|
||||
=> Content.IsSpecified ||
|
||||
IsTTS.IsSpecified ||
|
||||
Embeds.IsSpecified ||
|
||||
AllowedMentions.IsSpecified ||
|
||||
MessageComponents.IsSpecified ||
|
||||
Flags.IsSpecified ||
|
||||
Files.Any();
|
||||
|
||||
public UploadInteractionFileParams(params FileAttachment[] files)
|
||||
{
|
||||
Files = files;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, object> ToDictionary()
|
||||
{
|
||||
var d = new Dictionary<string, object>();
|
||||
|
||||
|
||||
var payload = new Dictionary<string, object>();
|
||||
payload["type"] = Type;
|
||||
|
||||
var data = new Dictionary<string, object>();
|
||||
if (Content.IsSpecified)
|
||||
data["content"] = Content.Value;
|
||||
if (IsTTS.IsSpecified)
|
||||
data["tts"] = IsTTS.Value.ToString();
|
||||
if (MessageComponents.IsSpecified)
|
||||
data["components"] = MessageComponents.Value;
|
||||
if (Embeds.IsSpecified)
|
||||
data["embeds"] = Embeds.Value;
|
||||
if (AllowedMentions.IsSpecified)
|
||||
data["allowed_mentions"] = AllowedMentions.Value;
|
||||
if (Flags.IsSpecified)
|
||||
data["flags"] = Flags.Value;
|
||||
|
||||
List<object> attachments = new();
|
||||
|
||||
for (int n = 0; n != Files.Length; n++)
|
||||
{
|
||||
var attachment = Files[n];
|
||||
|
||||
var filename = attachment.FileName ?? "unknown.dat";
|
||||
if (attachment.IsSpoiler && !filename.StartsWith(AttachmentExtensions.SpoilerPrefix))
|
||||
filename = filename.Insert(0, AttachmentExtensions.SpoilerPrefix);
|
||||
d[$"files[{n}]"] = new MultipartFile(attachment.Stream, filename);
|
||||
|
||||
attachments.Add(new
|
||||
{
|
||||
id = (ulong)n,
|
||||
filename = filename,
|
||||
description = attachment.Description ?? Optional<string>.Unspecified
|
||||
});
|
||||
}
|
||||
|
||||
data["attachments"] = attachments;
|
||||
|
||||
payload["data"] = data;
|
||||
|
||||
|
||||
if (data.Any())
|
||||
{
|
||||
var json = new StringBuilder();
|
||||
using (var text = new StringWriter(json))
|
||||
using (var writer = new JsonTextWriter(text))
|
||||
_serializer.Serialize(writer, payload);
|
||||
d["payload_json"] = json.ToString();
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../../Discord.Net.targets" />
|
||||
<Import Project="../../StyleAnalyzer.targets"/>
|
||||
<Import Project="../../StyleAnalyzer.targets" />
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Discord.Net.Rest</AssemblyName>
|
||||
<RootNamespace>Discord.Rest</RootNamespace>
|
||||
|
||||
@@ -1309,7 +1309,20 @@ namespace Discord.API
|
||||
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendJsonAsync<Message>("POST", () => $"interactions/{interactionId}/{interactionToken}/callback", response, new BucketIds(), options: options);
|
||||
await SendJsonAsync("POST", () => $"interactions/{interactionId}/{interactionToken}/callback", response, new BucketIds(), options: options);
|
||||
}
|
||||
public async Task CreateInteractionResponseAsync(UploadInteractionFileParams response, ulong interactionId, string interactionToken, RequestOptions options = null)
|
||||
{
|
||||
if ((!response.Embeds.IsSpecified || response.Embeds.Value == null || response.Embeds.Value.Length == 0) && !response.Files.Any())
|
||||
Preconditions.NotNullOrEmpty(response.Content, nameof(response.Content));
|
||||
|
||||
if (response.Content.IsSpecified && response.Content.Value.Length > DiscordConfig.MaxMessageSize)
|
||||
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(response.Content));
|
||||
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
var ids = new BucketIds();
|
||||
await SendMultipartAsync("POST", () => $"interactions/{interactionId}/{interactionToken}/callback", response.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Message> GetInteractionResponseAsync(string interactionToken, RequestOptions options = null)
|
||||
{
|
||||
|
||||
@@ -347,7 +347,8 @@ namespace Discord.Rest
|
||||
public static Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client,
|
||||
Stream stream, string filename, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, RequestOptions options, bool isSpoiler, Embed[] embeds)
|
||||
{
|
||||
return SendFileAsync(channel, client, new FileAttachment(stream, filename, isSpoiler: isSpoiler), text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds);
|
||||
using var file = new FileAttachment(stream, filename, isSpoiler: isSpoiler);
|
||||
return SendFileAsync(channel, client, file, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds);
|
||||
}
|
||||
|
||||
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
|
||||
|
||||
@@ -34,11 +34,16 @@ namespace Discord.Rest
|
||||
return client.ApiClient.BulkOverwriteGlobalApplicationCommandsAsync(Array.Empty<CreateApplicationCommandParams>(), options);
|
||||
}
|
||||
|
||||
public static async Task<RestInteractionMessage> SendInteractionResponseAsync(BaseDiscordClient client, InteractionResponse response,
|
||||
public static async Task SendInteractionResponseAsync(BaseDiscordClient client, InteractionResponse response,
|
||||
IDiscordInteraction interaction, IMessageChannel channel = null, RequestOptions options = null)
|
||||
{
|
||||
await client.ApiClient.CreateInteractionResponseAsync(response, interaction.Id, interaction.Token, options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task SendInteractionResponseAsync(BaseDiscordClient client, UploadInteractionFileParams response,
|
||||
IDiscordInteraction interaction, IMessageChannel channel = null, RequestOptions options = null)
|
||||
{
|
||||
await client.ApiClient.CreateInteractionResponseAsync(response, interaction.Id, interaction.Token, options).ConfigureAwait(false);
|
||||
return RestInteractionMessage.Create(client, response, interaction, channel);
|
||||
}
|
||||
|
||||
public static async Task<RestInteractionMessage> GetOriginalResponseAsync(BaseDiscordClient client, IMessageChannel channel,
|
||||
@@ -434,6 +439,9 @@ namespace Discord.Rest
|
||||
public static async Task DeleteInteractionResponseAsync(BaseDiscordClient client, RestInteractionMessage message, RequestOptions options = null)
|
||||
=> await client.ApiClient.DeleteInteractionFollowupMessageAsync(message.Id, message.Token, options);
|
||||
|
||||
public static async Task DeleteInteractionResponseAsync(BaseDiscordClient client, IDiscordInteraction interaction, RequestOptions options = null)
|
||||
=> await client.ApiClient.DeleteInteractionFollowupMessageAsync(interaction.Id, interaction.Token, options);
|
||||
|
||||
public static Task SendAutocompleteResultAsync(BaseDiscordClient client, IEnumerable<AutocompleteResult> result, ulong interactionId,
|
||||
string interactionToken, RequestOptions options)
|
||||
{
|
||||
|
||||
@@ -260,15 +260,17 @@ namespace Discord.Rest
|
||||
public abstract Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task DeleteOriginalResponseAsync(RequestOptions options = null)
|
||||
=> InteractionHelper.DeleteInteractionResponseAsync(Discord, this, options);
|
||||
|
||||
#region IDiscordInteraction
|
||||
/// <inheritdoc/>
|
||||
IUser IDiscordInteraction.User => User;
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<IUserMessage> IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
{
|
||||
return Task.FromResult<IUserMessage>(null);
|
||||
}
|
||||
Task IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> Task.FromResult(Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options));
|
||||
/// <inheritdoc/>
|
||||
Task IDiscordInteraction.DeferAsync(bool ephemeral, RequestOptions options)
|
||||
=> Task.FromResult(Defer(ephemeral, options));
|
||||
@@ -296,6 +298,17 @@ namespace Discord.Rest
|
||||
/// <inheritdoc/>
|
||||
async Task<IUserMessage> IDiscordInteraction.FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
/// <inheritdoc/>
|
||||
Task IDiscordInteraction.RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
|
||||
/// <inheritdoc/>
|
||||
#if NETCOREAPP3_0_OR_GREATER != true
|
||||
/// <inheritdoc/>
|
||||
Task IDiscordInteraction.RespondWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
|
||||
/// <inheritdoc/>
|
||||
Task IDiscordInteraction.RespondWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
|
||||
/// <inheritdoc/>
|
||||
Task IDiscordInteraction.RespondWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MessageModel = Discord.API.Message;
|
||||
using Model = Discord.API.InteractionResponse;
|
||||
|
||||
namespace Discord.Rest
|
||||
{
|
||||
@@ -26,24 +25,11 @@ namespace Discord.Rest
|
||||
return entity;
|
||||
}
|
||||
|
||||
internal static RestInteractionMessage Create(BaseDiscordClient discord, Model model, IDiscordInteraction interaction, IMessageChannel channel)
|
||||
{
|
||||
var entity = new RestInteractionMessage(discord, interaction.Id, discord.CurrentUser, interaction.Token, channel);
|
||||
entity.Update(model, interaction);
|
||||
return entity;
|
||||
}
|
||||
|
||||
internal new void Update(MessageModel model)
|
||||
{
|
||||
base.Update(model);
|
||||
}
|
||||
|
||||
internal void Update(Model model, IDiscordInteraction interaction)
|
||||
{
|
||||
ResponseType = model.Type;
|
||||
base.Update(model.ToMessage(interaction));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes this object and all of it's children.
|
||||
/// </summary>
|
||||
|
||||
@@ -29,10 +29,12 @@ namespace Discord.Rest
|
||||
public bool BotRequiresCodeGrant { get; private set; }
|
||||
/// <inheritdoc />
|
||||
public ITeam Team { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IUser Owner { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TermsOfService { get; private set; }
|
||||
/// <inheritdoc />
|
||||
public string PrivacyPolicy { get; private set; }
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
|
||||
/// <inheritdoc />
|
||||
@@ -61,6 +63,8 @@ namespace Discord.Rest
|
||||
IsBotPublic = model.IsBotPublic;
|
||||
BotRequiresCodeGrant = model.BotRequiresCodeGrant;
|
||||
Tags = model.Tags.GetValueOrDefault(null)?.ToImmutableArray() ?? ImmutableArray<string>.Empty;
|
||||
PrivacyPolicy = model.PrivacyPolicy;
|
||||
TermsOfService = model.TermsOfService;
|
||||
var installParams = model.InstallParams.GetValueOrDefault(null);
|
||||
InstallParams = new ApplicationInstallParams(installParams?.Scopes ?? new string[0], (GuildPermission?)installParams?.Permission ?? null);
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Rest
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Rest based context of an <see cref="IDiscordInteraction"/>.
|
||||
/// </summary>
|
||||
public class RestInteractionContext<TInteraction> : IInteractionContext
|
||||
public class RestInteractionContext<TInteraction> : IRestInteractionContext
|
||||
where TInteraction : RestInteraction
|
||||
{
|
||||
/// <summary>
|
||||
@@ -34,6 +37,14 @@ namespace Discord.Rest
|
||||
/// </summary>
|
||||
public TInteraction Interaction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback to use when the service has outgoing json for the rest webhook.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this property is <see langword="null"/> the default callback will be used.
|
||||
/// </remarks>
|
||||
public Func<string, Task> InteractionResponseCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="RestInteractionContext{TInteraction}"/>.
|
||||
/// </summary>
|
||||
@@ -48,6 +59,18 @@ namespace Discord.Rest
|
||||
Interaction = interaction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="RestInteractionContext{TInteraction}"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">The underlying client.</param>
|
||||
/// <param name="interaction">The underlying interaction.</param>
|
||||
/// <param name="interactionResponseCallback">The callback for outgoing json.</param>
|
||||
public RestInteractionContext(DiscordRestClient client, TInteraction interaction, Func<string, Task> interactionResponseCallback)
|
||||
: this(client, interaction)
|
||||
{
|
||||
InteractionResponseCallback = interactionResponseCallback;
|
||||
}
|
||||
|
||||
// IInterationContext
|
||||
/// <inheritdoc/>
|
||||
IDiscordClient IInteractionContext.Client => Client;
|
||||
@@ -66,15 +89,24 @@ namespace Discord.Rest
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Rest based context of an <see cref="IDiscordInteraction"/>
|
||||
/// Represents a Rest based context of an <see cref="IDiscordInteraction"/>.
|
||||
/// </summary>
|
||||
public class RestInteractionContext : RestInteractionContext<RestInteraction>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="RestInteractionContext"/>
|
||||
/// Initializes a new <see cref="RestInteractionContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">The underlying client</param>
|
||||
/// <param name="interaction">The underlying interaction</param>
|
||||
/// <param name="client">The underlying client.</param>
|
||||
/// <param name="interaction">The underlying interaction.</param>
|
||||
public RestInteractionContext(DiscordRestClient client, RestInteraction interaction) : base(client, interaction) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="RestInteractionContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">The underlying client.</param>
|
||||
/// <param name="interaction">The underlying interaction.</param>
|
||||
/// <param name="interactionResponseCallback">The callback for outgoing json.</param>
|
||||
public RestInteractionContext(DiscordRestClient client, RestInteraction interaction, Func<string, Task> interactionResponseCallback)
|
||||
: base(client, interaction, interactionResponseCallback) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,8 @@ namespace Discord.Net.Rest
|
||||
|
||||
continue;
|
||||
}
|
||||
default: throw new InvalidOperationException($"Unsupported param type \"{p.Value.GetType().Name}\".");
|
||||
default:
|
||||
throw new InvalidOperationException($"Unsupported param type \"{p.Value.GetType().Name}\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Discord.API;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
#if DEBUG_LIMITS
|
||||
|
||||
@@ -424,12 +424,12 @@ namespace Discord.WebSocket
|
||||
}
|
||||
internal readonly AsyncEvent<Func<SocketGuildUser, Task>> _userJoinedEvent = new AsyncEvent<Func<SocketGuildUser, Task>>();
|
||||
/// <summary> Fired when a user leaves a guild. </summary>
|
||||
public event Func<SocketUser, Task> UserLeft
|
||||
public event Func<SocketGuild, SocketUser, Task> UserLeft
|
||||
{
|
||||
add { _userLeftEvent.Add(value); }
|
||||
remove { _userLeftEvent.Remove(value); }
|
||||
}
|
||||
internal readonly AsyncEvent<Func<SocketUser, Task>> _userLeftEvent = new AsyncEvent<Func<SocketUser, Task>>();
|
||||
internal readonly AsyncEvent<Func<SocketGuild, SocketUser, Task>> _userLeftEvent = new AsyncEvent<Func<SocketGuild, SocketUser, Task>>();
|
||||
/// <summary> Fired when a user is banned from a guild. </summary>
|
||||
public event Func<SocketUser, SocketGuild, Task> UserBanned
|
||||
{
|
||||
@@ -452,12 +452,12 @@ namespace Discord.WebSocket
|
||||
}
|
||||
internal readonly AsyncEvent<Func<SocketUser, SocketUser, Task>> _userUpdatedEvent = new AsyncEvent<Func<SocketUser, SocketUser, Task>>();
|
||||
/// <summary> Fired when a guild member is updated, or a member presence is updated. </summary>
|
||||
public event Func<Cacheable<SocketGuildUser, RestGuildUser, IGuildUser, ulong>, SocketGuildUser, Task> GuildMemberUpdated
|
||||
public event Func<Cacheable<SocketGuildUser, ulong>, SocketGuildUser, Task> GuildMemberUpdated
|
||||
{
|
||||
add { _guildMemberUpdatedEvent.Add(value); }
|
||||
remove { _guildMemberUpdatedEvent.Remove(value); }
|
||||
}
|
||||
internal readonly AsyncEvent<Func<Cacheable<SocketGuildUser, RestGuildUser, IGuildUser, ulong>, SocketGuildUser, Task>> _guildMemberUpdatedEvent = new AsyncEvent<Func<Cacheable<SocketGuildUser, RestGuildUser, IGuildUser, ulong>, SocketGuildUser, Task>>();
|
||||
internal readonly AsyncEvent<Func<Cacheable<SocketGuildUser, ulong>, SocketGuildUser, Task>> _guildMemberUpdatedEvent = new AsyncEvent<Func<Cacheable<SocketGuildUser, ulong>, SocketGuildUser, Task>>();
|
||||
/// <summary> Fired when a user joins, leaves, or moves voice channels. </summary>
|
||||
public event Func<SocketUser, SocketVoiceState, SocketVoiceState, Task> UserVoiceStateUpdated
|
||||
{
|
||||
|
||||
@@ -445,7 +445,7 @@ namespace Discord.WebSocket
|
||||
client.GuildUpdated += (oldGuild, newGuild) => _guildUpdatedEvent.InvokeAsync(oldGuild, newGuild);
|
||||
|
||||
client.UserJoined += (user) => _userJoinedEvent.InvokeAsync(user);
|
||||
client.UserLeft += (user) => _userLeftEvent.InvokeAsync(user);
|
||||
client.UserLeft += (guild, user) => _userLeftEvent.InvokeAsync(guild, user);
|
||||
client.UserBanned += (user, guild) => _userBannedEvent.InvokeAsync(user, guild);
|
||||
client.UserUnbanned += (user, guild) => _userUnbannedEvent.InvokeAsync(user, guild);
|
||||
client.UserUpdated += (oldUser, newUser) => _userUpdatedEvent.InvokeAsync(oldUser, newUser);
|
||||
|
||||
@@ -1275,13 +1275,13 @@ namespace Discord.WebSocket
|
||||
var before = user.Clone();
|
||||
user.Update(State, data);
|
||||
|
||||
var cacheableBefore = new Cacheable<SocketGuildUser, RestGuildUser, IGuildUser, ulong>(null, user.Id, false, () => Rest.GetGuildUserAsync(guild.Id, user.Id));
|
||||
var cacheableBefore = new Cacheable<SocketGuildUser, ulong>(before, user.Id, true, () => null);
|
||||
await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), cacheableBefore, user).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = guild.AddOrUpdateUser(data);
|
||||
var cacheableBefore = new Cacheable<SocketGuildUser, RestGuildUser, IGuildUser, ulong>(null, user.Id, false, () => Rest.GetGuildUserAsync(guild.Id, user.Id));
|
||||
var cacheableBefore = new Cacheable<SocketGuildUser, ulong>(user, user.Id, true, () => null);
|
||||
await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), cacheableBefore, user).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -1309,10 +1309,14 @@ namespace Discord.WebSocket
|
||||
return;
|
||||
}
|
||||
|
||||
if(user == null)
|
||||
user = State.GetUser(data.User.Id);
|
||||
|
||||
if (user != null)
|
||||
user.Update(State, data.User);
|
||||
else
|
||||
user = SocketGlobalUser.Create(this, State, data.User);
|
||||
|
||||
await TimedInvokeAsync(_userLeftEvent, nameof(UserLeft), user).ConfigureAwait(false);
|
||||
await TimedInvokeAsync(_userLeftEvent, nameof(UserLeft), guild, user).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -71,8 +71,72 @@ namespace Discord.WebSocket
|
||||
}
|
||||
}
|
||||
}
|
||||
public override async Task RespondWithFilesAsync(
|
||||
IEnumerable<FileAttachment> attachments,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
if (!IsValidToken)
|
||||
throw new InvalidOperationException("Interaction token is no longer valid");
|
||||
|
||||
if (!InteractionHelper.CanSendResponse(this))
|
||||
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
|
||||
|
||||
embeds ??= Array.Empty<Embed>();
|
||||
if (embed != null)
|
||||
embeds = new[] { embed }.Concat(embeds).ToArray();
|
||||
|
||||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed.");
|
||||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
|
||||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
|
||||
|
||||
// check that user flag and user Id list are exclusive, same with role flag and role Id list
|
||||
if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
|
||||
{
|
||||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
|
||||
allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
|
||||
{
|
||||
throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
|
||||
}
|
||||
|
||||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
|
||||
allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
|
||||
{
|
||||
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
|
||||
}
|
||||
}
|
||||
|
||||
var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
|
||||
{
|
||||
Type = InteractionResponseType.ChannelMessageWithSource,
|
||||
Content = text ?? Optional<string>.Unspecified,
|
||||
AllowedMentions = allowedMentions != null ? allowedMentions?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
|
||||
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
|
||||
IsTTS = isTTS,
|
||||
MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
|
||||
Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified
|
||||
};
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (HasResponded)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot respond, update, or defer the same interaction twice");
|
||||
}
|
||||
}
|
||||
|
||||
await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
HasResponded = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<RestInteractionMessage> RespondAsync(
|
||||
public override async Task RespondAsync(
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
@@ -121,13 +185,11 @@ namespace Discord.WebSocket
|
||||
AllowedMentions = allowedMentions?.ToModel(),
|
||||
Embeds = embeds.Select(x => x.ToModel()).ToArray(),
|
||||
TTS = isTTS,
|
||||
Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
|
||||
Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified
|
||||
}
|
||||
};
|
||||
|
||||
if (ephemeral)
|
||||
response.Data.Value.Flags = MessageFlags.Ephemeral;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (HasResponded)
|
||||
@@ -136,17 +198,8 @@ namespace Discord.WebSocket
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
HasResponded = true;
|
||||
}
|
||||
}
|
||||
await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
HasResponded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -155,7 +208,7 @@ namespace Discord.WebSocket
|
||||
/// <param name="func">A delegate containing the properties to modify the message with.</param>
|
||||
/// <param name="options">The request options for this <see langword="async"/> request.</param>
|
||||
/// <returns>A task that represents the asynchronous operation of updating the message.</returns>
|
||||
public async Task<RestInteractionMessage> UpdateAsync(Action<MessageProperties> func, RequestOptions options = null)
|
||||
public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions options = null)
|
||||
{
|
||||
var args = new MessageProperties();
|
||||
func(args);
|
||||
@@ -236,12 +289,8 @@ namespace Discord.WebSocket
|
||||
}
|
||||
}
|
||||
|
||||
return await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
HasResponded = true;
|
||||
}
|
||||
await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
HasResponded = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -281,68 +330,6 @@ namespace Discord.WebSocket
|
||||
return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(
|
||||
Stream fileStream,
|
||||
string fileName,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
if (!IsValidToken)
|
||||
throw new InvalidOperationException("Interaction token is no longer valid");
|
||||
|
||||
embeds ??= Array.Empty<Embed>();
|
||||
if (embed != null)
|
||||
embeds = new[] { embed }.Concat(embeds).ToArray();
|
||||
|
||||
Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data");
|
||||
Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
|
||||
|
||||
return FollowupWithFileAsync(new FileAttachment(fileStream, fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(
|
||||
string filePath,
|
||||
string fileName = null,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist");
|
||||
|
||||
fileName ??= Path.GetFileName(filePath);
|
||||
Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
|
||||
|
||||
return FollowupWithFileAsync(new FileAttachment(File.OpenRead(filePath), fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(
|
||||
FileAttachment attachment,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<RestFollowupMessage> FollowupWithFilesAsync(
|
||||
IEnumerable<FileAttachment> attachments,
|
||||
|
||||
@@ -89,20 +89,16 @@ namespace Discord.WebSocket
|
||||
/// </returns>
|
||||
public Task RespondAsync(RequestOptions options = null, params AutocompleteResult[] result)
|
||||
=> RespondAsync(result, options);
|
||||
public override Task<RestInteractionMessage> RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
public override Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
public override Task DeferAsync(bool ephemeral = false, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
public override Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> throw new NotSupportedException("Autocomplete interactions don't support this method!");
|
||||
|
||||
//IAutocompleteInteraction
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Discord.WebSocket
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<RestInteractionMessage> RespondAsync(
|
||||
public override async Task RespondAsync(
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
@@ -131,17 +131,72 @@ namespace Discord.WebSocket
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
HasResponded = true;
|
||||
}
|
||||
|
||||
public override async Task RespondWithFilesAsync(
|
||||
IEnumerable<FileAttachment> attachments,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
if (!IsValidToken)
|
||||
throw new InvalidOperationException("Interaction token is no longer valid");
|
||||
|
||||
if (!InteractionHelper.CanSendResponse(this))
|
||||
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
|
||||
|
||||
embeds ??= Array.Empty<Embed>();
|
||||
if (embed != null)
|
||||
embeds = new[] { embed }.Concat(embeds).ToArray();
|
||||
|
||||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed.");
|
||||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
|
||||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
|
||||
|
||||
// check that user flag and user Id list are exclusive, same with role flag and role Id list
|
||||
if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
|
||||
{
|
||||
return await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_lock)
|
||||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
|
||||
allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
|
||||
{
|
||||
HasResponded = true;
|
||||
throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
|
||||
}
|
||||
|
||||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
|
||||
allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
|
||||
{
|
||||
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
|
||||
}
|
||||
}
|
||||
|
||||
var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
|
||||
{
|
||||
Type = InteractionResponseType.ChannelMessageWithSource,
|
||||
Content = text ?? Optional<string>.Unspecified,
|
||||
AllowedMentions = allowedMentions != null ? allowedMentions?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
|
||||
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
|
||||
IsTTS = isTTS,
|
||||
Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
|
||||
MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified
|
||||
};
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (HasResponded)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot respond, update, or defer the same interaction twice");
|
||||
}
|
||||
}
|
||||
|
||||
await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
|
||||
HasResponded = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -180,69 +235,7 @@ namespace Discord.WebSocket
|
||||
|
||||
return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(
|
||||
Stream fileStream,
|
||||
string fileName,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
if (!IsValidToken)
|
||||
throw new InvalidOperationException("Interaction token is no longer valid");
|
||||
|
||||
embeds ??= Array.Empty<Embed>();
|
||||
if (embed != null)
|
||||
embeds = new[] { embed }.Concat(embeds).ToArray();
|
||||
|
||||
Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data");
|
||||
Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
|
||||
|
||||
return FollowupWithFileAsync(new FileAttachment(fileStream, fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(
|
||||
string filePath,
|
||||
string fileName = null,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist");
|
||||
|
||||
fileName ??= Path.GetFileName(filePath);
|
||||
Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
|
||||
|
||||
return FollowupWithFileAsync(new FileAttachment(File.OpenRead(filePath), fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<RestFollowupMessage> FollowupWithFileAsync(
|
||||
FileAttachment attachment,
|
||||
string text = null,
|
||||
Embed[] embeds = null,
|
||||
bool isTTS = false,
|
||||
bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null,
|
||||
MessageComponent components = null,
|
||||
Embed embed = null,
|
||||
RequestOptions options = null)
|
||||
{
|
||||
return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<RestFollowupMessage> FollowupWithFilesAsync(
|
||||
IEnumerable<FileAttachment> attachments,
|
||||
|
||||
@@ -136,9 +136,97 @@ namespace Discord.WebSocket
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
|
||||
/// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception>
|
||||
public abstract Task<RestInteractionMessage> RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false,
|
||||
public abstract Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false,
|
||||
bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a file attachment.
|
||||
/// </summary>
|
||||
/// <param name="fileStream">The file to upload.</param>
|
||||
/// <param name="fileName">The file name of the attachment.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
public async Task RespondWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
{
|
||||
using (var file = new FileAttachment(fileStream, fileName))
|
||||
{
|
||||
await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a file attachment.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file to upload.</param>
|
||||
/// <param name="fileName">The file name of the attachment.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
public async Task RespondWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
{
|
||||
using (var file = new FileAttachment(filePath, fileName))
|
||||
{
|
||||
await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a file attachment.
|
||||
/// </summary>
|
||||
/// <param name="attachment">The attachment containing the file and description.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
public Task RespondWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> RespondWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
|
||||
/// <summary>
|
||||
/// Responds to this interaction with a collection of file attachments.
|
||||
/// </summary>
|
||||
/// <param name="attachments">A collection of attachments to upload.</param>
|
||||
/// <param name="text">The text of the message to be sent.</param>
|
||||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param>
|
||||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
|
||||
/// <param name="allowedMentions">The allowed mentions for this response.</param>
|
||||
/// <param name="options">The request options for this response.</param>
|
||||
/// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param>
|
||||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
public abstract Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
/// </summary>
|
||||
@@ -172,8 +260,14 @@ namespace Discord.WebSocket
|
||||
/// <returns>
|
||||
/// The sent message.
|
||||
/// </returns>
|
||||
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
public async Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
{
|
||||
using (var file = new FileAttachment(fileStream, fileName))
|
||||
{
|
||||
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
@@ -191,8 +285,14 @@ namespace Discord.WebSocket
|
||||
/// <returns>
|
||||
/// The sent message.
|
||||
/// </returns>
|
||||
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
public async Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
{
|
||||
using (var file = new FileAttachment(filePath, fileName))
|
||||
{
|
||||
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
@@ -210,8 +310,9 @@ namespace Discord.WebSocket
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
|
||||
public Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
|
||||
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
|
||||
=> FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a followup message for this interaction.
|
||||
@@ -252,6 +353,10 @@ namespace Discord.WebSocket
|
||||
return RestInteractionMessage.Create(Discord, model, Token, Channel);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task DeleteOriginalResponseAsync(RequestOptions options = null)
|
||||
=> InteractionHelper.DeleteInteractionResponseAsync(Discord, this, options);
|
||||
|
||||
/// <summary>
|
||||
/// Acknowledges this interaction.
|
||||
/// </summary>
|
||||
@@ -275,12 +380,16 @@ namespace Discord.WebSocket
|
||||
async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options)
|
||||
=> await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false);
|
||||
/// <inheritdoc/>
|
||||
async Task<IUserMessage> IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
async Task IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> await RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
/// <inheritdoc/>
|
||||
async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
/// <inheritdoc/>
|
||||
async Task<IUserMessage> IDiscordInteraction.FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
#if NETCOREAPP3_0_OR_GREATER != true
|
||||
/// <inheritdoc/>
|
||||
async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed).ConfigureAwait(false);
|
||||
/// <inheritdoc/>
|
||||
@@ -289,9 +398,7 @@ namespace Discord.WebSocket
|
||||
/// <inheritdoc/>
|
||||
async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> await FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
/// <inheritdoc/>
|
||||
async Task<IUserMessage> IDiscordInteraction.FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
|
||||
=> await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../../Discord.Net.targets" />
|
||||
<Import Project="../../StyleAnalyzer.targets"/>
|
||||
<Import Project="../../StyleAnalyzer.targets" />
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Discord.Net.Webhook</AssemblyName>
|
||||
<RootNamespace>Discord.Webhook</RootNamespace>
|
||||
|
||||
Reference in New Issue
Block a user