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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user