@@ -25,10 +25,15 @@ namespace Discord.API | |||||
private readonly IRestClient _restClient; | private readonly IRestClient _restClient; | ||||
private readonly CancellationToken _cancelToken; | private readonly CancellationToken _cancelToken; | ||||
private readonly JsonSerializer _serializer; | private readonly JsonSerializer _serializer; | ||||
public TokenType AuthTokenType { get; private set; } | |||||
public IRestClient RestClient { get; private set; } | |||||
public IRequestQueue RequestQueue { get; private set; } | |||||
internal DiscordRawClient(RestClientProvider restClientProvider, CancellationToken cancelToken, TokenType authTokenType, string authToken) | internal DiscordRawClient(RestClientProvider restClientProvider, CancellationToken cancelToken, TokenType authTokenType, string authToken) | ||||
{ | { | ||||
_cancelToken = cancelToken; | _cancelToken = cancelToken; | ||||
AuthTokenType = authTokenType; | |||||
switch (authTokenType) | switch (authTokenType) | ||||
{ | { | ||||
@@ -11,8 +11,6 @@ namespace Discord | |||||
int AFKTimeout { get; } | int AFKTimeout { get; } | ||||
/// <summary> Returns true if this guild is embeddable (e.g. widget) </summary> | /// <summary> Returns true if this guild is embeddable (e.g. widget) </summary> | ||||
bool IsEmbeddable { get; } | bool IsEmbeddable { get; } | ||||
/// <summary> Returns true if the current user owns this guild. </summary> | |||||
bool IsOwner { get; } | |||||
/// <summary> Gets the name of this guild. </summary> | /// <summary> Gets the name of this guild. </summary> | ||||
string Name { get; } | string Name { get; } | ||||
int VerificationLevel { get; } | int VerificationLevel { get; } | ||||
@@ -9,8 +9,6 @@ namespace Discord | |||||
{ | { | ||||
/// <summary> Gets the time of this message's last edit, if any. </summary> | /// <summary> Gets the time of this message's last edit, if any. </summary> | ||||
DateTime? EditedTimestamp { get; } | DateTime? EditedTimestamp { get; } | ||||
/// <summary> Returns true if this message originated from the logged-in account. </summary> | |||||
bool IsAuthor { get; } | |||||
/// <summary> Returns true if this message was sent as a text-to-speech message. </summary> | /// <summary> Returns true if this message was sent as a text-to-speech message. </summary> | ||||
bool IsTTS { get; } | bool IsTTS { get; } | ||||
/// <summary> Returns the original, unprocessed text for this message. </summary> | /// <summary> Returns the original, unprocessed text for this message. </summary> | ||||
@@ -12,8 +12,6 @@ namespace Discord | |||||
ushort Discriminator { get; } | ushort Discriminator { get; } | ||||
/// <summary> Returns true if this user is a bot account. </summary> | /// <summary> Returns true if this user is a bot account. </summary> | ||||
bool IsBot { get; } | bool IsBot { get; } | ||||
/// <summary> Returns true is this user is the current logged-in account. </summary> | |||||
bool IsCurrentUser { get; } | |||||
/// <summary> Gets the current status of this user. </summary> | /// <summary> Gets the current status of this user. </summary> | ||||
UserStatus Status { get; } | UserStatus Status { get; } | ||||
/// <summary> Gets the username for this user. </summary> | /// <summary> Gets the username for this user. </summary> | ||||
@@ -1,15 +1,18 @@ | |||||
using Discord.API; | using Discord.API; | ||||
using Discord.Net.Rest; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
//TODO: Add docstrings | |||||
public interface IDiscordClient | public interface IDiscordClient | ||||
{ | { | ||||
ISelfUser CurrentUser { get; } | |||||
TokenType AuthTokenType { get; } | |||||
DiscordRawClient BaseClient { get; } | DiscordRawClient BaseClient { get; } | ||||
//IMessageQueue MessageQueue { get; } | |||||
IRestClient RestClient { get; } | |||||
IRequestQueue RequestQueue { get; } | |||||
Task Login(TokenType tokenType, string token); | Task Login(TokenType tokenType, string token); | ||||
Task Logout(); | Task Logout(); | ||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks; | |||||
namespace Discord.Net.Rest | namespace Discord.Net.Rest | ||||
{ | { | ||||
//TODO: Add docstrings | |||||
public interface IRestClient | public interface IRestClient | ||||
{ | { | ||||
void SetHeader(string key, string value); | void SetHeader(string key, string value); | ||||
@@ -2,6 +2,7 @@ using System.Threading.Tasks; | |||||
namespace Discord.Net.Rest | namespace Discord.Net.Rest | ||||
{ | { | ||||
//TODO: Add docstrings | |||||
public interface IRequestQueue | public interface IRequestQueue | ||||
{ | { | ||||
Task Clear(GlobalBucket type); | Task Clear(GlobalBucket type); | ||||
@@ -1,11 +1,13 @@ | |||||
using Discord.API.Rest; | using Discord.API.Rest; | ||||
using Discord.Logging; | using Discord.Logging; | ||||
using Discord.Net; | |||||
using Discord.Net.Rest; | using Discord.Net.Rest; | ||||
using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -22,10 +24,14 @@ namespace Discord.Rest | |||||
private CancellationTokenSource _cancelTokenSource; | private CancellationTokenSource _cancelTokenSource; | ||||
private bool _isDisposed; | private bool _isDisposed; | ||||
private string _userAgent; | private string _userAgent; | ||||
private SelfUser _currentUser; | |||||
public bool IsLoggedIn { get; private set; } | public bool IsLoggedIn { get; private set; } | ||||
internal API.DiscordRawClient BaseClient { get; private set; } | |||||
internal SelfUser CurrentUser { get; private set; } | |||||
public API.DiscordRawClient BaseClient { get; private set; } | |||||
public TokenType AuthTokenType => BaseClient.AuthTokenType; | |||||
public IRestClient RestClient => BaseClient.RestClient; | |||||
public IRequestQueue RequestQueue => BaseClient.RequestQueue; | |||||
public DiscordClient(DiscordConfig config = null) | public DiscordClient(DiscordConfig config = null) | ||||
{ | { | ||||
@@ -64,9 +70,13 @@ namespace Discord.Rest | |||||
//MessageQueue = new MessageQueue(RestClient, _restLogger); | //MessageQueue = new MessageQueue(RestClient, _restLogger); | ||||
//await MessageQueue.Start(_cancelTokenSource.Token).ConfigureAwait(false); | //await MessageQueue.Start(_cancelTokenSource.Token).ConfigureAwait(false); | ||||
var currentUser = await BaseClient.GetCurrentUser().ConfigureAwait(false); | |||||
CurrentUser = new SelfUser(this, currentUser); | |||||
try | |||||
{ | |||||
var currentUser = await BaseClient.GetCurrentUser().ConfigureAwait(false); | |||||
_currentUser = new SelfUser(this, currentUser); | |||||
} | |||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized && tokenType == TokenType.Bearer) { } //Ignore 401 if Bearer doesnt have identity | |||||
_cancelTokenSource = cancelTokenSource; | _cancelTokenSource = cancelTokenSource; | ||||
IsLoggedIn = true; | IsLoggedIn = true; | ||||
@@ -173,25 +183,25 @@ namespace Discord.Rest | |||||
return new PublicUser(this, model); | return new PublicUser(this, model); | ||||
return null; | return null; | ||||
} | } | ||||
public async Task<IUser> GetUser(string username, ushort discriminator) | |||||
public async Task<User> GetUser(string username, ushort discriminator) | |||||
{ | { | ||||
var model = await BaseClient.GetUser(username, discriminator).ConfigureAwait(false); | var model = await BaseClient.GetUser(username, discriminator).ConfigureAwait(false); | ||||
if (model != null) | if (model != null) | ||||
return new PublicUser(this, model); | return new PublicUser(this, model); | ||||
return null; | return null; | ||||
} | } | ||||
public async Task<ISelfUser> GetCurrentUser() | |||||
public async Task<SelfUser> GetCurrentUser() | |||||
{ | { | ||||
var currentUser = CurrentUser; | |||||
if (currentUser == null) | |||||
var user = _currentUser; | |||||
if (user == null) | |||||
{ | { | ||||
var model = await BaseClient.GetCurrentUser().ConfigureAwait(false); | var model = await BaseClient.GetCurrentUser().ConfigureAwait(false); | ||||
currentUser = new SelfUser(this, model); | |||||
CurrentUser = currentUser; | |||||
user = new SelfUser(this, model); | |||||
_currentUser = user; | |||||
} | } | ||||
return currentUser; | |||||
return user; | |||||
} | } | ||||
public async Task<IEnumerable<IUser>> QueryUsers(string query, int limit) | |||||
public async Task<IEnumerable<User>> QueryUsers(string query, int limit) | |||||
{ | { | ||||
var models = await BaseClient.QueryUsers(query, limit).ConfigureAwait(false); | var models = await BaseClient.QueryUsers(query, limit).ConfigureAwait(false); | ||||
return models.Select(x => new PublicUser(this, x)); | return models.Select(x => new PublicUser(this, x)); | ||||
@@ -225,7 +235,6 @@ namespace Discord.Rest | |||||
public void Dispose() => Dispose(true); | public void Dispose() => Dispose(true); | ||||
API.DiscordRawClient IDiscordClient.BaseClient => BaseClient; | API.DiscordRawClient IDiscordClient.BaseClient => BaseClient; | ||||
ISelfUser IDiscordClient.CurrentUser => CurrentUser; | |||||
async Task<IChannel> IDiscordClient.GetChannel(ulong id) | async Task<IChannel> IDiscordClient.GetChannel(ulong id) | ||||
=> await GetChannel(id).ConfigureAwait(false); | => await GetChannel(id).ConfigureAwait(false); | ||||
@@ -243,6 +252,10 @@ namespace Discord.Rest | |||||
=> await CreateGuild(name, region, jpegIcon).ConfigureAwait(false); | => await CreateGuild(name, region, jpegIcon).ConfigureAwait(false); | ||||
async Task<IUser> IDiscordClient.GetUser(ulong id) | async Task<IUser> IDiscordClient.GetUser(ulong id) | ||||
=> await GetUser(id).ConfigureAwait(false); | => await GetUser(id).ConfigureAwait(false); | ||||
async Task<IUser> IDiscordClient.GetUser(string username, ushort discriminator) | |||||
=> await GetUser(username, discriminator).ConfigureAwait(false); | |||||
async Task<ISelfUser> IDiscordClient.GetCurrentUser() | |||||
=> await GetCurrentUser().ConfigureAwait(false); | |||||
async Task<IEnumerable<IUser>> IDiscordClient.QueryUsers(string query, int limit) | async Task<IEnumerable<IUser>> IDiscordClient.QueryUsers(string query, int limit) | ||||
=> await QueryUsers(query, limit).ConfigureAwait(false); | => await QueryUsers(query, limit).ConfigureAwait(false); | ||||
async Task<IEnumerable<IVoiceRegion>> IDiscordClient.GetVoiceRegions() | async Task<IEnumerable<IVoiceRegion>> IDiscordClient.GetVoiceRegions() | ||||
@@ -20,8 +20,6 @@ namespace Discord.Rest | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public DateTime CreatedAt => DateTimeHelper.FromSnowflake(Id); | public DateTime CreatedAt => DateTimeHelper.FromSnowflake(Id); | ||||
/// <inheritdoc /> | |||||
public IEnumerable<IUser> Users => ImmutableArray.Create<IUser>(Discord.CurrentUser, Recipient); | |||||
internal DMChannel(DiscordClient discord, Model model) | internal DMChannel(DiscordClient discord, Model model) | ||||
{ | { | ||||
@@ -39,20 +37,23 @@ namespace Discord.Rest | |||||
} | } | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public IUser GetUser(ulong id) | |||||
public async Task<IUser> GetUser(ulong id) | |||||
{ | { | ||||
var currentUser = await Discord.GetCurrentUser().ConfigureAwait(false); | |||||
if (id == Recipient.Id) | if (id == Recipient.Id) | ||||
return Recipient; | return Recipient; | ||||
else if (id == Discord.CurrentUser.Id) | |||||
return Discord.CurrentUser; | |||||
else if (id == currentUser.Id) | |||||
return currentUser; | |||||
else | else | ||||
return null; | return null; | ||||
} | } | ||||
public IEnumerable<IUser> GetUsers() | |||||
/// <inheritdoc /> | |||||
public async Task<IEnumerable<IUser>> GetUsers() | |||||
{ | { | ||||
return ImmutableArray.Create<IUser>(Discord.CurrentUser, Recipient); | |||||
var currentUser = await Discord.GetCurrentUser().ConfigureAwait(false); | |||||
return ImmutableArray.Create<IUser>(currentUser, Recipient); | |||||
} | } | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public async Task<IEnumerable<Message>> GetMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | public async Task<IEnumerable<Message>> GetMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
{ | { | ||||
@@ -124,10 +125,10 @@ namespace Discord.Rest | |||||
IDMUser IDMChannel.Recipient => Recipient; | IDMUser IDMChannel.Recipient => Recipient; | ||||
Task<IEnumerable<IUser>> IChannel.GetUsers() | |||||
=> Task.FromResult(GetUsers()); | |||||
Task<IUser> IChannel.GetUser(ulong id) | |||||
=> Task.FromResult(GetUser(id)); | |||||
async Task<IEnumerable<IUser>> IChannel.GetUsers() | |||||
=> await GetUsers().ConfigureAwait(false); | |||||
async Task<IUser> IChannel.GetUser(ulong id) | |||||
=> await GetUser(id).ConfigureAwait(false); | |||||
Task<IMessage> IMessageChannel.GetMessage(ulong id) | Task<IMessage> IMessageChannel.GetMessage(ulong id) | ||||
=> throw new NotSupportedException(); | => throw new NotSupportedException(); | ||||
async Task<IEnumerable<IMessage>> IMessageChannel.GetMessages(int limit) | async Task<IEnumerable<IMessage>> IMessageChannel.GetMessages(int limit) | ||||
@@ -46,8 +46,6 @@ namespace Discord.Rest | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public DateTime CreatedAt => DateTimeHelper.FromSnowflake(Id); | public DateTime CreatedAt => DateTimeHelper.FromSnowflake(Id); | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public bool IsOwner => OwnerId == Discord.CurrentUser.Id; | |||||
/// <inheritdoc /> | |||||
public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); | public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, _splashId); | public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, _splashId); | ||||
@@ -157,15 +155,11 @@ namespace Discord.Rest | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public async Task Leave() | public async Task Leave() | ||||
{ | { | ||||
if (IsOwner) | |||||
throw new InvalidOperationException("Unable to leave a guild the current user owns."); | |||||
await Discord.BaseClient.LeaveGuild(Id).ConfigureAwait(false); | await Discord.BaseClient.LeaveGuild(Id).ConfigureAwait(false); | ||||
} | } | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public async Task Delete() | public async Task Delete() | ||||
{ | { | ||||
if (!IsOwner) | |||||
throw new InvalidOperationException("Unable to delete a guild the current user does not own."); | |||||
await Discord.BaseClient.DeleteGuild(Id).ConfigureAwait(false); | await Discord.BaseClient.DeleteGuild(Id).ConfigureAwait(false); | ||||
} | } | ||||
@@ -317,7 +311,8 @@ namespace Discord.Rest | |||||
/// <summary> Gets a the current user. </summary> | /// <summary> Gets a the current user. </summary> | ||||
public async Task<GuildUser> GetCurrentUser() | public async Task<GuildUser> GetCurrentUser() | ||||
{ | { | ||||
return await GetUser(Discord.CurrentUser.Id).ConfigureAwait(false); | |||||
var currentUser = await Discord.GetCurrentUser().ConfigureAwait(false); | |||||
return await GetUser(currentUser.Id).ConfigureAwait(false); | |||||
} | } | ||||
public async Task<int> PruneUsers(int days = 30, bool simulate = false) | public async Task<int> PruneUsers(int days = 30, bool simulate = false) | ||||
{ | { | ||||
@@ -41,8 +41,6 @@ namespace Discord.Rest | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public DateTime CreatedAt => DateTimeHelper.FromSnowflake(Id); | public DateTime CreatedAt => DateTimeHelper.FromSnowflake(Id); | ||||
/// <inheritdoc /> | |||||
public bool IsAuthor => Discord.CurrentUser.Id == Author.Id; | |||||
internal DiscordClient Discord => (Channel as TextChannel)?.Discord ?? (Channel as DMChannel).Discord; | internal DiscordClient Discord => (Channel as TextChannel)?.Discord ?? (Channel as DMChannel).Discord; | ||||
internal Message(IMessageChannel channel, Model model) | internal Message(IMessageChannel channel, Model model) | ||||
@@ -84,7 +84,7 @@ namespace Discord.Rest | |||||
var args = new ModifyGuildMemberParams(); | var args = new ModifyGuildMemberParams(); | ||||
func(args); | func(args); | ||||
bool isCurrentUser = IsCurrentUser; | |||||
bool isCurrentUser = (await Discord.GetCurrentUser().ConfigureAwait(false)).Id == Id; | |||||
if (isCurrentUser && args.Nickname.IsSpecified) | if (isCurrentUser && args.Nickname.IsSpecified) | ||||
{ | { | ||||
var nickArgs = new ModifyCurrentUserNickParams | var nickArgs = new ModifyCurrentUserNickParams | ||||
@@ -92,6 +92,7 @@ namespace Discord.Rest | |||||
Nickname = args.Nickname.Value | Nickname = args.Nickname.Value | ||||
}; | }; | ||||
await Discord.BaseClient.ModifyCurrentUserNick(Guild.Id, nickArgs).ConfigureAwait(false); | await Discord.BaseClient.ModifyCurrentUserNick(Guild.Id, nickArgs).ConfigureAwait(false); | ||||
args.Nickname = new API.Optional<string>(); //Remove | |||||
} | } | ||||
if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.Roles.IsSpecified) | if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.Roles.IsSpecified) | ||||
@@ -28,8 +28,6 @@ namespace Discord.Rest | |||||
public string Mention => MentionHelper.Mention(this, false); | public string Mention => MentionHelper.Mention(this, false); | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public string NicknameMention => MentionHelper.Mention(this, true); | public string NicknameMention => MentionHelper.Mention(this, true); | ||||
/// <inheritdoc /> | |||||
public bool IsCurrentUser => Id == Discord.CurrentUser.Id; | |||||
internal User(Model model) | internal User(Model model) | ||||
{ | { | ||||