From e9b807a0dd5ea877d54ee856aa2581b57086d814 Mon Sep 17 00:00:00 2001 From: quin lynch Date: Tue, 24 Aug 2021 03:18:20 -0300 Subject: [PATCH] Fix #115. Added 2 more methods to upload stickers via filepath or stream --- src/Discord.Net.Core/Discord.Net.Core.xml | 27 +++++++++++ .../Entities/Guilds/IGuild.cs | 28 +++++++++++ src/Discord.Net.Rest/API/Net/MultipartFile.cs | 6 ++- .../API/Rest/CreateStickerParams.cs | 14 ++++-- src/Discord.Net.Rest/Discord.Net.Rest.xml | 45 ++++++++++++++++++ .../Entities/Guilds/GuildHelper.cs | 27 +++++++++++ .../Entities/Guilds/RestGuild.cs | 47 +++++++++++++++++++ src/Discord.Net.Rest/Net/DefaultRestClient.cs | 13 ++++- .../Entities/Guilds/SocketGuild.cs | 47 +++++++++++++++++++ 9 files changed, 247 insertions(+), 7 deletions(-) diff --git a/src/Discord.Net.Core/Discord.Net.Core.xml b/src/Discord.Net.Core/Discord.Net.Core.xml index 989f35ec1..f60adb42c 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.xml +++ b/src/Discord.Net.Core/Discord.Net.Core.xml @@ -3945,6 +3945,33 @@ A task that represents the asynchronous creation operation. The task result contains the created sticker. + + + Creates a new sticker in this guild + + The name of the sticker. + The description of the sticker. + The tags of the sticker. + The path of the file to upload. + The options to be used when sending the request. + + A task that represents the asynchronous creation operation. The task result contains the created sticker. + + + + + Creates a new sticker in this guild + + The name of the sticker. + The description of the sticker. + The tags of the sticker. + The stream containing the file data. + The name of the file with the extension, ex: image.png + The options to be used when sending the request. + + A task that represents the asynchronous creation operation. The task result contains the created sticker. + + Gets a specific sticker within this guild. diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index d9daf80cd..314e7e6c3 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -2,6 +2,7 @@ using Discord.Audio; using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Threading.Tasks; namespace Discord @@ -962,6 +963,33 @@ namespace Discord /// Task CreateStickerAsync(string name, string description, IEnumerable tags, Image image, RequestOptions options = null); + /// + /// Creates a new sticker in this guild + /// + /// The name of the sticker. + /// The description of the sticker. + /// The tags of the sticker. + /// The path of the file to upload. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the created sticker. + /// + Task CreateStickerAsync(string name, string description, IEnumerable tags, string path, RequestOptions options = null); + + /// + /// Creates a new sticker in this guild + /// + /// The name of the sticker. + /// The description of the sticker. + /// The tags of the sticker. + /// The stream containing the file data. + /// The name of the file with the extension, ex: image.png + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the created sticker. + /// + Task CreateStickerAsync(string name, string description, IEnumerable tags, Stream stream, string filename, RequestOptions options = null); + /// /// Gets a specific sticker within this guild. /// diff --git a/src/Discord.Net.Rest/API/Net/MultipartFile.cs b/src/Discord.Net.Rest/API/Net/MultipartFile.cs index 604852e90..ab28c0dbc 100644 --- a/src/Discord.Net.Rest/API/Net/MultipartFile.cs +++ b/src/Discord.Net.Rest/API/Net/MultipartFile.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Discord.Net.Rest { @@ -6,11 +6,13 @@ namespace Discord.Net.Rest { public Stream Stream { get; } public string Filename { get; } + public string ContentType { get; } - public MultipartFile(Stream stream, string filename) + public MultipartFile(Stream stream, string filename, string contentType = null) { Stream = stream; Filename = filename; + this.ContentType = contentType; } } } diff --git a/src/Discord.Net.Rest/API/Rest/CreateStickerParams.cs b/src/Discord.Net.Rest/API/Rest/CreateStickerParams.cs index be9cb05f0..405d3e04e 100644 --- a/src/Discord.Net.Rest/API/Rest/CreateStickerParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateStickerParams.cs @@ -15,17 +15,25 @@ namespace Discord.API.Rest public string Name { get; set; } public string Description { get; set; } public string Tags { get; set; } + public string FileName { get; set; } public IReadOnlyDictionary ToDictionary() { var d = new Dictionary(); - d["file"] = new MultipartFile(File, Name + ".dat"); - - d["name"] = Name; + d["name"] = $"{Name}"; d["description"] = Description; d["tags"] = Tags; + string contentType = "image/png"; + + if (File is FileStream fileStream) + contentType = $"image/{Path.GetExtension(fileStream.Name)}"; + else if(FileName != null) + contentType = $"image/{Path.GetExtension(FileName)}"; + + d["file"] = new MultipartFile(File, FileName ?? "image", contentType.Replace(".", "")); + return d; } } diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.xml b/src/Discord.Net.Rest/Discord.Net.Rest.xml index 4cb5f6e02..2c53beefc 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.xml +++ b/src/Discord.Net.Rest/Discord.Net.Rest.xml @@ -3540,6 +3540,33 @@ A task that represents the asynchronous creation operation. The task result contains the created sticker. + + + Creates a new sticker in this guild + + The name of the sticker. + The description of the sticker. + The tags of the sticker. + The path of the file to upload. + The options to be used when sending the request. + + A task that represents the asynchronous creation operation. The task result contains the created sticker. + + + + + Creates a new sticker in this guild + + The name of the sticker. + The description of the sticker. + The tags of the sticker. + The stream containing the file data. + The name of the file with the extension, ex: image.png + The options to be used when sending the request. + + A task that represents the asynchronous creation operation. The task result contains the created sticker. + + Gets a specific sticker within this guild. @@ -3707,6 +3734,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index bedb9de2f..b1fb2f044 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -8,6 +8,7 @@ using WidgetModel = Discord.API.GuildWidget; using Model = Discord.API.Guild; using RoleModel = Discord.API.Role; using ImageModel = Discord.API.Image; +using System.IO; namespace Discord.Rest { @@ -559,6 +560,32 @@ namespace Discord.Rest return await client.ApiClient.CreateGuildStickerAsync(apiArgs, guild.Id, options).ConfigureAwait(false); } + public static async Task CreateStickerAsync(BaseDiscordClient client, IGuild guild, string name, string description, IEnumerable tags, + Stream file, string filename, RequestOptions options = null) + { + Preconditions.NotNull(name, nameof(name)); + Preconditions.NotNull(description, nameof(description)); + Preconditions.NotNull(file, nameof(file)); + Preconditions.NotNull(filename, nameof(filename)); + + Preconditions.AtLeast(name.Length, 2, nameof(name)); + Preconditions.AtLeast(description.Length, 2, nameof(description)); + + Preconditions.AtMost(name.Length, 30, nameof(name)); + Preconditions.AtMost(description.Length, 100, nameof(name)); + + var apiArgs = new CreateStickerParams() + { + Name = name, + Description = description, + File = file, + Tags = string.Join(", ", tags), + FileName = filename + }; + + return await client.ApiClient.CreateGuildStickerAsync(apiArgs, guild.Id, options).ConfigureAwait(false); + } + public static async Task ModifyStickerAsync(BaseDiscordClient client, ulong guildId, ISticker sticker, Action func, RequestOptions options = null) { diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index e87e24b25..b27bcd996 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading.Tasks; using WidgetModel = Discord.API.GuildWidget; using Model = Discord.API.Guild; +using System.IO; namespace Discord.Rest { @@ -946,6 +947,42 @@ namespace Discord.Rest return CustomSticker.Create(Discord, model, this, model.User.IsSpecified ? model.User.Value.Id : null); } /// + /// Creates a new sticker in this guild + /// + /// The name of the sticker. + /// The description of the sticker. + /// The tags of the sticker. + /// The path of the file to upload. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the created sticker. + /// + public Task CreateStickerAsync(string name, string description, IEnumerable tags, string path, + RequestOptions options = null) + { + var fs = File.OpenRead(path); + return CreateStickerAsync(name, description, tags, fs, Path.GetFileName(fs.Name), options); + } + /// + /// Creates a new sticker in this guild + /// + /// The name of the sticker. + /// The description of the sticker. + /// The tags of the sticker. + /// The stream containing the file data. + /// The name of the file with the extension, ex: image.png + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the created sticker. + /// + public async Task CreateStickerAsync(string name, string description, IEnumerable tags, Stream stream, + string filename, RequestOptions options = null) + { + var model = await GuildHelper.CreateStickerAsync(Discord, this, name, description, tags, stream, filename, options).ConfigureAwait(false); + + return CustomSticker.Create(Discord, model, this, model.User.IsSpecified ? model.User.Value.Id : null); + } + /// /// Gets a specific sticker within this guild. /// /// The id of the sticker to get. @@ -1262,8 +1299,16 @@ namespace Discord.Rest /// async Task> IGuild.GetApplicationCommandsAsync (RequestOptions options) => await GetApplicationCommandsAsync(options).ConfigureAwait(false); + /// async Task IGuild.CreateStickerAsync(string name, string description, IEnumerable tags, Image image, RequestOptions options) => await CreateStickerAsync(name, description, tags, image, options); + /// + async Task IGuild.CreateStickerAsync(string name, string description, IEnumerable tags, Stream stream, string filename, RequestOptions options) + => await CreateStickerAsync(name, description, tags, stream, filename, options); + /// + async Task IGuild.CreateStickerAsync(string name, string description, IEnumerable tags, string path, RequestOptions options) + => await CreateStickerAsync(name, description, tags, path, options); + /// async Task IGuild.GetStickerAsync(ulong id, CacheMode mode, RequestOptions options) { if (mode != CacheMode.AllowDownload) @@ -1271,6 +1316,7 @@ namespace Discord.Rest return await GetStickerAsync(id, options); } + /// async Task> IGuild.GetStickersAsync(CacheMode mode, RequestOptions options) { if (mode != CacheMode.AllowDownload) @@ -1278,6 +1324,7 @@ namespace Discord.Rest return await GetStickersAsync(options); } + /// Task IGuild.DeleteStickerAsync(ICustomSticker sticker, RequestOptions options) => sticker.DeleteAsync(); } diff --git a/src/Discord.Net.Rest/Net/DefaultRestClient.cs b/src/Discord.Net.Rest/Net/DefaultRestClient.cs index b5036d94e..62ebd6d78 100644 --- a/src/Discord.Net.Rest/Net/DefaultRestClient.cs +++ b/src/Discord.Net.Rest/Net/DefaultRestClient.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -101,7 +102,7 @@ namespace Discord.Net.Rest switch (p.Value) { #pragma warning disable IDISP004 - case string stringValue: { content.Add(new StringContent(stringValue), p.Key); continue; } + case string stringValue: { content.Add(new StringContent(stringValue, Encoding.UTF8, "text/plain"), p.Key); continue; } case byte[] byteArrayValue: { content.Add(new ByteArrayContent(byteArrayValue), p.Key); continue; } case Stream streamValue: { content.Add(new StreamContent(streamValue), p.Key); continue; } case MultipartFile fileValue: @@ -116,8 +117,16 @@ namespace Discord.Net.Rest stream = memoryStream; #pragma warning restore IDISP001 } - content.Add(new StreamContent(stream), p.Key, fileValue.Filename); + + var streamContent = new StreamContent(stream); + var extension = fileValue.Filename.Split('.').Last(); + + if(fileValue.ContentType != null) + streamContent.Headers.ContentType = new MediaTypeHeaderValue(fileValue.ContentType); + + content.Add(streamContent, p.Key, fileValue.Filename); #pragma warning restore IDISP004 + continue; } default: throw new InvalidOperationException($"Unsupported param type \"{p.Value.GetType().Name}\"."); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 390bc0512..205642634 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -20,6 +20,7 @@ using RoleModel = Discord.API.Role; using UserModel = Discord.API.User; using VoiceStateModel = Discord.API.VoiceState; using StickerModel = Discord.API.Sticker; +using System.IO; namespace Discord.WebSocket { @@ -1248,6 +1249,42 @@ namespace Discord.WebSocket return AddOrUpdateSticker(model); } /// + /// Creates a new sticker in this guild + /// + /// The name of the sticker. + /// The description of the sticker. + /// The tags of the sticker. + /// The path of the file to upload. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the created sticker. + /// + public Task CreateStickerAsync(string name, string description, IEnumerable tags, string path, + RequestOptions options = null) + { + var fs = File.OpenRead(path); + return CreateStickerAsync(name, description, tags, fs, Path.GetFileName(fs.Name), options); + } + /// + /// Creates a new sticker in this guild + /// + /// The name of the sticker. + /// The description of the sticker. + /// The tags of the sticker. + /// The stream containing the file data. + /// The name of the file with the extension, ex: image.png + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the created sticker. + /// + public async Task CreateStickerAsync(string name, string description, IEnumerable tags, Stream stream, + string filename, RequestOptions options = null) + { + var model = await GuildHelper.CreateStickerAsync(Discord, this, name, description, tags, stream, filename, options).ConfigureAwait(false); + + return AddOrUpdateSticker(model); + } + /// /// Deletes a sticker within this guild. /// /// The sticker to delete. @@ -1632,12 +1669,22 @@ namespace Discord.WebSocket /// async Task> IGuild.GetApplicationCommandsAsync (RequestOptions options) => await GetApplicationCommandsAsync(options).ConfigureAwait(false); + /// async Task IGuild.CreateStickerAsync(string name, string description, IEnumerable tags, Image image, RequestOptions options) => await CreateStickerAsync(name, description, tags, image, options); + /// + async Task IGuild.CreateStickerAsync(string name, string description, IEnumerable tags, Stream stream, string filename, RequestOptions options) + => await CreateStickerAsync(name, description, tags, stream, filename, options); + /// + async Task IGuild.CreateStickerAsync(string name, string description, IEnumerable tags, string path, RequestOptions options) + => await CreateStickerAsync(name, description, tags, path, options); + /// async Task IGuild.GetStickerAsync(ulong id, CacheMode mode, RequestOptions options) => await GetStickerAsync(id, mode, options); + /// async Task> IGuild.GetStickersAsync(CacheMode mode, RequestOptions options) => await GetStickersAsync(mode, options); + /// Task IGuild.DeleteStickerAsync(ICustomSticker sticker, RequestOptions options) => DeleteStickerAsync(_stickers[sticker.Id], options);