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:
Quin Lynch
2021-12-19 03:41:58 -04:00
committed by GitHub
parent 52e2019990
commit 6c7502da68
41 changed files with 1097 additions and 272 deletions

View File

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

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

View File

@@ -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>

View File

@@ -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)
{

View File

@@ -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>

View File

@@ -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)
{

View File

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

View File

@@ -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>

View File

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

View File

@@ -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) { }
}
}

View File

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

View File

@@ -1,3 +1,4 @@
using Discord.API;
using Newtonsoft.Json;
using System;
#if DEBUG_LIMITS