@@ -1,28 +1,50 @@ | |||||
| | ||||
Microsoft Visual Studio Solution File, Format Version 12.00 | Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
# Visual Studio 14 | # Visual Studio 14 | ||||
VisualStudioVersion = 14.0.25123.0 | |||||
VisualStudioVersion = 14.0.25420.1 | |||||
MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | |||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7F3E124-93C7-4846-AE87-9CE12BD82859}" | |||||
ProjectSection(SolutionItems) = preProject | |||||
global.json = global.json | |||||
README.md = README.md | |||||
EndProjectSection | |||||
EndProject | EndProject | ||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" | |||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}" | |||||
EndProject | |||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | |||||
EndProject | |||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Impls", "Impls", "{288C363D-A636-4EAE-9AC1-4698B641B26E}" | |||||
EndProject | |||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Discord.Net.Utils", "src\Discord.Net.Utils\Discord.Net.Utils.shproj", "{2B75119C-9893-4AAA-8D38-6176EEB09060}" | |||||
EndProject | |||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Rest", "src\Discord.Net.Rest\Discord.Net.Rest.xproj", "{BFC6DC28-0351-4573-926A-D4124244C04F}" | |||||
EndProject | EndProject | ||||
Global | Global | ||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution | |||||
src\Discord.Net.Utils\Discord.Net.Utils.projitems*{2b75119c-9893-4aaa-8d38-6176eeb09060}*SharedItemsImports = 13 | |||||
EndGlobalSection | |||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
Release|Any CPU = Release|Any CPU | Release|Any CPU = Release|Any CPU | ||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(NestedProjects) = preSolution | |||||
{BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | |||||
EndGlobalSection | |||||
EndGlobal | EndGlobal |
@@ -1,6 +1,6 @@ | |||||
namespace Discord.API | namespace Discord.API | ||||
{ | { | ||||
internal static class CDN | |||||
public static class CDN | |||||
{ | { | ||||
public static string GetApplicationIconUrl(ulong appId, string iconId) | public static string GetApplicationIconUrl(ulong appId, string iconId) | ||||
=> iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; | => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; |
@@ -4,7 +4,6 @@ using Discord.Net; | |||||
using Discord.Net.Converters; | using Discord.Net.Converters; | ||||
using Discord.Net.Queue; | using Discord.Net.Queue; | ||||
using Discord.Net.Rest; | using Discord.Net.Rest; | ||||
using Discord.Rest; | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -28,6 +27,7 @@ namespace Discord.API | |||||
protected readonly JsonSerializer _serializer; | protected readonly JsonSerializer _serializer; | ||||
protected readonly SemaphoreSlim _stateLock; | protected readonly SemaphoreSlim _stateLock; | ||||
private readonly RestClientProvider _restClientProvider; | private readonly RestClientProvider _restClientProvider; | ||||
private readonly string _userAgent; | |||||
protected string _authToken; | protected string _authToken; | ||||
protected bool _isDisposed; | protected bool _isDisposed; | ||||
@@ -36,11 +36,13 @@ namespace Discord.API | |||||
public LoginState LoginState { get; private set; } | public LoginState LoginState { get; private set; } | ||||
public TokenType AuthTokenType { get; private set; } | public TokenType AuthTokenType { get; private set; } | ||||
internal RequestQueue RequestQueue { get; private set; } | |||||
public User CurrentUser { get; private set; } | |||||
public RequestQueue RequestQueue { get; private set; } | |||||
public DiscordRestApiClient(RestClientProvider restClientProvider, JsonSerializer serializer = null, RequestQueue requestQueue = null) | |||||
public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, JsonSerializer serializer = null, RequestQueue requestQueue = null) | |||||
{ | { | ||||
_restClientProvider = restClientProvider; | _restClientProvider = restClientProvider; | ||||
_userAgent = userAgent; | |||||
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; | _serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; | ||||
RequestQueue = requestQueue; | RequestQueue = requestQueue; | ||||
@@ -52,7 +54,7 @@ namespace Discord.API | |||||
{ | { | ||||
_restClient = _restClientProvider(baseUrl); | _restClient = _restClientProvider(baseUrl); | ||||
_restClient.SetHeader("accept", "*/*"); | _restClient.SetHeader("accept", "*/*"); | ||||
_restClient.SetHeader("user-agent", DiscordRestConfig.UserAgent); | |||||
_restClient.SetHeader("user-agent", _userAgent); | |||||
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); | _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); | ||||
} | } | ||||
internal static string GetPrefixedToken(TokenType tokenType, string token) | internal static string GetPrefixedToken(TokenType tokenType, string token) | ||||
@@ -111,6 +113,8 @@ namespace Discord.API | |||||
_authToken = token; | _authToken = token; | ||||
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); | _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); | ||||
CurrentUser = await GetMyUserAsync(); | |||||
LoginState = LoginState.LoggedIn; | LoginState = LoginState.LoggedIn; | ||||
} | } | ||||
catch (Exception) | catch (Exception) | ||||
@@ -144,6 +148,7 @@ namespace Discord.API | |||||
await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false); | await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false); | ||||
_restClient.SetCancelToken(CancellationToken.None); | _restClient.SetCancelToken(CancellationToken.None); | ||||
CurrentUser = null; | |||||
LoginState = LoginState.LoggedOut; | LoginState = LoginState.LoggedOut; | ||||
} | } | ||||
@@ -268,8 +273,8 @@ namespace Discord.API | |||||
{ | { | ||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.GreaterThan(args._bitrate, 0, nameof(args.Bitrate)); | |||||
Preconditions.NotNullOrWhitespace(args._name, nameof(args.Name)); | |||||
Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); | |||||
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | |||||
return await SendAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -283,8 +288,8 @@ namespace Discord.API | |||||
{ | { | ||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -292,8 +297,8 @@ namespace Discord.API | |||||
{ | { | ||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -301,10 +306,10 @@ namespace Discord.API | |||||
{ | { | ||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.GreaterThan(args._bitrate, 0, nameof(args.Bitrate)); | |||||
Preconditions.AtLeast(args._userLimit, 0, nameof(args.Bitrate)); | |||||
Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); | |||||
Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); | |||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -326,6 +331,132 @@ namespace Discord.API | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
//Channel Messages | |||||
public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
try | |||||
{ | |||||
return await SendAsync<Message>("GET", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); | |||||
} | |||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } | |||||
} | |||||
public async Task<IReadOnlyCollection<Message>> GetChannelMessagesAsync(ulong channelId, GetChannelMessagesParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); | |||||
Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); | |||||
int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch); | |||||
ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null; | |||||
string relativeDir; | |||||
switch (args.RelativeDirection.GetValueOrDefault(Direction.Before)) | |||||
{ | |||||
case Direction.Before: | |||||
default: | |||||
relativeDir = "before"; | |||||
break; | |||||
case Direction.After: | |||||
relativeDir = "after"; | |||||
break; | |||||
case Direction.Around: | |||||
relativeDir = "around"; | |||||
break; | |||||
} | |||||
string endpoint; | |||||
if (relativeId != null) | |||||
endpoint = $"channels/{channelId}/messages?limit={limit}&{relativeDir}={relativeId}"; | |||||
else | |||||
endpoint = $"channels/{channelId}/messages?limit={limit}"; | |||||
return await SendAsync<IReadOnlyCollection<Message>>("GET", endpoint, options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task<Message> CreateMessageAsync(ulong channelId, CreateMessageParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | |||||
if (args.Content.Length > DiscordConfig.MaxMessageSize) | |||||
throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
if (args.Content.GetValueOrDefault(null) == null) | |||||
args.Content = ""; | |||||
else if (args.Content.IsSpecified) | |||||
{ | |||||
if (args.Content.Value == null) | |||||
args.Content = ""; | |||||
if (args.Content.Value?.Length > DiscordConfig.MaxMessageSize) | |||||
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
} | |||||
return await SendMultipartAsync<Message>("POST", $"channels/{channelId}/messages", args.ToDictionary(), GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task DeleteMessagesAsync(ulong channelId, DeleteMessagesParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); | |||||
Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); | |||||
switch (args.MessageIds.Length) | |||||
{ | |||||
case 0: | |||||
return; | |||||
case 1: | |||||
await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false); | |||||
break; | |||||
default: | |||||
await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); | |||||
break; | |||||
} | |||||
} | |||||
public async Task<Message> ModifyMessageAsync(ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
if (args.Content.IsSpecified) | |||||
{ | |||||
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | |||||
if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) | |||||
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
} | |||||
return await SendAsync<Message>("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); | |||||
} | |||||
//Channel Permissions | //Channel Permissions | ||||
public async Task ModifyChannelPermissionsAsync(ulong channelId, ulong targetId, ModifyChannelPermissionsParams args, RequestOptions options = null) | public async Task ModifyChannelPermissionsAsync(ulong channelId, ulong targetId, ModifyChannelPermissionsParams args, RequestOptions options = null) | ||||
@@ -399,7 +530,7 @@ namespace Discord.API | |||||
{ | { | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | ||||
Preconditions.NotNullOrWhitespace(args.Region, nameof(args.Region)); | |||||
Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); | |||||
return await SendAsync<Guild>("POST", "guilds", args, options: options).ConfigureAwait(false); | return await SendAsync<Guild>("POST", "guilds", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -419,11 +550,11 @@ namespace Discord.API | |||||
{ | { | ||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.NotEqual(args._afkChannelId, 0, nameof(args.AFKChannelId)); | |||||
Preconditions.AtLeast(args._afkTimeout, 0, nameof(args.AFKTimeout)); | |||||
Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
Preconditions.GreaterThan(args._ownerId, 0, nameof(args.OwnerId)); | |||||
Preconditions.NotNull(args._region, nameof(args.Region)); | |||||
Preconditions.NotEqual(args.AfkChannelId, 0, nameof(args.AfkChannelId)); | |||||
Preconditions.AtLeast(args.AfkTimeout, 0, nameof(args.AfkTimeout)); | |||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId)); | |||||
Preconditions.NotNull(args.RegionId, nameof(args.RegionId)); | |||||
return await SendAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -456,7 +587,7 @@ namespace Discord.API | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.AtLeast(args._deleteMessageDays, 0, nameof(args.DeleteMessageDays)); | |||||
Preconditions.AtLeast(args.DeleteMessageDays, 0, nameof(args.DeleteMessageDays)); | |||||
await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); | await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -514,8 +645,8 @@ namespace Discord.API | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.AtLeast(args._expireBehavior, 0, nameof(args.ExpireBehavior)); | |||||
Preconditions.AtLeast(args._expireGracePeriod, 0, nameof(args.ExpireGracePeriod)); | |||||
Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior)); | |||||
Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod)); | |||||
return await SendAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -552,18 +683,18 @@ namespace Discord.API | |||||
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false); | ||||
} | } | ||||
public async Task<InviteMetadata[]> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) | |||||
public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) | |||||
{ | { | ||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
return await SendAsync<InviteMetadata[]>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); | |||||
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); | |||||
} | } | ||||
public async Task<InviteMetadata> CreateChannelInviteAsync(ulong channelId, CreateChannelInviteParams args, RequestOptions options = null) | public async Task<InviteMetadata> CreateChannelInviteAsync(ulong channelId, CreateChannelInviteParams args, RequestOptions options = null) | ||||
{ | { | ||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.AtLeast(args._maxAge, 0, nameof(args.MaxAge)); | |||||
Preconditions.AtLeast(args._maxUses, 0, nameof(args.MaxUses)); | |||||
Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge)); | |||||
Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses)); | |||||
return await SendAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); | return await SendAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -596,42 +727,15 @@ namespace Discord.API | |||||
{ | { | ||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.GreaterThan(args._limit, 0, nameof(args.Limit)); | |||||
Preconditions.GreaterThan(args._afterUserId, 0, nameof(args.AfterUserId)); | |||||
int limit = args._limit.GetValueOrDefault(int.MaxValue); | |||||
ulong afterUserId = args._afterUserId.GetValueOrDefault(0); | |||||
Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); | |||||
Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); | |||||
Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); | |||||
List<GuildMember[]> result; | |||||
if (args._limit.IsSpecified) | |||||
result = new List<GuildMember[]>((limit + DiscordConfig.MaxUsersPerBatch - 1) / DiscordConfig.MaxUsersPerBatch); | |||||
else | |||||
result = new List<GuildMember[]>(); | |||||
while (true) | |||||
{ | |||||
int runLimit = (limit >= DiscordConfig.MaxUsersPerBatch) ? DiscordConfig.MaxUsersPerBatch : limit; | |||||
string endpoint = $"guilds/{guildId}/members?limit={runLimit}&after={afterUserId}"; | |||||
var models = await SendAsync<GuildMember[]>("GET", endpoint, options: options).ConfigureAwait(false); | |||||
//Was this an empty batch? | |||||
if (models.Length == 0) break; | |||||
result.Add(models); | |||||
limit -= DiscordConfig.MaxUsersPerBatch; | |||||
afterUserId = models[models.Length - 1].User.Id; | |||||
//Was this an incomplete (the last) batch? | |||||
if (models.Length != DiscordConfig.MaxUsersPerBatch) break; | |||||
} | |||||
if (result.Count > 1) | |||||
return result.SelectMany(x => x).ToImmutableArray(); | |||||
else if (result.Count == 1) | |||||
return result[0]; | |||||
else | |||||
return ImmutableArray.Create<GuildMember>(); | |||||
int limit = args.Limit.GetValueOrDefault(int.MaxValue); | |||||
ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); | |||||
string endpoint = $"guilds/{guildId}/members?limit={limit}&after={afterUserId}"; | |||||
return await SendAsync<IReadOnlyCollection<GuildMember>>("GET", endpoint, options: options).ConfigureAwait(false); | |||||
} | } | ||||
public async Task RemoveGuildMemberAsync(ulong guildId, ulong userId, RequestOptions options = null) | public async Task RemoveGuildMemberAsync(ulong guildId, ulong userId, RequestOptions options = null) | ||||
{ | { | ||||
@@ -646,7 +750,18 @@ namespace Discord.API | |||||
Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
await SendAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, GuildBucket.ModifyMember, guildId, options: options).ConfigureAwait(false); | |||||
bool isCurrentUser = userId == CurrentUser.Id; | |||||
if (isCurrentUser && args.Nickname.IsSpecified) | |||||
{ | |||||
var nickArgs = new ModifyCurrentUserNickParams(args.Nickname.Value ?? ""); | |||||
await ModifyMyNickAsync(guildId, nickArgs).ConfigureAwait(false); | |||||
args.Nickname = Optional.Create<string>(); //Remove | |||||
} | |||||
if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) | |||||
{ | |||||
await SendAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, GuildBucket.ModifyMember, guildId, options: options).ConfigureAwait(false); | |||||
} | |||||
} | } | ||||
//Guild Roles | //Guild Roles | ||||
@@ -674,9 +789,9 @@ namespace Discord.API | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
Preconditions.NotEqual(roleId, 0, nameof(roleId)); | Preconditions.NotEqual(roleId, 0, nameof(roleId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.AtLeast(args._color, 0, nameof(args.Color)); | |||||
Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
Preconditions.AtLeast(args.Color, 0, nameof(args.Color)); | |||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
return await SendAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -697,266 +812,6 @@ namespace Discord.API | |||||
} | } | ||||
} | } | ||||
//Messages | |||||
public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
try | |||||
{ | |||||
return await SendAsync<Message>("GET", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); | |||||
} | |||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } | |||||
} | |||||
public async Task<IReadOnlyCollection<Message>> GetChannelMessagesAsync(ulong channelId, GetChannelMessagesParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); | |||||
int limit = args.Limit; | |||||
ulong? relativeId = args._relativeMessageId.IsSpecified ? args._relativeMessageId.Value : (ulong?)null; | |||||
string relativeDir; | |||||
switch (args.RelativeDirection) | |||||
{ | |||||
case Direction.Before: | |||||
default: | |||||
relativeDir = "before"; | |||||
break; | |||||
case Direction.After: | |||||
relativeDir = "after"; | |||||
break; | |||||
case Direction.Around: | |||||
relativeDir = "around"; | |||||
break; | |||||
} | |||||
int runs = (limit + DiscordConfig.MaxMessagesPerBatch - 1) / DiscordConfig.MaxMessagesPerBatch; | |||||
int lastRunCount = limit - (runs - 1) * DiscordConfig.MaxMessagesPerBatch; | |||||
var result = new API.Message[runs][]; | |||||
int i = 0; | |||||
for (; i < runs; i++) | |||||
{ | |||||
int runCount = i == (runs - 1) ? lastRunCount : DiscordConfig.MaxMessagesPerBatch; | |||||
string endpoint; | |||||
if (relativeId != null) | |||||
endpoint = $"channels/{channelId}/messages?limit={runCount}&{relativeDir}={relativeId}"; | |||||
else | |||||
endpoint = $"channels/{channelId}/messages?limit={runCount}"; | |||||
var models = await SendAsync<Message[]>("GET", endpoint, options: options).ConfigureAwait(false); | |||||
//Was this an empty batch? | |||||
if (models.Length == 0) break; | |||||
//We can't assume these messages to be sorted by id (fails in rare cases), lets search for the highest/lowest id ourselves | |||||
switch (args.RelativeDirection) | |||||
{ | |||||
case Direction.Before: | |||||
case Direction.Around: | |||||
default: | |||||
result[i] = models; | |||||
relativeId = ulong.MaxValue; | |||||
//Lowest id *should* be the last one | |||||
for (int j = models.Length - 1; j >= 0; j--) | |||||
{ | |||||
if (models[j].Id < relativeId.Value) | |||||
relativeId = models[j].Id; | |||||
} | |||||
break; | |||||
case Direction.After: | |||||
result[runs - i - 1] = models; | |||||
relativeId = ulong.MinValue; | |||||
//Highest id *should* be the first one | |||||
for (int j = 0; j < models.Length; j++) | |||||
{ | |||||
if (models[j].Id > relativeId.Value) | |||||
relativeId = models[j].Id; | |||||
} | |||||
break; | |||||
} | |||||
//Was this an incomplete (the last) batch? | |||||
if (models.Length != DiscordConfig.MaxMessagesPerBatch) { i++; break; } | |||||
} | |||||
if (i > 1) | |||||
{ | |||||
switch (args.RelativeDirection) | |||||
{ | |||||
case Direction.Before: | |||||
case Direction.Around: | |||||
default: | |||||
return result.Take(i).SelectMany(x => x).ToImmutableArray(); | |||||
case Direction.After: | |||||
return result.Skip(runs - i).Take(i).SelectMany(x => x).ToImmutableArray(); | |||||
} | |||||
} | |||||
else if (i == 1) | |||||
{ | |||||
switch (args.RelativeDirection) | |||||
{ | |||||
case Direction.Before: | |||||
case Direction.Around: | |||||
default: | |||||
return result[0]; | |||||
case Direction.After: | |||||
return result[runs - 1]; | |||||
} | |||||
} | |||||
else | |||||
return ImmutableArray.Create<Message>(); | |||||
} | |||||
public Task<Message> CreateMessageAsync(ulong guildId, ulong channelId, CreateMessageParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
return CreateMessageInternalAsync(guildId, channelId, args); | |||||
} | |||||
public Task<Message> CreateDMMessageAsync(ulong channelId, CreateMessageParams args, RequestOptions options = null) | |||||
{ | |||||
return CreateMessageInternalAsync(0, channelId, args); | |||||
} | |||||
private async Task<Message> CreateMessageInternalAsync(ulong guildId, ulong channelId, CreateMessageParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
Preconditions.NotNullOrEmpty(args._content, nameof(args.Content)); | |||||
if (args._content.Length > DiscordConfig.MaxMessageSize) | |||||
throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
if (guildId != 0) | |||||
return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); | |||||
else | |||||
return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); | |||||
} | |||||
public Task<Message> UploadFileAsync(ulong guildId, ulong channelId, UploadFileParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
return UploadFileInternalAsync(guildId, channelId, args); | |||||
} | |||||
public Task<Message> UploadDMFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | |||||
{ | |||||
return UploadFileInternalAsync(0, channelId, args); | |||||
} | |||||
private async Task<Message> UploadFileInternalAsync(ulong guildId, ulong channelId, UploadFileParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
if (args._content.GetValueOrDefault(null) == null) | |||||
args._content = ""; | |||||
else if (args._content.IsSpecified) | |||||
{ | |||||
if (args._content.Value == null) | |||||
args._content = ""; | |||||
if (args._content.Value?.Length > DiscordConfig.MaxMessageSize) | |||||
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
} | |||||
if (guildId != 0) | |||||
return await SendMultipartAsync<Message>("POST", $"channels/{channelId}/messages", args.ToDictionary(), GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); | |||||
else | |||||
return await SendMultipartAsync<Message>("POST", $"channels/{channelId}/messages", args.ToDictionary(), GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); | |||||
} | |||||
public Task DeleteMessageAsync(ulong guildId, ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
return DeleteMessageInternalAsync(guildId, channelId, messageId); | |||||
} | |||||
public Task DeleteDMMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
return DeleteMessageInternalAsync(0, channelId, messageId); | |||||
} | |||||
private async Task DeleteMessageInternalAsync(ulong guildId, ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
if (guildId != 0) | |||||
await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", GuildBucket.DeleteMessage, guildId, options: options).ConfigureAwait(false); | |||||
else | |||||
await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); | |||||
} | |||||
public Task DeleteMessagesAsync(ulong guildId, ulong channelId, DeleteMessagesParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
return DeleteMessagesInternalAsync(guildId, channelId, args); | |||||
} | |||||
public Task DeleteDMMessagesAsync(ulong channelId, DeleteMessagesParams args, RequestOptions options = null) | |||||
{ | |||||
return DeleteMessagesInternalAsync(0, channelId, args); | |||||
} | |||||
private async Task DeleteMessagesInternalAsync(ulong guildId, ulong channelId, DeleteMessagesParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
var messageIds = args._messages; | |||||
Preconditions.NotNull(args._messages, nameof(args.MessageIds)); | |||||
Preconditions.AtMost(messageIds.Length, 100, nameof(messageIds.Length)); | |||||
switch (messageIds.Length) | |||||
{ | |||||
case 0: | |||||
return; | |||||
case 1: | |||||
await DeleteMessageInternalAsync(guildId, channelId, messageIds[0]).ConfigureAwait(false); | |||||
break; | |||||
default: | |||||
if (guildId != 0) | |||||
await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, GuildBucket.DeleteMessages, guildId, options: options).ConfigureAwait(false); | |||||
else | |||||
await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); | |||||
break; | |||||
} | |||||
} | |||||
public Task<Message> ModifyMessageAsync(ulong guildId, ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
return ModifyMessageInternalAsync(guildId, channelId, messageId, args); | |||||
} | |||||
public Task<Message> ModifyDMMessageAsync(ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) | |||||
{ | |||||
return ModifyMessageInternalAsync(0, channelId, messageId, args); | |||||
} | |||||
private async Task<Message> ModifyMessageInternalAsync(ulong guildId, ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
Preconditions.NotNull(args, nameof(args)); | |||||
if (args._content.IsSpecified) | |||||
{ | |||||
Preconditions.NotNullOrEmpty(args._content, nameof(args.Content)); | |||||
if (args._content.Value.Length > DiscordConfig.MaxMessageSize) | |||||
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
} | |||||
if (guildId != 0) | |||||
return await SendAsync<Message>("PATCH", $"channels/{channelId}/messages/{messageId}", args, GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); | |||||
else | |||||
return await SendAsync<Message>("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); | |||||
} | |||||
//Users | //Users | ||||
public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) | public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) | ||||
{ | { | ||||
@@ -1012,7 +867,7 @@ namespace Discord.API | |||||
public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) | public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) | ||||
{ | { | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.NotNullOrEmpty(args._username, nameof(args.Username)); | |||||
Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); | |||||
return await SendAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false); | return await SendAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -1026,7 +881,7 @@ namespace Discord.API | |||||
public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) | public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) | ||||
{ | { | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
Preconditions.GreaterThan(args._recipientId, 0, nameof(args.Recipient)); | |||||
Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.RecipientId)); | |||||
return await SendAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); | ||||
} | } |
@@ -2,7 +2,7 @@ | |||||
namespace Discord.API | namespace Discord.API | ||||
{ | { | ||||
internal struct Image | |||||
public struct Image | |||||
{ | { | ||||
public Stream Stream { get; } | public Stream Stream { get; } | ||||
public string Hash { get; } | public string Hash { get; } |
@@ -0,0 +1,16 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class CreateChannelInviteParams | |||||
{ | |||||
[JsonProperty("max_age")] | |||||
public Optional<int> MaxAge { get; set; } | |||||
[JsonProperty("max_uses")] | |||||
public Optional<int> MaxUses { get; set; } | |||||
[JsonProperty("temporary")] | |||||
public Optional<bool> IsTemporary { get; set; } | |||||
} | |||||
} |
@@ -7,8 +7,11 @@ namespace Discord.API.Rest | |||||
public class CreateDMChannelParams | public class CreateDMChannelParams | ||||
{ | { | ||||
[JsonProperty("recipient_id")] | [JsonProperty("recipient_id")] | ||||
internal ulong _recipientId { get; set; } | |||||
public ulong RecipientId { set { _recipientId = value; } } | |||||
public IUser Recipient { set { _recipientId = value.Id; } } | |||||
public ulong RecipientId { get; } | |||||
public CreateDMChannelParams(ulong recipientId) | |||||
{ | |||||
RecipientId = recipientId; | |||||
} | |||||
} | } | ||||
} | } |
@@ -7,7 +7,6 @@ namespace Discord.API.Rest | |||||
public class CreateGuildBanParams | public class CreateGuildBanParams | ||||
{ | { | ||||
[JsonProperty("delete-message-days")] | [JsonProperty("delete-message-days")] | ||||
internal Optional<int> _deleteMessageDays { get; set; } | |||||
public int DeleteMessageDays { set { _deleteMessageDays = value; } } | |||||
public Optional<int> DeleteMessageDays { get; set; } | |||||
} | } | ||||
} | } |
@@ -0,0 +1,23 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class CreateGuildChannelParams | |||||
{ | |||||
[JsonProperty("name")] | |||||
public string Name { get; } | |||||
[JsonProperty("type")] | |||||
public ChannelType Type { get; } | |||||
[JsonProperty("bitrate")] | |||||
public Optional<int> Bitrate { get; set; } | |||||
public CreateGuildChannelParams(string name, ChannelType type) | |||||
{ | |||||
Name = name; | |||||
Type = type; | |||||
} | |||||
} | |||||
} |
@@ -7,9 +7,14 @@ namespace Discord.API.Rest | |||||
public class CreateGuildIntegrationParams | public class CreateGuildIntegrationParams | ||||
{ | { | ||||
[JsonProperty("id")] | [JsonProperty("id")] | ||||
public ulong Id { internal get; set; } | |||||
public ulong Id { get; } | |||||
[JsonProperty("type")] | [JsonProperty("type")] | ||||
public string Type { internal get; set; } | |||||
public string Type { get; } | |||||
public CreateGuildIntegrationParams(ulong id, string type) | |||||
{ | |||||
Id = id; | |||||
Type = type; | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,6 +1,5 @@ | |||||
#pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System.IO; | |||||
namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
{ | { | ||||
@@ -8,13 +7,17 @@ namespace Discord.API.Rest | |||||
public class CreateGuildParams | public class CreateGuildParams | ||||
{ | { | ||||
[JsonProperty("name")] | [JsonProperty("name")] | ||||
public string Name { internal get; set; } | |||||
public string Name { get; } | |||||
[JsonProperty("region")] | [JsonProperty("region")] | ||||
public string Region { internal get; set; } | |||||
public string RegionId { get; } | |||||
[JsonProperty("icon")] | [JsonProperty("icon")] | ||||
internal Optional<Image?> _icon { get; set; } | |||||
public Stream Icon { set { _icon = value != null ? new Image(value) : (Image?)null; } } | |||||
public Optional<Image?> Icon { get; set; } | |||||
public CreateGuildParams(string name, string regionId) | |||||
{ | |||||
Name = name; | |||||
RegionId = regionId; | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,22 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class CreateMessageParams | |||||
{ | |||||
[JsonProperty("content")] | |||||
public string Content { get; } | |||||
[JsonProperty("nonce")] | |||||
public Optional<string> Nonce { get; set; } | |||||
[JsonProperty("tts")] | |||||
public Optional<bool> IsTTS { get; set; } | |||||
public CreateMessageParams(string content) | |||||
{ | |||||
Content = content; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class DeleteMessagesParams | |||||
{ | |||||
[JsonProperty("messages")] | |||||
public ulong[] MessageIds { get; } | |||||
public DeleteMessagesParams(ulong[] messageIds) | |||||
{ | |||||
MessageIds = messageIds; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,10 @@ | |||||
#pragma warning disable CS1591 | |||||
namespace Discord.API.Rest | |||||
{ | |||||
public class GetChannelMessagesParams | |||||
{ | |||||
public Optional<int> Limit { get; set; } | |||||
public Optional<Direction> RelativeDirection { get; set; } | |||||
public Optional<ulong> RelativeMessageId { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
#pragma warning disable CS1591 | |||||
namespace Discord.API.Rest | |||||
{ | |||||
public class GetGuildMembersParams | |||||
{ | |||||
public Optional<int> Limit { get; set; } | |||||
public Optional<ulong> AfterUserId { get; set; } | |||||
} | |||||
} |
@@ -7,6 +7,11 @@ namespace Discord.API.Rest | |||||
public class GuildPruneParams | public class GuildPruneParams | ||||
{ | { | ||||
[JsonProperty("days")] | [JsonProperty("days")] | ||||
public int Days { internal get; set; } | |||||
public int Days { get; } | |||||
public GuildPruneParams(int days) | |||||
{ | |||||
Days = days; | |||||
} | |||||
} | } | ||||
} | } |
@@ -7,10 +7,17 @@ namespace Discord.API.Rest | |||||
public class ModifyChannelPermissionsParams | public class ModifyChannelPermissionsParams | ||||
{ | { | ||||
[JsonProperty("type")] | [JsonProperty("type")] | ||||
public string Type { internal get; set; } | |||||
public string Type { get; } | |||||
[JsonProperty("allow")] | [JsonProperty("allow")] | ||||
public ulong Allow { internal get; set; } | |||||
public ulong Allow { get; } | |||||
[JsonProperty("deny")] | [JsonProperty("deny")] | ||||
public ulong Deny { internal get; set; } | |||||
public ulong Deny { get; } | |||||
public ModifyChannelPermissionsParams(string type, ulong allow, ulong deny) | |||||
{ | |||||
Type = type; | |||||
Allow = allow; | |||||
Deny = deny; | |||||
} | |||||
} | } | ||||
} | } |
@@ -7,6 +7,11 @@ namespace Discord.API.Rest | |||||
public class ModifyCurrentUserNickParams | public class ModifyCurrentUserNickParams | ||||
{ | { | ||||
[JsonProperty("nick")] | [JsonProperty("nick")] | ||||
public string Nickname { internal get; set; } | |||||
public string Nickname { get; } | |||||
public ModifyCurrentUserNickParams(string nickname) | |||||
{ | |||||
Nickname = nickname; | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,6 +1,5 @@ | |||||
#pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System.IO; | |||||
namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
{ | { | ||||
@@ -8,11 +7,8 @@ namespace Discord.API.Rest | |||||
public class ModifyCurrentUserParams | public class ModifyCurrentUserParams | ||||
{ | { | ||||
[JsonProperty("username")] | [JsonProperty("username")] | ||||
internal Optional<string> _username { get; set; } | |||||
public string Username { set { _username = value; } } | |||||
public Optional<string> Username { get; set; } | |||||
[JsonProperty("avatar")] | [JsonProperty("avatar")] | ||||
internal Optional<Image> _avatar { get; set; } | |||||
public Stream Avatar { set { _avatar = new Image(value); } } | |||||
public Optional<Image> Avatar { get; set; } | |||||
} | } | ||||
} | } |
@@ -7,11 +7,8 @@ namespace Discord.API.Rest | |||||
public class ModifyGuildChannelParams | public class ModifyGuildChannelParams | ||||
{ | { | ||||
[JsonProperty("name")] | [JsonProperty("name")] | ||||
internal Optional<string> _name { get; set; } | |||||
public string Name { set { _name = value; } } | |||||
public Optional<string> Name { get; set; } | |||||
[JsonProperty("position")] | [JsonProperty("position")] | ||||
internal Optional<int> _position { get; set; } | |||||
public int Position { set { _position = value; } } | |||||
public Optional<int> Position { get; set; } | |||||
} | } | ||||
} | } |
@@ -7,9 +7,14 @@ namespace Discord.API.Rest | |||||
public class ModifyGuildChannelsParams | public class ModifyGuildChannelsParams | ||||
{ | { | ||||
[JsonProperty("id")] | [JsonProperty("id")] | ||||
public ulong Id { internal get; set; } | |||||
public ulong Id { get; set; } | |||||
[JsonProperty("position")] | [JsonProperty("position")] | ||||
public int Position { internal get; set; } | |||||
public int Position { get; set; } | |||||
public ModifyGuildChannelsParams(ulong id, int position) | |||||
{ | |||||
Id = id; | |||||
Position = position; | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,14 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class ModifyGuildEmbedParams | |||||
{ | |||||
[JsonProperty("enabled")] | |||||
public Optional<bool> Enabled { get; set; } | |||||
[JsonProperty("channel")] | |||||
public Optional<ulong?> ChannelId { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class ModifyGuildIntegrationParams | |||||
{ | |||||
[JsonProperty("expire_behavior")] | |||||
public Optional<int> ExpireBehavior { get; set; } | |||||
[JsonProperty("expire_grace_period")] | |||||
public Optional<int> ExpireGracePeriod { get; set; } | |||||
[JsonProperty("enable_emoticons")] | |||||
public Optional<bool> EnableEmoticons { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class ModifyGuildMemberParams | |||||
{ | |||||
[JsonProperty("mute")] | |||||
public Optional<bool> Mute { get; set; } | |||||
[JsonProperty("deaf")] | |||||
public Optional<bool> Deaf { get; set; } | |||||
[JsonProperty("nick")] | |||||
public Optional<string> Nickname { get; set; } | |||||
[JsonProperty("roles")] | |||||
public Optional<ulong[]> RoleIds { get; set; } | |||||
[JsonProperty("channel_id")] | |||||
public Optional<ulong> ChannelId { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,30 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class ModifyGuildParams | |||||
{ | |||||
[JsonProperty("username")] | |||||
public Optional<string> Username { get; set; } | |||||
[JsonProperty("name")] | |||||
public Optional<string> Name { get; set; } | |||||
[JsonProperty("region")] | |||||
public Optional<string> RegionId { get; set; } | |||||
[JsonProperty("verification_level")] | |||||
public Optional<VerificationLevel> VerificationLevel { get; set; } | |||||
[JsonProperty("default_message_notifications")] | |||||
public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; } | |||||
[JsonProperty("afk_timeout")] | |||||
public Optional<int> AfkTimeout { get; set; } | |||||
[JsonProperty("icon")] | |||||
public Optional<Image?> Icon { get; set; } | |||||
[JsonProperty("splash")] | |||||
public Optional<Image?> Splash { get; set; } | |||||
[JsonProperty("afk_channel_id")] | |||||
public Optional<ulong?> AfkChannelId { get; set; } | |||||
[JsonProperty("owner_id")] | |||||
public Optional<ulong> OwnerId { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
public class ModifyGuildRoleParams | |||||
{ | |||||
[JsonProperty("name")] | |||||
public Optional<string> Name { get; set; } | |||||
[JsonProperty("permissions")] | |||||
public Optional<ulong> Permissions { get; set; } | |||||
[JsonProperty("position")] | |||||
public Optional<int> Position { get; set; } | |||||
[JsonProperty("color")] | |||||
public Optional<uint> Color { get; set; } | |||||
[JsonProperty("hoist")] | |||||
public Optional<bool> Hoist { get; set; } | |||||
} | |||||
} |
@@ -7,6 +7,11 @@ namespace Discord.API.Rest | |||||
public class ModifyGuildRolesParams : ModifyGuildRoleParams | public class ModifyGuildRolesParams : ModifyGuildRoleParams | ||||
{ | { | ||||
[JsonProperty("id")] | [JsonProperty("id")] | ||||
public ulong Id { internal get; set; } | |||||
public ulong Id { get; } | |||||
public ModifyGuildRolesParams(ulong id) | |||||
{ | |||||
Id = id; | |||||
} | |||||
} | } | ||||
} | } |
@@ -7,7 +7,6 @@ namespace Discord.API.Rest | |||||
public class ModifyMessageParams | public class ModifyMessageParams | ||||
{ | { | ||||
[JsonProperty("content")] | [JsonProperty("content")] | ||||
internal Optional<string> _content { get; set; } | |||||
public string Content { set { _content = value; } } | |||||
public Optional<string> Content { get; set; } | |||||
} | } | ||||
} | } |
@@ -0,0 +1,9 @@ | |||||
#pragma warning disable CS1591 | |||||
namespace Discord.API.Rest | |||||
{ | |||||
public class ModifyPresenceParams | |||||
{ | |||||
public Optional<UserStatus> Status { get; set; } | |||||
public Optional<Game> Game { get; set; } | |||||
} | |||||
} |
@@ -7,7 +7,6 @@ namespace Discord.API.Rest | |||||
public class ModifyTextChannelParams : ModifyGuildChannelParams | public class ModifyTextChannelParams : ModifyGuildChannelParams | ||||
{ | { | ||||
[JsonProperty("topic")] | [JsonProperty("topic")] | ||||
internal Optional<string> _topic { get; set; } | |||||
public string Topic { set { _topic = value; } } | |||||
public Optional<string> Topic { get; set; } | |||||
} | } | ||||
} | } |
@@ -7,11 +7,8 @@ namespace Discord.API.Rest | |||||
public class ModifyVoiceChannelParams : ModifyGuildChannelParams | public class ModifyVoiceChannelParams : ModifyGuildChannelParams | ||||
{ | { | ||||
[JsonProperty("bitrate")] | [JsonProperty("bitrate")] | ||||
internal Optional<int> _bitrate { get; set; } | |||||
public int Bitrate { set { _bitrate = value; } } | |||||
public Optional<int> Bitrate { get; set; } | |||||
[JsonProperty("user_limit")] | [JsonProperty("user_limit")] | ||||
internal Optional<int> _userLimit { get; set; } | |||||
public int UserLimit { set { _userLimit = value; } } | |||||
public Optional<int> UserLimit { get; set; } | |||||
} | } | ||||
} | } |
@@ -0,0 +1,35 @@ | |||||
#pragma warning disable CS1591 | |||||
using Discord.Net.Rest; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
public class UploadFileParams | |||||
{ | |||||
public Stream File { get; } | |||||
public Optional<string> Filename { get; set; } | |||||
public Optional<string> Content { get; set; } | |||||
public Optional<string> Nonce { get; set; } | |||||
public Optional<bool> IsTTS { get; set; } | |||||
public UploadFileParams(Stream file) | |||||
{ | |||||
File = file; | |||||
} | |||||
public IReadOnlyDictionary<string, object> ToDictionary() | |||||
{ | |||||
var d = new Dictionary<string, object>(); | |||||
d["file"] = new MultipartFile(File, Filename.GetValueOrDefault("unknown.dat")); | |||||
if (Content.IsSpecified) | |||||
d["content"] = Content.Value; | |||||
if (IsTTS.IsSpecified) | |||||
d["tts"] = IsTTS.Value.ToString(); | |||||
if (Nonce.IsSpecified) | |||||
d["nonce"] = Nonce.Value; | |||||
return d; | |||||
} | |||||
} | |||||
} |
@@ -1,4 +1,5 @@ | |||||
using System; | using System; | ||||
using System.IO; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Discord.Audio | namespace Discord.Audio | ||||
@@ -8,8 +9,7 @@ namespace Discord.Audio | |||||
event Func<Task> Connected; | event Func<Task> Connected; | ||||
event Func<Exception, Task> Disconnected; | event Func<Exception, Task> Disconnected; | ||||
event Func<int, int, Task> LatencyUpdated; | event Func<int, int, Task> LatencyUpdated; | ||||
DiscordVoiceAPIClient ApiClient { get; } | |||||
/// <summary> Gets the current connection state of this client. </summary> | /// <summary> Gets the current connection state of this client. </summary> | ||||
ConnectionState ConnectionState { get; } | ConnectionState ConnectionState { get; } | ||||
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> | /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> | ||||
@@ -17,7 +17,7 @@ namespace Discord.Audio | |||||
Task DisconnectAsync(); | Task DisconnectAsync(); | ||||
RTPWriteStream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); | |||||
OpusEncodeStream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000); | |||||
Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); | |||||
Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000); | |||||
} | } | ||||
} | } |
@@ -0,0 +1,19 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | |||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | |||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||||
</PropertyGroup> | |||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> | |||||
<PropertyGroup Label="Globals"> | |||||
<ProjectGuid>91e9e7bd-75c9-4e98-84aa-2c271922e5c2</ProjectGuid> | |||||
<RootNamespace>Discord</RootNamespace> | |||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> | |||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> | |||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> | |||||
</PropertyGroup> | |||||
<PropertyGroup> | |||||
<SchemaVersion>2.0</SchemaVersion> | |||||
</PropertyGroup> | |||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> | |||||
</Project> |
@@ -3,11 +3,15 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public interface IChannel : ISnowflakeEntity, IUpdateable | |||||
public interface IChannel : ISnowflakeEntity | |||||
{ | { | ||||
IReadOnlyCollection<IUser> CachedUsers { get; } | |||||
/// <summary> Gets a collection of all users in this channel. </summary> | /// <summary> Gets a collection of all users in this channel. </summary> | ||||
Task<IReadOnlyCollection<IUser>> GetUsersAsync(); | |||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync(); | |||||
/// <summary> Gets a user in this channel with the provided id.</summary> | /// <summary> Gets a user in this channel with the provided id.</summary> | ||||
Task<IUser> GetUserAsync(ulong id); | Task<IUser> GetUserAsync(ulong id); | ||||
IUser GetCachedUser(ulong id); | |||||
} | } | ||||
} | } |
@@ -0,0 +1,16 @@ | |||||
using System.Collections.Generic; | |||||
using System.Threading.Tasks; | |||||
namespace Discord | |||||
{ | |||||
public interface IGroupChannel : IMessageChannel, IPrivateChannel | |||||
{ | |||||
///// <summary> Adds a user to this group. </summary> | |||||
//Task AddUserAsync(IUser user); | |||||
//new IReadOnlyCollection<IGroupUser> CachedUsers { get; } | |||||
/// <summary> Leaves this group. </summary> | |||||
Task LeaveAsync(); | |||||
} | |||||
} |
@@ -12,8 +12,9 @@ namespace Discord | |||||
/// <summary> Gets the position of this channel in the guild's channel list, relative to others of the same type. </summary> | /// <summary> Gets the position of this channel in the guild's channel list, relative to others of the same type. </summary> | ||||
int Position { get; } | int Position { get; } | ||||
/// <summary> Gets the guild this channel is a member of. </summary> | |||||
IGuild Guild { get; } | |||||
/// <summary> Gets the id of the guild this channel is a member of. </summary> | |||||
ulong GuildId { get; } | |||||
new IReadOnlyCollection<IGuildUser> CachedUsers { get; } | |||||
/// <summary> Creates a new invite to this channel. </summary> | /// <summary> Creates a new invite to this channel. </summary> | ||||
/// <param name="maxAge"> The time (in seconds) until the invite expires. Set to null to never expire. </param> | /// <param name="maxAge"> The time (in seconds) until the invite expires. Set to null to never expire. </param> | ||||
@@ -43,8 +44,9 @@ namespace Discord | |||||
Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions); | Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions); | ||||
/// <summary> Gets a collection of all users in this channel. </summary> | /// <summary> Gets a collection of all users in this channel. </summary> | ||||
new Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync(); | |||||
new IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync(); | |||||
/// <summary> Gets a user in this channel with the provided id.</summary> | /// <summary> Gets a user in this channel with the provided id.</summary> | ||||
new Task<IGuildUser> GetUserAsync(ulong id); | new Task<IGuildUser> GetUserAsync(ulong id); | ||||
new IGuildUser GetCachedUser(ulong id); | |||||
} | } | ||||
} | } |
@@ -1,4 +1,5 @@ | |||||
using System.Collections.Generic; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -15,20 +16,21 @@ namespace Discord | |||||
Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false); | Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false); | ||||
/// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false); | Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false); | ||||
/// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | /// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | ||||
Task<IMessage> GetMessageAsync(ulong id); | Task<IMessage> GetMessageAsync(ulong id); | ||||
/// <summary> Gets the message from this channel's cache with the given id, or null if not found. </summary> | /// <summary> Gets the message from this channel's cache with the given id, or null if not found. </summary> | ||||
IMessage GetCachedMessage(ulong id); | IMessage GetCachedMessage(ulong id); | ||||
/// <summary> Gets the last N messages from this message channel. </summary> | /// <summary> Gets the last N messages from this message channel. </summary> | ||||
Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch); | |||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch); | |||||
/// <summary> Gets a collection of messages in this channel. </summary> | /// <summary> Gets a collection of messages in this channel. </summary> | ||||
Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch); | |||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch); | |||||
/// <summary> Gets a collection of pinned messages in this channel. </summary> | /// <summary> Gets a collection of pinned messages in this channel. </summary> | ||||
Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(); | Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(); | ||||
/// <summary> Bulk deletes multiple messages. </summary> | /// <summary> Bulk deletes multiple messages. </summary> | ||||
Task DeleteMessagesAsync(IEnumerable<IMessage> messages); | Task DeleteMessagesAsync(IEnumerable<IMessage> messages); | ||||
/// <summary> Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.</summary> | /// <summary> Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.</summary> | ||||
Task TriggerTypingAsync(); | |||||
IDisposable EnterTypingState(); | |||||
} | } | ||||
} | } |
@@ -0,0 +1,33 @@ | |||||
using System.Collections.Generic; | |||||
using System.Collections.Immutable; | |||||
using System.Diagnostics; | |||||
using Model = Discord.API.Emoji; | |||||
namespace Discord | |||||
{ | |||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
public struct Emoji | |||||
{ | |||||
public ulong Id { get; } | |||||
public string Name { get; } | |||||
public bool IsManaged { get; } | |||||
public bool RequireColons { get; } | |||||
public IReadOnlyList<ulong> RoleIds { get; } | |||||
public Emoji(ulong id, string name, bool isManaged, bool requireColons, IReadOnlyList<ulong> roleIds) | |||||
{ | |||||
Id = id; | |||||
Name = name; | |||||
IsManaged = isManaged; | |||||
RequireColons = requireColons; | |||||
RoleIds = roleIds; | |||||
} | |||||
public static Emoji Create(Model model) | |||||
{ | |||||
return new Emoji(model.Id, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); | |||||
} | |||||
public override string ToString() => Name; | |||||
private string DebuggerDisplay => $"{Name} ({Id})"; | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
//using Discord.Rest; | |||||
using System.Diagnostics; | |||||
using Model = Discord.API.Ban; | |||||
namespace Discord | |||||
{ | |||||
public interface IBan | |||||
{ | |||||
IUser User { get; } | |||||
string Reason { get; } | |||||
} | |||||
} |
@@ -6,7 +6,7 @@ using Discord.Audio; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public interface IGuild : IDeletable, ISnowflakeEntity, IUpdateable | |||||
public interface IGuild : IDeletable, ISnowflakeEntity | |||||
{ | { | ||||
/// <summary> Gets the name of this guild. </summary> | /// <summary> Gets the name of this guild. </summary> | ||||
string Name { get; } | string Name { get; } | ||||
@@ -20,8 +20,12 @@ namespace Discord | |||||
MfaLevel MfaLevel { get; } | MfaLevel MfaLevel { get; } | ||||
/// <summary> Gets the level of requirements a user must fulfill before being allowed to post messages in this guild. </summary> | /// <summary> Gets the level of requirements a user must fulfill before being allowed to post messages in this guild. </summary> | ||||
VerificationLevel VerificationLevel { get; } | VerificationLevel VerificationLevel { get; } | ||||
/// <summary> Returns the id of this guild's icon, or null if one is not set. </summary> | |||||
string IconId { get; } | |||||
/// <summary> Returns the url to this guild's icon, or null if one is not set. </summary> | /// <summary> Returns the url to this guild's icon, or null if one is not set. </summary> | ||||
string IconUrl { get; } | string IconUrl { get; } | ||||
/// <summary> Returns the id of this guild's splash image, or null if one is not set. </summary> | |||||
string SplashId { get; } | |||||
/// <summary> Returns the url to this guild's splash image, or null if one is not set. </summary> | /// <summary> Returns the url to this guild's splash image, or null if one is not set. </summary> | ||||
string SplashUrl { get; } | string SplashUrl { get; } | ||||
/// <summary> Returns true if this guild is currently connected and ready to be used. Only applies to the WebSocket client. </summary> | /// <summary> Returns true if this guild is currently connected and ready to be used. Only applies to the WebSocket client. </summary> | ||||
@@ -48,6 +52,7 @@ namespace Discord | |||||
IReadOnlyCollection<string> Features { get; } | IReadOnlyCollection<string> Features { get; } | ||||
/// <summary> Gets a collection of all roles in this guild. </summary> | /// <summary> Gets a collection of all roles in this guild. </summary> | ||||
IReadOnlyCollection<IRole> Roles { get; } | IReadOnlyCollection<IRole> Roles { get; } | ||||
IReadOnlyCollection<IGuildUser> CachedUsers { get; } | |||||
/// <summary> Modifies this guild. </summary> | /// <summary> Modifies this guild. </summary> | ||||
Task ModifyAsync(Action<ModifyGuildParams> func); | Task ModifyAsync(Action<ModifyGuildParams> func); | ||||
@@ -61,7 +66,7 @@ namespace Discord | |||||
Task LeaveAsync(); | Task LeaveAsync(); | ||||
/// <summary> Gets a collection of all users banned on this guild. </summary> | /// <summary> Gets a collection of all users banned on this guild. </summary> | ||||
Task<IReadOnlyCollection<Ban>> GetBansAsync(); | |||||
Task<IReadOnlyCollection<IBan>> GetBansAsync(); | |||||
/// <summary> Bans the provided user from this guild and optionally prunes their recent messages. </summary> | /// <summary> Bans the provided user from this guild and optionally prunes their recent messages. </summary> | ||||
Task AddBanAsync(IUser user, int pruneDays = 0); | Task AddBanAsync(IUser user, int pruneDays = 0); | ||||
/// <summary> Bans the provided user id from this guild and optionally prunes their recent messages. </summary> | /// <summary> Bans the provided user id from this guild and optionally prunes their recent messages. </summary> | ||||
@@ -75,11 +80,15 @@ namespace Discord | |||||
Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync(); | Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync(); | ||||
/// <summary> Gets the channel in this guild with the provided id, or null if not found. </summary> | /// <summary> Gets the channel in this guild with the provided id, or null if not found. </summary> | ||||
Task<IGuildChannel> GetChannelAsync(ulong id); | Task<IGuildChannel> GetChannelAsync(ulong id); | ||||
IGuildChannel GetCachedChannel(ulong id); | |||||
/// <summary> Creates a new text channel. </summary> | /// <summary> Creates a new text channel. </summary> | ||||
Task<ITextChannel> CreateTextChannelAsync(string name); | Task<ITextChannel> CreateTextChannelAsync(string name); | ||||
/// <summary> Creates a new voice channel. </summary> | /// <summary> Creates a new voice channel. </summary> | ||||
Task<IVoiceChannel> CreateVoiceChannelAsync(string name); | Task<IVoiceChannel> CreateVoiceChannelAsync(string name); | ||||
Task<IReadOnlyCollection<IGuildIntegration>> GetIntegrationsAsync(); | |||||
Task<IGuildIntegration> CreateIntegrationAsync(ulong id, string type); | |||||
/// <summary> Gets a collection of all invites to this guild. </summary> | /// <summary> Gets a collection of all invites to this guild. </summary> | ||||
Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(); | Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(); | ||||
@@ -92,9 +101,10 @@ namespace Discord | |||||
Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync(); | Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync(); | ||||
/// <summary> Gets the user in this guild with the provided id, or null if not found. </summary> | /// <summary> Gets the user in this guild with the provided id, or null if not found. </summary> | ||||
Task<IGuildUser> GetUserAsync(ulong id); | Task<IGuildUser> GetUserAsync(ulong id); | ||||
IGuildUser GetCachedUser(ulong id); | |||||
/// <summary> Gets the current user for this guild. </summary> | /// <summary> Gets the current user for this guild. </summary> | ||||
Task<IGuildUser> GetCurrentUserAsync(); | Task<IGuildUser> GetCurrentUserAsync(); | ||||
/// <summary> Downloads all users for this guild if the current list is incomplete. Only applies to the WebSocket client. </summary> | |||||
/// <summary> Downloads all users for this guild if the current list is incomplete. </summary> | |||||
Task DownloadUsersAsync(); | Task DownloadUsersAsync(); | ||||
/// <summary> Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. </summary> | /// <summary> Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. </summary> | ||||
Task<int> PruneUsersAsync(int days = 30, bool simulate = false); | Task<int> PruneUsersAsync(int days = 30, bool simulate = false); |
@@ -2,7 +2,6 @@ | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
//TODO: Add docstrings | |||||
public interface IGuildIntegration | public interface IGuildIntegration | ||||
{ | { | ||||
ulong Id { get; } | ulong Id { get; } | ||||
@@ -15,8 +14,8 @@ namespace Discord | |||||
DateTimeOffset SyncedAt { get; } | DateTimeOffset SyncedAt { get; } | ||||
IntegrationAccount Account { get; } | IntegrationAccount Account { get; } | ||||
IGuild Guild { get; } | |||||
ulong GuildId { get; } | |||||
ulong RoleId { get; } | |||||
IUser User { get; } | IUser User { get; } | ||||
IRole Role { get; } | |||||
} | } | ||||
} | } |
@@ -1,6 +1,6 @@ | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public interface IApplication : ISnowflakeEntity, IUpdateable | |||||
public interface IApplication : ISnowflakeEntity | |||||
{ | { | ||||
string Name { get; } | string Name { get; } | ||||
string Description { get; } | string Description { get; } |
@@ -0,0 +1,15 @@ | |||||
using System; | |||||
namespace Discord | |||||
{ | |||||
public interface IEntity<TId> | |||||
where TId : IEquatable<TId> | |||||
{ | |||||
/// <summary> Gets the IDiscordClient that created this object. </summary> | |||||
IDiscordClient Discord { get; } | |||||
/// <summary> Gets the unique identifier for this object. </summary> | |||||
TId Id { get; } | |||||
} | |||||
} |
@@ -0,0 +1,6 @@ | |||||
namespace Discord | |||||
{ | |||||
public interface ISnowflakeEntity : IEntity<ulong> | |||||
{ | |||||
} | |||||
} |
@@ -12,7 +12,9 @@ namespace Discord | |||||
Name = name; | Name = name; | ||||
Url = url; | Url = url; | ||||
} | } | ||||
internal EmbedProvider(Model model) | |||||
: this(model.Name, model.Url) { } | |||||
public static EmbedProvider Create(Model model) | |||||
{ | |||||
return new EmbedProvider(model.Name, model.Url); | |||||
} | |||||
} | } | ||||
} | } |
@@ -16,14 +16,11 @@ namespace Discord | |||||
Height = height; | Height = height; | ||||
Width = width; | Width = width; | ||||
} | } | ||||
internal EmbedThumbnail(Model model) | |||||
: this( | |||||
model.Url, | |||||
model.ProxyUrl, | |||||
model.Height.IsSpecified ? model.Height.Value : (int?)null, | |||||
model.Width.IsSpecified ? model.Width.Value : (int?)null) | |||||
public static EmbedThumbnail Create(Model model) | |||||
{ | { | ||||
return new EmbedThumbnail(model.Url, model.ProxyUrl, | |||||
model.Height.IsSpecified ? model.Height.Value : (int?)null, | |||||
model.Width.IsSpecified ? model.Width.Value : (int?)null); | |||||
} | } | ||||
} | } | ||||
} | } |