| @@ -6,6 +6,12 @@ namespace Discord.Rpc | |||||
| public partial class DiscordRpcClient | public partial class DiscordRpcClient | ||||
| { | { | ||||
| //General | //General | ||||
| public event Func<Task> Connecting | |||||
| { | |||||
| add { _connectingEvent.Add(value); } | |||||
| remove { _connectingEvent.Remove(value); } | |||||
| } | |||||
| private readonly AsyncEvent<Func<Task>> _connectingEvent = new AsyncEvent<Func<Task>>(); | |||||
| public event Func<Task> Connected | public event Func<Task> Connected | ||||
| { | { | ||||
| add { _connectedEvent.Add(value); } | add { _connectedEvent.Add(value); } | ||||
| @@ -29,7 +29,7 @@ namespace Discord.Rpc | |||||
| public RestApplication ApplicationInfo { get; private set; } | public RestApplication ApplicationInfo { get; private set; } | ||||
| /// <summary> Creates a new RPC discord client. </summary> | /// <summary> Creates a new RPC discord client. </summary> | ||||
| public DiscordRpcClient(string clientId, string origin) | |||||
| public DiscordRpcClient(string clientId, string origin) | |||||
| : this(clientId, origin, new DiscordRpcConfig()) { } | : this(clientId, origin, new DiscordRpcConfig()) { } | ||||
| /// <summary> Creates a new RPC discord client. </summary> | /// <summary> Creates a new RPC discord client. </summary> | ||||
| public DiscordRpcClient(string clientId, string origin, DiscordRpcConfig config) | public DiscordRpcClient(string clientId, string origin, DiscordRpcConfig config) | ||||
| @@ -38,8 +38,9 @@ namespace Discord.Rpc | |||||
| _stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
| _authorizeLock = new SemaphoreSlim(1, 1); | _authorizeLock = new SemaphoreSlim(1, 1); | ||||
| _rpcLogger = LogManager.CreateLogger("RPC"); | _rpcLogger = LogManager.CreateLogger("RPC"); | ||||
| _connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout, | |||||
| OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); | |||||
| _connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout, | |||||
| ConnectAsync, DisconnectAsync, x => ApiClient.Disconnected += x); | |||||
| _connection.Connecting += () => _connectingEvent.InvokeAsync(); | |||||
| _connection.Connected += () => _connectedEvent.InvokeAsync(); | _connection.Connected += () => _connectedEvent.InvokeAsync(); | ||||
| _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); | _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); | ||||
| @@ -49,7 +50,7 @@ namespace Discord.Rpc | |||||
| _rpcLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); | _rpcLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); | ||||
| e.ErrorContext.Handled = true; | e.ErrorContext.Handled = true; | ||||
| }; | }; | ||||
| ApiClient.SentRpcMessage += async opCode => await _rpcLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ApiClient.SentRpcMessage += async opCode => await _rpcLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ||||
| ApiClient.ReceivedRpcEvent += ProcessMessageAsync; | ApiClient.ReceivedRpcEvent += ProcessMessageAsync; | ||||
| } | } | ||||
| @@ -68,14 +69,14 @@ namespace Discord.Rpc | |||||
| public Task StartAsync() => _connection.StartAsync(); | public Task StartAsync() => _connection.StartAsync(); | ||||
| public Task StopAsync() => _connection.StopAsync(); | public Task StopAsync() => _connection.StopAsync(); | ||||
| private async Task OnConnectingAsync() | |||||
| private async Task ConnectAsync() | |||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); | ||||
| await ApiClient.ConnectAsync().ConfigureAwait(false); | await ApiClient.ConnectAsync().ConfigureAwait(false); | ||||
| await _connection.WaitAsync().ConfigureAwait(false); | await _connection.WaitAsync().ConfigureAwait(false); | ||||
| } | } | ||||
| private async Task OnDisconnectingAsync(Exception ex) | |||||
| private async Task DisconnectAsync(Exception ex) | |||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | ||||
| await ApiClient.DisconnectAsync().ConfigureAwait(false); | await ApiClient.DisconnectAsync().ConfigureAwait(false); | ||||
| @@ -313,7 +314,7 @@ namespace Discord.Rpc | |||||
| ApplicationInfo = RestApplication.Create(this, response.Application); | ApplicationInfo = RestApplication.Create(this, response.Application); | ||||
| Scopes = response.Scopes; | Scopes = response.Scopes; | ||||
| TokenExpiresAt = response.Expires; | TokenExpiresAt = response.Expires; | ||||
| var __ = _connection.CompleteAsync(); | var __ = _connection.CompleteAsync(); | ||||
| await _rpcLogger.InfoAsync("Ready").ConfigureAwait(false); | await _rpcLogger.InfoAsync("Ready").ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -8,6 +8,8 @@ namespace Discord | |||||
| { | { | ||||
| internal class ConnectionManager | internal class ConnectionManager | ||||
| { | { | ||||
| public event Func<Task> Connecting { add { _connectingEvent.Add(value); } remove { _connectingEvent.Remove(value); } } | |||||
| private readonly AsyncEvent<Func<Task>> _connectingEvent = new AsyncEvent<Func<Task>>(); | |||||
| public event Func<Task> Connected { add { _connectedEvent.Add(value); } remove { _connectedEvent.Remove(value); } } | public event Func<Task> Connected { add { _connectedEvent.Add(value); } remove { _connectedEvent.Remove(value); } } | ||||
| private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>(); | private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>(); | ||||
| public event Func<Exception, bool, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } | public event Func<Exception, bool, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } | ||||
| @@ -16,8 +18,8 @@ namespace Discord | |||||
| private readonly SemaphoreSlim _stateLock; | private readonly SemaphoreSlim _stateLock; | ||||
| private readonly Logger _logger; | private readonly Logger _logger; | ||||
| private readonly int _connectionTimeout; | private readonly int _connectionTimeout; | ||||
| private readonly Func<Task> _onConnecting; | |||||
| private readonly Func<Exception, Task> _onDisconnecting; | |||||
| private readonly Func<Task> _connect; | |||||
| private readonly Func<Exception, Task> _disconnect; | |||||
| private TaskCompletionSource<bool> _connectionPromise, _readyPromise; | private TaskCompletionSource<bool> _connectionPromise, _readyPromise; | ||||
| private CancellationTokenSource _combinedCancelToken, _reconnectCancelToken, _connectionCancelToken; | private CancellationTokenSource _combinedCancelToken, _reconnectCancelToken, _connectionCancelToken; | ||||
| @@ -26,14 +28,14 @@ namespace Discord | |||||
| public ConnectionState State { get; private set; } | public ConnectionState State { get; private set; } | ||||
| public CancellationToken CancelToken { get; private set; } | public CancellationToken CancelToken { get; private set; } | ||||
| internal ConnectionManager(SemaphoreSlim stateLock, Logger logger, int connectionTimeout, | |||||
| Func<Task> onConnecting, Func<Exception, Task> onDisconnecting, Action<Func<Exception, Task>> clientDisconnectHandler) | |||||
| internal ConnectionManager(SemaphoreSlim stateLock, Logger logger, int connectionTimeout, | |||||
| Func<Task> connect, Func<Exception, Task> disconnect, Action<Func<Exception, Task>> clientDisconnectHandler) | |||||
| { | { | ||||
| _stateLock = stateLock; | _stateLock = stateLock; | ||||
| _logger = logger; | _logger = logger; | ||||
| _connectionTimeout = connectionTimeout; | _connectionTimeout = connectionTimeout; | ||||
| _onConnecting = onConnecting; | |||||
| _onDisconnecting = onDisconnecting; | |||||
| _connect = connect; | |||||
| _disconnect = disconnect; | |||||
| clientDisconnectHandler(ex => | clientDisconnectHandler(ex => | ||||
| { | { | ||||
| @@ -67,16 +69,16 @@ namespace Discord | |||||
| try | try | ||||
| { | { | ||||
| await ConnectAsync(reconnectCancelToken).ConfigureAwait(false); | await ConnectAsync(reconnectCancelToken).ConfigureAwait(false); | ||||
| nextReconnectDelay = 1000; //Reset delay | |||||
| nextReconnectDelay = 1000; //Reset delay | |||||
| await _connectionPromise.Task.ConfigureAwait(false); | await _connectionPromise.Task.ConfigureAwait(false); | ||||
| } | } | ||||
| catch (OperationCanceledException ex) | |||||
| { | |||||
| catch (OperationCanceledException ex) | |||||
| { | |||||
| Cancel(); //In case this exception didn't come from another Error call | Cancel(); //In case this exception didn't come from another Error call | ||||
| await DisconnectAsync(ex, !reconnectCancelToken.IsCancellationRequested).ConfigureAwait(false); | await DisconnectAsync(ex, !reconnectCancelToken.IsCancellationRequested).ConfigureAwait(false); | ||||
| } | } | ||||
| catch (Exception ex) | |||||
| { | |||||
| catch (Exception ex) | |||||
| { | |||||
| Error(ex); //In case this exception didn't come from another Error call | Error(ex); //In case this exception didn't come from another Error call | ||||
| if (!reconnectCancelToken.IsCancellationRequested) | if (!reconnectCancelToken.IsCancellationRequested) | ||||
| { | { | ||||
| @@ -120,9 +122,11 @@ namespace Discord | |||||
| _connectionPromise = new TaskCompletionSource<bool>(); | _connectionPromise = new TaskCompletionSource<bool>(); | ||||
| State = ConnectionState.Connecting; | State = ConnectionState.Connecting; | ||||
| await _logger.InfoAsync("Connecting").ConfigureAwait(false); | await _logger.InfoAsync("Connecting").ConfigureAwait(false); | ||||
| try | try | ||||
| { | { | ||||
| await _connectingEvent.InvokeAsync().ConfigureAwait(false); | |||||
| var readyPromise = new TaskCompletionSource<bool>(); | var readyPromise = new TaskCompletionSource<bool>(); | ||||
| _readyPromise = readyPromise; | _readyPromise = readyPromise; | ||||
| @@ -138,7 +142,7 @@ namespace Discord | |||||
| catch (OperationCanceledException) { } | catch (OperationCanceledException) { } | ||||
| }); | }); | ||||
| await _onConnecting().ConfigureAwait(false); | |||||
| await _connect().ConfigureAwait(false); | |||||
| await _logger.InfoAsync("Connected").ConfigureAwait(false); | await _logger.InfoAsync("Connected").ConfigureAwait(false); | ||||
| State = ConnectionState.Connected; | State = ConnectionState.Connected; | ||||
| @@ -157,7 +161,7 @@ namespace Discord | |||||
| State = ConnectionState.Disconnecting; | State = ConnectionState.Disconnecting; | ||||
| await _logger.InfoAsync("Disconnecting").ConfigureAwait(false); | await _logger.InfoAsync("Disconnecting").ConfigureAwait(false); | ||||
| await _onDisconnecting(ex).ConfigureAwait(false); | |||||
| await _disconnect(ex).ConfigureAwait(false); | |||||
| await _logger.InfoAsync("Disconnected").ConfigureAwait(false); | await _logger.InfoAsync("Disconnected").ConfigureAwait(false); | ||||
| State = ConnectionState.Disconnected; | State = ConnectionState.Disconnected; | ||||
| @@ -207,4 +211,4 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -7,6 +7,12 @@ namespace Discord.WebSocket | |||||
| public partial class DiscordSocketClient | public partial class DiscordSocketClient | ||||
| { | { | ||||
| //General | //General | ||||
| public event Func<Task> Connecting | |||||
| { | |||||
| add { _connectingEvent.Add(value); } | |||||
| remove { _connectingEvent.Remove(value); } | |||||
| } | |||||
| private readonly AsyncEvent<Func<Task>> _connectingEvent = new AsyncEvent<Func<Task>>(); | |||||
| public event Func<Task> Connected | public event Func<Task> Connected | ||||
| { | { | ||||
| add { _connectedEvent.Add(value); } | add { _connectedEvent.Add(value); } | ||||
| @@ -63,9 +63,9 @@ namespace Discord.WebSocket | |||||
| public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; private set => base.CurrentUser = value; } | public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; private set => base.CurrentUser = value; } | ||||
| public IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; | public IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; | ||||
| public IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels; | public IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels; | ||||
| public IReadOnlyCollection<SocketDMChannel> DMChannels | |||||
| public IReadOnlyCollection<SocketDMChannel> DMChannels | |||||
| => State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); | => State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); | ||||
| public IReadOnlyCollection<SocketGroupChannel> GroupChannels | |||||
| public IReadOnlyCollection<SocketGroupChannel> GroupChannels | |||||
| => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); | => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); | ||||
| public IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | public IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | ||||
| @@ -90,11 +90,12 @@ namespace Discord.WebSocket | |||||
| _stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
| _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); | _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); | ||||
| _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, | |||||
| OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); | |||||
| _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, | |||||
| ConnectAsync, DisconnectAsync, x => ApiClient.Disconnected += x); | |||||
| _connection.Connecting += () => TimedInvokeAsync(_connectingEvent, nameof(Connecting)); | |||||
| _connection.Connected += () => TimedInvokeAsync(_connectedEvent, nameof(Connected)); | _connection.Connected += () => TimedInvokeAsync(_connectedEvent, nameof(Connected)); | ||||
| _connection.Disconnected += (ex, recon) => TimedInvokeAsync(_disconnectedEvent, nameof(Disconnected), ex); | _connection.Disconnected += (ex, recon) => TimedInvokeAsync(_disconnectedEvent, nameof(Disconnected), ex); | ||||
| _nextAudioId = 1; | _nextAudioId = 1; | ||||
| _connectionGroupLock = groupLock; | _connectionGroupLock = groupLock; | ||||
| _parentClient = parentClient; | _parentClient = parentClient; | ||||
| @@ -105,7 +106,7 @@ namespace Discord.WebSocket | |||||
| _gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult(); | _gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult(); | ||||
| e.ErrorContext.Handled = true; | e.ErrorContext.Handled = true; | ||||
| }; | }; | ||||
| ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ||||
| ApiClient.ReceivedGatewayEvent += ProcessMessageAsync; | ApiClient.ReceivedGatewayEvent += ProcessMessageAsync; | ||||
| @@ -137,7 +138,7 @@ namespace Discord.WebSocket | |||||
| ApiClient.Dispose(); | ApiClient.Dispose(); | ||||
| } | } | ||||
| } | } | ||||
| internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
| { | { | ||||
| if (_parentClient == null) | if (_parentClient == null) | ||||
| @@ -155,12 +156,12 @@ namespace Discord.WebSocket | |||||
| _voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>(); | _voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>(); | ||||
| } | } | ||||
| public async Task StartAsync() | |||||
| public async Task StartAsync() | |||||
| => await _connection.StartAsync().ConfigureAwait(false); | => await _connection.StartAsync().ConfigureAwait(false); | ||||
| public async Task StopAsync() | |||||
| public async Task StopAsync() | |||||
| => await _connection.StopAsync().ConfigureAwait(false); | => await _connection.StopAsync().ConfigureAwait(false); | ||||
| private async Task OnConnectingAsync() | |||||
| private async Task ConnectAsync() | |||||
| { | { | ||||
| if (_connectionGroupLock != null) | if (_connectionGroupLock != null) | ||||
| await _connectionGroupLock.WaitAsync(_connection.CancelToken).ConfigureAwait(false); | await _connectionGroupLock.WaitAsync(_connection.CancelToken).ConfigureAwait(false); | ||||
| @@ -182,11 +183,11 @@ namespace Discord.WebSocket | |||||
| //Wait for READY | //Wait for READY | ||||
| await _connection.WaitAsync().ConfigureAwait(false); | await _connection.WaitAsync().ConfigureAwait(false); | ||||
| await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); | ||||
| await SendStatusAsync().ConfigureAwait(false); | await SendStatusAsync().ConfigureAwait(false); | ||||
| } | } | ||||
| finally | |||||
| finally | |||||
| { | { | ||||
| if (_connectionGroupLock != null) | if (_connectionGroupLock != null) | ||||
| { | { | ||||
| @@ -195,7 +196,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| private async Task OnDisconnectingAsync(Exception ex) | |||||
| private async Task DisconnectAsync(Exception ex) | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | ||||
| @@ -232,7 +233,7 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public async Task<RestApplication> GetApplicationInfoAsync() | public async Task<RestApplication> GetApplicationInfoAsync() | ||||
| { | |||||
| { | |||||
| return _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this, new RequestOptions())); | return _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this, new RequestOptions())); | ||||
| } | } | ||||
| @@ -391,7 +392,7 @@ namespace Discord.WebSocket | |||||
| if (seq != null) | if (seq != null) | ||||
| _lastSeq = seq.Value; | _lastSeq = seq.Value; | ||||
| _lastMessageTime = Environment.TickCount; | _lastMessageTime = Environment.TickCount; | ||||
| try | try | ||||
| { | { | ||||
| switch (opCode) | switch (opCode) | ||||
| @@ -407,7 +408,7 @@ namespace Discord.WebSocket | |||||
| case GatewayOpCode.Heartbeat: | case GatewayOpCode.Heartbeat: | ||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Received Heartbeat").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Heartbeat").ConfigureAwait(false); | ||||
| await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); | await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -498,7 +499,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| else if (_connection.CancelToken.IsCancellationRequested) | else if (_connection.CancelToken.IsCancellationRequested) | ||||
| return; | return; | ||||
| await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); | await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); | ||||
| await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); | await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); | ||||
| }); | }); | ||||
| @@ -537,7 +538,7 @@ namespace Discord.WebSocket | |||||
| if (guild != null) | if (guild != null) | ||||
| { | { | ||||
| guild.Update(State, data); | guild.Update(State, data); | ||||
| if (_unavailableGuildCount != 0) | if (_unavailableGuildCount != 0) | ||||
| _unavailableGuildCount--; | _unavailableGuildCount--; | ||||
| await GuildAvailableAsync(guild).ConfigureAwait(false); | await GuildAvailableAsync(guild).ConfigureAwait(false); | ||||
| @@ -1050,7 +1051,7 @@ namespace Discord.WebSocket | |||||
| SocketUser user = guild.GetUser(data.User.Id); | SocketUser user = guild.GetUser(data.User.Id); | ||||
| if (user == null) | if (user == null) | ||||
| user = SocketUnknownUser.Create(this, State, data.User); | |||||
| user = SocketUnknownUser.Create(this, State, data.User); | |||||
| await TimedInvokeAsync(_userBannedEvent, nameof(UserBanned), user, guild).ConfigureAwait(false); | await TimedInvokeAsync(_userBannedEvent, nameof(UserBanned), user, guild).ConfigureAwait(false); | ||||
| } | } | ||||
| else | else | ||||
| @@ -1348,7 +1349,7 @@ namespace Discord.WebSocket | |||||
| await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), globalBefore, user).ConfigureAwait(false); | await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), globalBefore, user).ConfigureAwait(false); | ||||
| } | } | ||||
| } | } | ||||
| var before = user.Clone(); | var before = user.Clone(); | ||||
| user.Update(State, data, true); | user.Update(State, data, true); | ||||
| await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false); | await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false); | ||||