@@ -4,7 +4,6 @@ using Discord.Net;
using Discord.Net.Converters;
using Discord.Net.Queue;
using Discord.Net.Rest;
using Discord.Rest;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -28,6 +27,7 @@ namespace Discord.API
protected readonly JsonSerializer _serializer;
protected readonly SemaphoreSlim _stateLock;
private readonly RestClientProvider _restClientProvider;
private readonly string _userAgent;
protected string _authToken;
protected bool _isDisposed;
@@ -36,11 +36,13 @@ namespace Discord.API
public LoginState LoginState { 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;
_userAgent = userAgent;
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
RequestQueue = requestQueue;
@@ -52,7 +54,7 @@ namespace Discord.API
{
_restClient = _restClientProvider(baseUrl);
_restClient.SetHeader("accept", "*/*");
_restClient.SetHeader("user-agent", DiscordRestConfig.U serAgent);
_restClient.SetHeader("user-agent", _u serAgent);
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken));
}
internal static string GetPrefixedToken(TokenType tokenType, string token)
@@ -111,6 +113,8 @@ namespace Discord.API
_authToken = token;
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken));
CurrentUser = await GetMyUserAsync();
LoginState = LoginState.LoggedIn;
}
catch (Exception)
@@ -144,6 +148,7 @@ namespace Discord.API
await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false);
_restClient.SetCancelToken(CancellationToken.None);
CurrentUser = null;
LoginState = LoginState.LoggedOut;
}
@@ -268,8 +273,8 @@ namespace Discord.API
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args));
Preconditions.GreaterThan(args._b itrate, 0, nameof(args.Bitrate));
Preconditions.NotNullOrWhitespace(args._n ame, nameof(args.Name));
Preconditions.GreaterThan(args.B itrate, 0, nameof(args.Bitrate));
Preconditions.NotNullOrWhitespace(args.N ame, nameof(args.Name));
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.NotNull(args, nameof(args));
Preconditions.AtLeast(args._p osition, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args._n ame, nameof(args.Name));
Preconditions.AtLeast(args.P osition, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args.N ame, nameof(args.Name));
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.NotNull(args, nameof(args));
Preconditions.AtLeast(args._p osition, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args._n ame, nameof(args.Name));
Preconditions.AtLeast(args.P osition, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args.N ame, nameof(args.Name));
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.NotNull(args, nameof(args));
Preconditions.GreaterThan(args._b itrate, 0, nameof(args.Bitrate));
Preconditions.AtLeast(args._u serLimit, 0, nameof(args.Bitrate));
Preconditions.AtLeast(args._p osition, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args._n ame, nameof(args.Name));
Preconditions.GreaterThan(args.B itrate, 0, nameof(args.Bitrate));
Preconditions.AtLeast(args.U serLimit, 0, nameof(args.Bitrate));
Preconditions.AtLeast(args.P osition, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args.N ame, nameof(args.Name));
return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false);
}
@@ -326,6 +331,132 @@ namespace Discord.API
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
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.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);
}
@@ -419,11 +550,11 @@ namespace Discord.API
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args));
Preconditions.NotEqual(args._afkChannelId, 0, nameof(args.AFK ChannelId));
Preconditions.AtLeast(args._afkTimeout, 0, nameof(args.AFK Timeout));
Preconditions.NotNullOrEmpty(args._n ame, nameof(args.Name));
Preconditions.GreaterThan(args._o wnerId, 0, nameof(args.OwnerId));
Preconditions.NotNull(args._region, nameof(args.Region ));
Preconditions.NotEqual(args.AfkChannelId, 0, nameof(args.Afk ChannelId));
Preconditions.AtLeast(args.AfkTimeout, 0, nameof(args.Afk Timeout));
Preconditions.NotNullOrEmpty(args.N ame, nameof(args.Name));
Preconditions.GreaterThan(args.O wnerId, 0, nameof(args.OwnerId));
Preconditions.NotNull(args.RegionId, nameof(args.RegionId ));
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(userId, 0, nameof(userId));
Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args._d eleteMessageDays, 0, nameof(args.DeleteMessageDays));
Preconditions.AtLeast(args.D eleteMessageDays, 0, nameof(args.DeleteMessageDays));
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(integrationId, 0, nameof(integrationId));
Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args._e xpireBehavior, 0, nameof(args.ExpireBehavior));
Preconditions.AtLeast(args._e xpireGracePeriod, 0, nameof(args.ExpireGracePeriod));
Preconditions.AtLeast(args.E xpireBehavior, 0, nameof(args.ExpireBehavior));
Preconditions.AtLeast(args.E xpireGracePeriod, 0, nameof(args.ExpireGracePeriod));
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);
}
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));
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)
{
Preconditions.NotEqual(channelId, 0, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args._m axAge, 0, nameof(args.MaxAge));
Preconditions.AtLeast(args._m axUses, 0, nameof(args.MaxUses));
Preconditions.AtLeast(args.M axAge, 0, nameof(args.MaxAge));
Preconditions.AtLeast(args.M axUses, 0, nameof(args.MaxUses));
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.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)
{
@@ -646,7 +750,18 @@ namespace Discord.API
Preconditions.NotEqual(userId, 0, nameof(userId));
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
@@ -674,9 +789,9 @@ namespace Discord.API
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(roleId, 0, nameof(roleId));
Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args._c olor, 0, nameof(args.Color));
Preconditions.NotNullOrEmpty(args._n ame, nameof(args.Name));
Preconditions.AtLeast(args._p osition, 0, nameof(args.Position));
Preconditions.AtLeast(args.C olor, 0, nameof(args.Color));
Preconditions.NotNullOrEmpty(args.N ame, nameof(args.Name));
Preconditions.AtLeast(args.P osition, 0, nameof(args.Position));
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
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)
{
Preconditions.NotNull(args, nameof(args));
Preconditions.NotNullOrEmpty(args._u sername, nameof(args.Username));
Preconditions.NotNullOrEmpty(args.U sername, nameof(args.Username));
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)
{
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);
}