| @@ -45,7 +45,6 @@ | |||||
| public const string Voice = "voice"; | public const string Voice = "voice"; | ||||
| public const string VoiceRegions = "voice/regions"; | public const string VoiceRegions = "voice/regions"; | ||||
| //public const string VoiceIce = "voice/ice"; | |||||
| public const string StatusActiveMaintenance = "scheduled-maintenances/active.json"; | public const string StatusActiveMaintenance = "scheduled-maintenances/active.json"; | ||||
| public const string StatusUpcomingMaintenance = "scheduled-maintenances/upcoming.json"; | public const string StatusUpcomingMaintenance = "scheduled-maintenances/upcoming.json"; | ||||
| @@ -2,6 +2,7 @@ | |||||
| using Discord.Net.Rest; | using Discord.Net.Rest; | ||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -198,12 +199,13 @@ namespace Discord | |||||
| var request = new SendMessageRequest { Content = message, Mentions = mentionedUserIds ?? new long[0], Nonce = nonce, IsTTS = isTTS ? true : false }; | var request = new SendMessageRequest { Content = message, Mentions = mentionedUserIds ?? new long[0], Nonce = nonce, IsTTS = isTTS ? true : false }; | ||||
| return _rest.Post<SendMessageResponse>(Endpoints.ChannelMessages(channelId), request); | return _rest.Post<SendMessageResponse>(Endpoints.ChannelMessages(channelId), request); | ||||
| } | } | ||||
| public Task<SendMessageResponse> SendFile(long channelId, string filePath) | |||||
| public Task<SendMessageResponse> SendFile(long channelId, string filename, Stream stream) | |||||
| { | { | ||||
| if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); | if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); | ||||
| if (filePath == null) throw new ArgumentNullException(nameof(filePath)); | |||||
| if (filename == null) throw new ArgumentNullException(nameof(filename)); | |||||
| if (stream == null) throw new ArgumentNullException(nameof(stream)); | |||||
| return _rest.PostFile<SendMessageResponse>(Endpoints.ChannelMessages(channelId), filePath); | |||||
| return _rest.PostFile<SendMessageResponse>(Endpoints.ChannelMessages(channelId), filename, stream); | |||||
| } | } | ||||
| public Task DeleteMessage(long messageId, long channelId) | public Task DeleteMessage(long messageId, long channelId) | ||||
| { | { | ||||
| @@ -301,38 +303,39 @@ namespace Discord | |||||
| return _rest.Delete<DeleteServerResponse>(Endpoints.Server(serverId)); | return _rest.Delete<DeleteServerResponse>(Endpoints.Server(serverId)); | ||||
| } | } | ||||
| public Task<EditServerResponse> EditServer(long serverId, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||||
| public Task<EditServerResponse> EditServer(long serverId, string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png) | |||||
| { | { | ||||
| if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); | if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); | ||||
| var request = new EditServerRequest { Name = name, Region = region, Icon = Base64Picture(iconType, icon) }; | |||||
| var request = new EditServerRequest { Name = name, Region = region, Icon = Base64Picture(icon, iconType) }; | |||||
| return _rest.Patch<EditServerResponse>(Endpoints.Server(serverId), request); | return _rest.Patch<EditServerResponse>(Endpoints.Server(serverId), request); | ||||
| } | } | ||||
| //User | //User | ||||
| public Task<EditUserResponse> EditUser(string currentPassword = "", | |||||
| public Task<EditUserResponse> EditProfile(string currentPassword = "", | |||||
| string username = null, string email = null, string password = null, | string username = null, string email = null, string password = null, | ||||
| ImageType avatarType = ImageType.Png, byte[] avatar = null) | |||||
| Stream avatar = null, ImageType avatarType = ImageType.Png) | |||||
| { | { | ||||
| if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); | if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); | ||||
| var request = new EditUserRequest { CurrentPassword = currentPassword, Username = username, Email = email, Password = password, Avatar = Base64Picture(avatarType, avatar) }; | |||||
| var request = new EditUserRequest { CurrentPassword = currentPassword, Username = username, Email = email, Password = password, Avatar = Base64Picture(avatar, avatarType) }; | |||||
| return _rest.Patch<EditUserResponse>(Endpoints.UserMe, request); | return _rest.Patch<EditUserResponse>(Endpoints.UserMe, request); | ||||
| } | } | ||||
| //Voice | //Voice | ||||
| public Task<GetRegionsResponse> GetVoiceRegions() | public Task<GetRegionsResponse> GetVoiceRegions() | ||||
| => _rest.Get<GetRegionsResponse>(Endpoints.VoiceRegions); | => _rest.Get<GetRegionsResponse>(Endpoints.VoiceRegions); | ||||
| /*public Task<GetIceResponse> GetVoiceIce() | |||||
| => _rest.Get<GetIceResponse>(Endpoints.VoiceIce);*/ | |||||
| private string Base64Picture(ImageType type, byte[] data) | |||||
| private string Base64Picture(Stream stream, ImageType type) | |||||
| { | { | ||||
| if (type == ImageType.None) | if (type == ImageType.None) | ||||
| return ""; | return ""; | ||||
| else if (data != null) | |||||
| else if (stream != null) | |||||
| { | { | ||||
| string base64 = Convert.ToBase64String(data); | |||||
| byte[] bytes = new byte[stream.Length - stream.Position]; | |||||
| stream.Read(bytes, 0, bytes.Length); | |||||
| string base64 = Convert.ToBase64String(bytes); | |||||
| string imageType = type == ImageType.Jpeg ? "image/jpeg;base64" : "image/png;base64"; | string imageType = type == ImageType.Jpeg ? "image/jpeg;base64" : "image/png;base64"; | ||||
| return $"data:{imageType},{base64}"; | return $"data:{imageType},{base64}"; | ||||
| } | } | ||||
| @@ -170,8 +170,18 @@ namespace Discord | |||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| if (filePath == null) throw new ArgumentNullException(nameof(filePath)); | if (filePath == null) throw new ArgumentNullException(nameof(filePath)); | ||||
| CheckReady(); | CheckReady(); | ||||
| return _api.SendFile(channel.Id, Path.GetFileName(filePath), File.OpenRead(filePath)); | |||||
| } | |||||
| /// <summary> Sends a file to the provided channel. </summary> | |||||
| public Task SendFile(Channel channel, string filename, Stream stream) | |||||
| { | |||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||||
| if (filename == null) throw new ArgumentNullException(nameof(filename)); | |||||
| if (stream == null) throw new ArgumentNullException(nameof(stream)); | |||||
| CheckReady(); | |||||
| return _api.SendFile(channel.Id, filePath); | |||||
| return _api.SendFile(channel.Id, filename, stream); | |||||
| } | } | ||||
| /// <summary> Edits the provided message, changing only non-null attributes. </summary> | /// <summary> Edits the provided message, changing only non-null attributes. </summary> | ||||
| @@ -1,5 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Net; | using System.Net; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -93,12 +94,12 @@ namespace Discord | |||||
| } | } | ||||
| /// <summary> Edits the provided server, changing only non-null attributes. </summary> | /// <summary> Edits the provided server, changing only non-null attributes. </summary> | ||||
| public async Task EditServer(Server server, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||||
| public async Task EditServer(Server server, string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png) | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region, iconType: iconType, icon: icon).ConfigureAwait(false); | |||||
| var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region, icon: icon, iconType: iconType).ConfigureAwait(false); | |||||
| server.Update(response); | server.Update(response); | ||||
| } | } | ||||
| @@ -1,5 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Net; | using System.Net; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -282,14 +283,14 @@ namespace Discord | |||||
| public Task EditProfile(string currentPassword = "", | public Task EditProfile(string currentPassword = "", | ||||
| string username = null, string email = null, string password = null, | string username = null, string email = null, string password = null, | ||||
| ImageType avatarType = ImageType.Png, byte[] avatar = null) | |||||
| Stream avatar = null, ImageType avatarType = ImageType.Png) | |||||
| { | { | ||||
| if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); | if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); | ||||
| CheckReady(); | CheckReady(); | ||||
| return _api.EditUser(currentPassword: currentPassword, | |||||
| return _api.EditProfile(currentPassword: currentPassword, | |||||
| username: username ?? _privateUser?.Name, email: email ?? _privateUser?.Global.Email, password: password, | username: username ?? _privateUser?.Name, email: email ?? _privateUser?.Global.Email, password: password, | ||||
| avatarType: avatarType, avatar: avatar); | |||||
| avatar: avatar, avatarType: avatarType); | |||||
| } | } | ||||
| public Task SetStatus(UserStatus status) | public Task SetStatus(UserStatus status) | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System.Threading; | |||||
| using System.IO; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Net.Rest | namespace Discord.Net.Rest | ||||
| @@ -7,6 +8,6 @@ namespace Discord.Net.Rest | |||||
| { | { | ||||
| void SetToken(string token); | void SetToken(string token); | ||||
| Task<string> Send(string method, string path, string json, CancellationToken cancelToken); | Task<string> Send(string method, string path, string json, CancellationToken cancelToken); | ||||
| Task<string> SendFile(string method, string path, string filePath, CancellationToken cancelToken); | |||||
| Task<string> SendFile(string method, string path, string filename, Stream stream, CancellationToken cancelToken); | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.IO; | |||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -65,15 +66,15 @@ namespace Discord.Net.Rest | |||||
| internal Task Put(string path) | internal Task Put(string path) | ||||
| => Send("PUT", path); | => Send("PUT", path); | ||||
| internal Task<ResponseT> PostFile<ResponseT>(string path, string filePath) where ResponseT : class | |||||
| => SendFile<ResponseT>("POST", path, filePath); | |||||
| internal Task PostFile(string path, string filePath) | |||||
| => SendFile("POST", path, filePath); | |||||
| internal Task<ResponseT> PostFile<ResponseT>(string path, string filename, Stream stream) where ResponseT : class | |||||
| => SendFile<ResponseT>("POST", path, filename, stream); | |||||
| internal Task PostFile(string path, string filename, Stream stream) | |||||
| => SendFile("POST", path, filename, stream); | |||||
| internal Task<ResponseT> PutFile<ResponseT>(string path, string filePath) where ResponseT : class | |||||
| => SendFile<ResponseT>("PUT", path, filePath); | |||||
| internal Task PutFile(string path, string filePath) | |||||
| => SendFile("PUT", path, filePath); | |||||
| internal Task<ResponseT> PutFile<ResponseT>(string path, string filename, Stream stream) where ResponseT : class | |||||
| => SendFile<ResponseT>("PUT", path, filename, stream); | |||||
| internal Task PutFile(string path, string filename, Stream stream) | |||||
| => SendFile("PUT", path, filename, stream); | |||||
| private async Task<ResponseT> Send<ResponseT>(string method, string path, object content = null) | private async Task<ResponseT> Send<ResponseT>(string method, string path, object content = null) | ||||
| where ResponseT : class | where ResponseT : class | ||||
| @@ -117,22 +118,22 @@ namespace Discord.Net.Rest | |||||
| return responseJson; | return responseJson; | ||||
| } | } | ||||
| private async Task<ResponseT> SendFile<ResponseT>(string method, string path, string filePath) | |||||
| private async Task<ResponseT> SendFile<ResponseT>(string method, string path, string filename, Stream stream) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| { | { | ||||
| string responseJson = await SendFile(method, path, filePath, true).ConfigureAwait(false); | |||||
| string responseJson = await SendFile(method, path, filename, stream, true).ConfigureAwait(false); | |||||
| return DeserializeResponse<ResponseT>(responseJson); | return DeserializeResponse<ResponseT>(responseJson); | ||||
| } | } | ||||
| private Task SendFile(string method, string path, string filePath) | |||||
| => SendFile(method, path, filePath, false); | |||||
| private async Task<string> SendFile(string method, string path, string filePath, bool hasResponse) | |||||
| private Task SendFile(string method, string path, string filename, Stream stream) | |||||
| => SendFile(method, path, filename, stream, false); | |||||
| private async Task<string> SendFile(string method, string path, string filename, Stream stream, bool hasResponse) | |||||
| { | { | ||||
| Stopwatch stopwatch = null; | Stopwatch stopwatch = null; | ||||
| if (_config.LogLevel >= LogMessageSeverity.Verbose) | if (_config.LogLevel >= LogMessageSeverity.Verbose) | ||||
| stopwatch = Stopwatch.StartNew(); | stopwatch = Stopwatch.StartNew(); | ||||
| string responseJson = await _engine.SendFile(method, path, filePath, _cancelToken).ConfigureAwait(false); | |||||
| string responseJson = await _engine.SendFile(method, path, filename, stream, _cancelToken).ConfigureAwait(false); | |||||
| #if TEST_RESPONSES | #if TEST_RESPONSES | ||||
| if (!hasResponse && !string.IsNullOrEmpty(responseJson)) | if (!hasResponse && !string.IsNullOrEmpty(responseJson)) | ||||
| @@ -143,7 +144,7 @@ namespace Discord.Net.Rest | |||||
| { | { | ||||
| stopwatch.Stop(); | stopwatch.Stop(); | ||||
| if (_config.LogLevel >= LogMessageSeverity.Debug) | if (_config.LogLevel >= LogMessageSeverity.Debug) | ||||
| RaiseOnRequest(method, path, filePath, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); | |||||
| RaiseOnRequest(method, path, filename, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); | |||||
| else | else | ||||
| RaiseOnRequest(method, path, null, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); | RaiseOnRequest(method, path, null, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| using Discord.API; | using Discord.API; | ||||
| using RestSharp; | using RestSharp; | ||||
| using System; | using System; | ||||
| using System.IO; | |||||
| using System.Net; | using System.Net; | ||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -44,10 +45,10 @@ namespace Discord.Net.Rest | |||||
| request.AddParameter("application/json", json, ParameterType.RequestBody); | request.AddParameter("application/json", json, ParameterType.RequestBody); | ||||
| return Send(request, cancelToken); | return Send(request, cancelToken); | ||||
| } | } | ||||
| public Task<string> SendFile(string method, string path, string filePath, CancellationToken cancelToken) | |||||
| public Task<string> SendFile(string method, string path, string filename, Stream stream, CancellationToken cancelToken) | |||||
| { | { | ||||
| var request = new RestRequest(path, Method.POST); | var request = new RestRequest(path, Method.POST); | ||||
| request.AddFile("file", filePath); | |||||
| request.AddFile("file", x => stream.CopyTo(x), filename); | |||||
| return Send(request, cancelToken); | return Send(request, cancelToken); | ||||
| } | } | ||||
| private async Task<string> Send(RestRequest request, CancellationToken cancelToken) | private async Task<string> Send(RestRequest request, CancellationToken cancelToken) | ||||
| @@ -45,6 +45,7 @@ | |||||
| "System.Collections": "4.0.11-beta-23516", | "System.Collections": "4.0.11-beta-23516", | ||||
| "System.Collections.Concurrent": "4.0.11-beta-23516", | "System.Collections.Concurrent": "4.0.11-beta-23516", | ||||
| "System.Dynamic.Runtime": "4.0.11-beta-23516", | "System.Dynamic.Runtime": "4.0.11-beta-23516", | ||||
| "System.IO.FileSystem": "4.0.1-beta-23516", | |||||
| "System.IO.Compression": "4.1.0-beta-23516", | "System.IO.Compression": "4.1.0-beta-23516", | ||||
| "System.Linq": "4.0.1-beta-23516", | "System.Linq": "4.0.1-beta-23516", | ||||
| "System.Net.NameResolution": "4.0.0-beta-23516", | "System.Net.NameResolution": "4.0.0-beta-23516", | ||||