Browse Source

Merge branch 'release/3.x' of https://github.com/Discord-Net-Labs/Discord.Net-Labs into release/3.x

pull/1923/head
quin lynch 3 years ago
parent
commit
201cff7bff
7 changed files with 301 additions and 3 deletions
  1. +1
    -1
      src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
  2. +46
    -0
      src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
  3. +5
    -2
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  4. +48
    -0
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
  5. +81
    -0
      src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs
  6. +81
    -0
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
  7. +39
    -0
      src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs

+ 1
- 1
src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs View File

@@ -12,7 +12,7 @@ namespace Discord
/// <summary> Gets a <see cref="GuildPermissions"/> that grants all guild permissions for webhook users. </summary>
public static readonly GuildPermissions Webhook = new GuildPermissions(0b00000_0000000_0001101100000_000000);
/// <summary> Gets a <see cref="GuildPermissions"/> that grants all guild permissions. </summary>
public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111111_1111111111111_111111);
public static readonly GuildPermissions All = new GuildPermissions(0b11111111_11111_1111111_1111111111111_11111);

/// <summary> Gets a packed value representing all the permissions in this <see cref="GuildPermissions"/>. </summary>
public ulong RawValue { get; }


+ 46
- 0
src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs View File

@@ -1,11 +1,18 @@
#pragma warning disable CS1591
using Discord.Net.Converters;
using Discord.Net.Rest;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
internal class CreateWebhookMessageParams
{
private static JsonSerializer _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };

[JsonProperty("content")]
public string Content { get; set; }

@@ -32,5 +39,44 @@ namespace Discord.API.Rest

[JsonProperty("components")]
public Optional<API.ActionRowComponent[]> Components { get; set; }

[JsonProperty("file")]
public Optional<MultipartFile> File { get; set; }

public IReadOnlyDictionary<string, object> ToDictionary()
{
var d = new Dictionary<string, object>();

if (File.IsSpecified)
{
d["file"] = File.Value;
}

var payload = new Dictionary<string, object>();

payload["content"] = Content;

if (IsTTS.IsSpecified)
payload["tts"] = IsTTS.Value.ToString();
if (Nonce.IsSpecified)
payload["nonce"] = Nonce.Value;
if (Username.IsSpecified)
payload["username"] = Username.Value;
if (AvatarUrl.IsSpecified)
payload["avatar_url"] = AvatarUrl.Value;
if (Embeds.IsSpecified)
payload["embeds"] = Embeds.Value;
if (AllowedMentions.IsSpecified)
payload["allowed_mentions"] = AllowedMentions.Value;

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

+ 5
- 2
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -1286,7 +1286,7 @@ namespace Discord.API

public async Task<Message> CreateInteractionFollowupMessage(CreateWebhookMessageParams args, string token, RequestOptions options = null)
{
if (!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0)
if ((!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0) && !args.File.IsSpecified)
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));

if (args.Content?.Length > DiscordConfig.MaxMessageSize)
@@ -1294,7 +1294,10 @@ namespace Discord.API

options = RequestOptions.CreateOrClone(options);

return await SendJsonAsync<Message>("POST", () => $"webhooks/{CurrentUserId}/{token}?wait=true", args, new BucketIds(), options: options).ConfigureAwait(false);
if (!args.File.IsSpecified)
return await SendJsonAsync<Message>("POST", () => $"webhooks/{CurrentUserId}/{token}?wait=true", args, new BucketIds(), options: options).ConfigureAwait(false);
else
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{CurrentUserId}/{token}?wait=true", args.ToDictionary(), new BucketIds(), options: options).ConfigureAwait(false);
}

public async Task<Message> ModifyInteractionFollowupMessage(ModifyInteractionResponseParams args, ulong id, string token, RequestOptions options = null)


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

@@ -3911,6 +3911,12 @@
<member name="M:Discord.WebSocket.SocketMessageComponent.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketMessageComponent.FollowupWithFileAsync(System.String,System.IO.Stream,System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketMessageComponent.FollowupWithFileAsync(System.String,System.String,System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketMessageComponent.DeferLoadingAsync(System.Boolean,Discord.RequestOptions)">
<summary>
Defers an interaction and responds with type 5 (<see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>)
@@ -4090,6 +4096,12 @@
<member name="M:Discord.WebSocket.SocketCommandBase.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketCommandBase.FollowupWithFileAsync(System.String,System.IO.Stream,System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketCommandBase.FollowupWithFileAsync(System.String,System.String,System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketCommandBase.DeferAsync(System.Boolean,Discord.RequestOptions)">
<summary>
Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>.
@@ -4194,6 +4206,42 @@
The sent message.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketInteraction.FollowupWithFileAsync(System.String,System.IO.Stream,System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<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="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="component">A <see cref="T:Discord.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>
The sent message.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketInteraction.FollowupWithFileAsync(System.String,System.String,System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<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="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="component">A <see cref="T:Discord.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>
The sent message.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketInteraction.GetOriginalResponseAsync(Discord.RequestOptions)">
<summary>
Gets the original response for this interaction.


+ 81
- 0
src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs View File

@@ -5,6 +5,8 @@ using Model = Discord.API.Interaction;
using DataModel = Discord.API.MessageComponentInteractionData;
using Discord.Rest;
using System.Collections.Generic;
using Discord.Net.Rest;
using System.IO;

namespace Discord.WebSocket
{
@@ -243,6 +245,85 @@ namespace Discord.WebSocket
return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}

/// <inheritdoc/>
public override async Task<RestFollowupMessage> FollowupWithFileAsync(
string text = null,
Stream fileStream = null,
string fileName = null,
Embed[] embeds = null,
bool isTTS = false,
bool ephemeral = false,
AllowedMentions allowedMentions = null,
RequestOptions options = null,
MessageComponent component = null,
Embed embed = null)
{
if (!IsValidToken)
throw new InvalidOperationException("Interaction token is no longer valid");

if (embeds == null && embed != null)
embeds = new[] { embed };
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 ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data");
Preconditions.NotNullOrWhitespace(fileName, nameof(fileName), "File Name must not be empty or null");

var args = new API.Rest.CreateWebhookMessageParams
{
Content = text,
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
IsTTS = isTTS,
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified
};

if (ephemeral)
args.Flags = MessageFlags.Ephemeral;

return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}

/// <inheritdoc/>
public override async Task<RestFollowupMessage> FollowupWithFileAsync(
string text = null,
string filePath = null,
string fileName = null,
Embed[] embeds = null,
bool isTTS = false,
bool ephemeral = false,
AllowedMentions allowedMentions = null,
RequestOptions options = null,
MessageComponent component = null,
Embed embed = null)
{
if (!IsValidToken)
throw new InvalidOperationException("Interaction token is no longer valid");

if (embeds == null && embed != null)
embeds = new[] { embed };
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 ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
Preconditions.NotNullOrWhitespace(filePath, nameof(filePath), "Path must exist");

var args = new API.Rest.CreateWebhookMessageParams
{
Content = text,
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
IsTTS = isTTS,
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified
};

if (ephemeral)
args.Flags = MessageFlags.Ephemeral;

return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}

/// <summary>
/// Defers an interaction and responds with type 5 (<see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>)
/// </summary>


+ 81
- 0
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs View File

@@ -1,6 +1,8 @@
using Discord.Net.Rest;
using Discord.Rest;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -146,6 +148,85 @@ namespace Discord.WebSocket
return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}

/// <inheritdoc/>
public override async Task<RestFollowupMessage> FollowupWithFileAsync(
string text = null,
Stream fileStream = null,
string fileName = null,
Embed[] embeds = null,
bool isTTS = false,
bool ephemeral = false,
AllowedMentions allowedMentions = null,
RequestOptions options = null,
MessageComponent component = null,
Embed embed = null)
{
if (!IsValidToken)
throw new InvalidOperationException("Interaction token is no longer valid");

if (embeds == null && embed != null)
embeds = new[] { embed };
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 ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data");
Preconditions.NotNullOrWhitespace(fileName, nameof(fileName), "File Name must not be empty or null");

var args = new API.Rest.CreateWebhookMessageParams
{
Content = text,
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
IsTTS = isTTS,
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified
};

if (ephemeral)
args.Flags = MessageFlags.Ephemeral;

return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}

/// <inheritdoc/>
public override async Task<RestFollowupMessage> FollowupWithFileAsync(
string text = null,
string filePath = null,
string fileName = null,
Embed[] embeds = null,
bool isTTS = false,
bool ephemeral = false,
AllowedMentions allowedMentions = null,
RequestOptions options = null,
MessageComponent component = null,
Embed embed = null)
{
if (!IsValidToken)
throw new InvalidOperationException("Interaction token is no longer valid");

if (embeds == null && embed != null)
embeds = new[] { embed };
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 ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
Preconditions.NotNullOrWhitespace(filePath, nameof(filePath), "Path must exist");

var args = new API.Rest.CreateWebhookMessageParams
{
Content = text,
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
IsTTS = isTTS,
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified
};

if (ephemeral)
args.Flags = MessageFlags.Ephemeral;

return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}

/// <summary>
/// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>.
/// </summary>


+ 39
- 0
src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs View File

@@ -3,6 +3,7 @@ using System;
using System.Threading.Tasks;
using Model = Discord.API.Interaction;
using DataModel = Discord.API.ApplicationCommandInteractionData;
using System.IO;

namespace Discord.WebSocket
{
@@ -145,6 +146,44 @@ namespace Discord.WebSocket
public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = 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="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="component">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>
/// The sent message.
/// </returns>
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string text = null, Stream fileStream = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null);

/// <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="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="component">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>
/// The sent message.
/// </returns>
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string text = null, string filePath = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null);

/// <summary>
/// Gets the original response for this interaction.
/// </summary>


Loading…
Cancel
Save