You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

BaseDiscordClient.cs 8.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using Discord.Logging;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Collections.Immutable;
  5. using System.IO;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace Discord.Rest
  9. {
  10. public abstract class BaseDiscordClient : IDiscordClient
  11. {
  12. public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
  13. internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();
  14. public event Func<Task> LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } }
  15. private readonly AsyncEvent<Func<Task>> _loggedInEvent = new AsyncEvent<Func<Task>>();
  16. public event Func<Task> LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } }
  17. private readonly AsyncEvent<Func<Task>> _loggedOutEvent = new AsyncEvent<Func<Task>>();
  18. internal readonly Logger _restLogger;
  19. private readonly SemaphoreSlim _stateLock;
  20. private bool _isFirstLogin, _isDisposed;
  21. internal API.DiscordRestApiClient ApiClient { get; }
  22. internal LogManager LogManager { get; }
  23. public LoginState LoginState { get; private set; }
  24. public ISelfUser CurrentUser { get; protected set; }
  25. public TokenType TokenType => ApiClient.AuthTokenType;
  26. /// <summary> Creates a new REST-only discord client. </summary>
  27. internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client)
  28. {
  29. ApiClient = client;
  30. LogManager = new LogManager(config.LogLevel);
  31. LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);
  32. _stateLock = new SemaphoreSlim(1, 1);
  33. _restLogger = LogManager.CreateLogger("Rest");
  34. _isFirstLogin = config.DisplayInitialLog;
  35. ApiClient.RequestQueue.RateLimitTriggered += async (id, info) =>
  36. {
  37. if (info == null)
  38. await _restLogger.VerboseAsync($"Preemptive Rate limit triggered: {id ?? "null"}").ConfigureAwait(false);
  39. else
  40. await _restLogger.WarningAsync($"Rate limit triggered: {id ?? "null"}").ConfigureAwait(false);
  41. };
  42. ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false);
  43. }
  44. /// <inheritdoc />
  45. public async Task LoginAsync(TokenType tokenType, string token, bool validateToken = true)
  46. {
  47. await _stateLock.WaitAsync().ConfigureAwait(false);
  48. try
  49. {
  50. await LoginInternalAsync(tokenType, token, validateToken).ConfigureAwait(false);
  51. }
  52. finally { _stateLock.Release(); }
  53. }
  54. private async Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken)
  55. {
  56. if (_isFirstLogin)
  57. {
  58. _isFirstLogin = false;
  59. await LogManager.WriteInitialLog().ConfigureAwait(false);
  60. }
  61. if (LoginState != LoginState.LoggedOut)
  62. await LogoutInternalAsync().ConfigureAwait(false);
  63. LoginState = LoginState.LoggingIn;
  64. try
  65. {
  66. // If token validation is enabled, validate the token and let it throw any ArgumentExceptions
  67. // that result from invalid parameters
  68. if (validateToken)
  69. {
  70. try
  71. {
  72. TokenUtils.ValidateToken(tokenType, token);
  73. }
  74. catch (ArgumentException ex)
  75. {
  76. // log these ArgumentExceptions and allow for the client to attempt to log in anyways
  77. await LogManager.WarningAsync("Discord", "A supplied token was invalid", ex).ConfigureAwait(false);
  78. }
  79. }
  80. await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false);
  81. await OnLoginAsync(tokenType, token).ConfigureAwait(false);
  82. LoginState = LoginState.LoggedIn;
  83. }
  84. catch
  85. {
  86. await LogoutInternalAsync().ConfigureAwait(false);
  87. throw;
  88. }
  89. await _loggedInEvent.InvokeAsync().ConfigureAwait(false);
  90. }
  91. internal virtual Task OnLoginAsync(TokenType tokenType, string token)
  92. => Task.Delay(0);
  93. /// <inheritdoc />
  94. public async Task LogoutAsync()
  95. {
  96. await _stateLock.WaitAsync().ConfigureAwait(false);
  97. try
  98. {
  99. await LogoutInternalAsync().ConfigureAwait(false);
  100. }
  101. finally { _stateLock.Release(); }
  102. }
  103. private async Task LogoutInternalAsync()
  104. {
  105. if (LoginState == LoginState.LoggedOut) return;
  106. LoginState = LoginState.LoggingOut;
  107. await ApiClient.LogoutAsync().ConfigureAwait(false);
  108. await OnLogoutAsync().ConfigureAwait(false);
  109. CurrentUser = null;
  110. LoginState = LoginState.LoggedOut;
  111. await _loggedOutEvent.InvokeAsync().ConfigureAwait(false);
  112. }
  113. internal virtual Task OnLogoutAsync()
  114. => Task.Delay(0);
  115. internal virtual void Dispose(bool disposing)
  116. {
  117. if (!_isDisposed)
  118. {
  119. ApiClient.Dispose();
  120. _isDisposed = true;
  121. }
  122. }
  123. /// <inheritdoc />
  124. public void Dispose() => Dispose(true);
  125. /// <inheritdoc />
  126. public Task<int> GetRecommendedShardCountAsync(RequestOptions options = null)
  127. => ClientHelper.GetRecommendShardCountAsync(this, options);
  128. //IDiscordClient
  129. ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected;
  130. ISelfUser IDiscordClient.CurrentUser => CurrentUser;
  131. Task<IApplication> IDiscordClient.GetApplicationInfoAsync(RequestOptions options)
  132. => throw new NotSupportedException();
  133. Task<IChannel> IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options)
  134. => Task.FromResult<IChannel>(null);
  135. Task<IReadOnlyCollection<IPrivateChannel>> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode, RequestOptions options)
  136. => Task.FromResult<IReadOnlyCollection<IPrivateChannel>>(ImmutableArray.Create<IPrivateChannel>());
  137. Task<IReadOnlyCollection<IDMChannel>> IDiscordClient.GetDMChannelsAsync(CacheMode mode, RequestOptions options)
  138. => Task.FromResult<IReadOnlyCollection<IDMChannel>>(ImmutableArray.Create<IDMChannel>());
  139. Task<IReadOnlyCollection<IGroupChannel>> IDiscordClient.GetGroupChannelsAsync(CacheMode mode, RequestOptions options)
  140. => Task.FromResult<IReadOnlyCollection<IGroupChannel>>(ImmutableArray.Create<IGroupChannel>());
  141. Task<IReadOnlyCollection<IConnection>> IDiscordClient.GetConnectionsAsync(RequestOptions options)
  142. => Task.FromResult<IReadOnlyCollection<IConnection>>(ImmutableArray.Create<IConnection>());
  143. Task<IInvite> IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options)
  144. => Task.FromResult<IInvite>(null);
  145. Task<IGuild> IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options)
  146. => Task.FromResult<IGuild>(null);
  147. Task<IReadOnlyCollection<IGuild>> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options)
  148. => Task.FromResult<IReadOnlyCollection<IGuild>>(ImmutableArray.Create<IGuild>());
  149. Task<IGuild> IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon, RequestOptions options)
  150. => throw new NotSupportedException();
  151. Task<IUser> IDiscordClient.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
  152. => Task.FromResult<IUser>(null);
  153. Task<IUser> IDiscordClient.GetUserAsync(string username, string discriminator, RequestOptions options)
  154. => Task.FromResult<IUser>(null);
  155. Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options)
  156. => Task.FromResult<IReadOnlyCollection<IVoiceRegion>>(ImmutableArray.Create<IVoiceRegion>());
  157. Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
  158. => Task.FromResult<IVoiceRegion>(null);
  159. Task<IWebhook> IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options)
  160. => Task.FromResult<IWebhook>(null);
  161. Task IDiscordClient.StartAsync()
  162. => Task.Delay(0);
  163. Task IDiscordClient.StopAsync()
  164. => Task.Delay(0);
  165. }
  166. }