| @@ -0,0 +1,190 @@ | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| public partial class DiscordSocketClient | |||
| { | |||
| //General | |||
| public event Func<Task> Connected | |||
| { | |||
| add { _connectedEvent.Add(value); } | |||
| remove { _connectedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>(); | |||
| public event Func<Task> Disconnected | |||
| { | |||
| add { _disconnectedEvent.Add(value); } | |||
| remove { _disconnectedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<Task>> _disconnectedEvent = new AsyncEvent<Func<Task>>(); | |||
| public event Func<Task> Ready | |||
| { | |||
| add { _readyEvent.Add(value); } | |||
| remove { _readyEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<Task>> _readyEvent = new AsyncEvent<Func<Task>>(); | |||
| public event Func<int, int, Task> LatencyUpdated | |||
| { | |||
| add { _latencyUpdatedEvent.Add(value); } | |||
| remove { _latencyUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>(); | |||
| //Channels | |||
| public event Func<IChannel, Task> ChannelCreated | |||
| { | |||
| add { _channelCreatedEvent.Add(value); } | |||
| remove { _channelCreatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IChannel, Task>> _channelCreatedEvent = new AsyncEvent<Func<IChannel, Task>>(); | |||
| public event Func<IChannel, Task> ChannelDestroyed | |||
| { | |||
| add { _channelDestroyedEvent.Add(value); } | |||
| remove { _channelDestroyedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IChannel, Task>> _channelDestroyedEvent = new AsyncEvent<Func<IChannel, Task>>(); | |||
| public event Func<IChannel, IChannel, Task> ChannelUpdated | |||
| { | |||
| add { _channelUpdatedEvent.Add(value); } | |||
| remove { _channelUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IChannel, IChannel, Task>> _channelUpdatedEvent = new AsyncEvent<Func<IChannel, IChannel, Task>>(); | |||
| //Messages | |||
| public event Func<IMessage, Task> MessageReceived | |||
| { | |||
| add { _messageReceivedEvent.Add(value); } | |||
| remove { _messageReceivedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IMessage, Task>> _messageReceivedEvent = new AsyncEvent<Func<IMessage, Task>>(); | |||
| public event Func<ulong, Optional<IMessage>, Task> MessageDeleted | |||
| { | |||
| add { _messageDeletedEvent.Add(value); } | |||
| remove { _messageDeletedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<ulong, Optional<IMessage>, Task>> _messageDeletedEvent = new AsyncEvent<Func<ulong, Optional<IMessage>, Task>>(); | |||
| public event Func<Optional<IMessage>, IMessage, Task> MessageUpdated | |||
| { | |||
| add { _messageUpdatedEvent.Add(value); } | |||
| remove { _messageUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<Optional<IMessage>, IMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<IMessage>, IMessage, Task>>(); | |||
| //Roles | |||
| public event Func<IRole, Task> RoleCreated | |||
| { | |||
| add { _roleCreatedEvent.Add(value); } | |||
| remove { _roleCreatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IRole, Task>> _roleCreatedEvent = new AsyncEvent<Func<IRole, Task>>(); | |||
| public event Func<IRole, Task> RoleDeleted | |||
| { | |||
| add { _roleDeletedEvent.Add(value); } | |||
| remove { _roleDeletedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IRole, Task>> _roleDeletedEvent = new AsyncEvent<Func<IRole, Task>>(); | |||
| public event Func<IRole, IRole, Task> RoleUpdated | |||
| { | |||
| add { _roleUpdatedEvent.Add(value); } | |||
| remove { _roleUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IRole, IRole, Task>> _roleUpdatedEvent = new AsyncEvent<Func<IRole, IRole, Task>>(); | |||
| //Guilds | |||
| public event Func<IGuild, Task> JoinedGuild | |||
| { | |||
| add { _joinedGuildEvent.Add(value); } | |||
| remove { _joinedGuildEvent.Remove(value); } | |||
| } | |||
| private AsyncEvent<Func<IGuild, Task>> _joinedGuildEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> LeftGuild | |||
| { | |||
| add { _leftGuildEvent.Add(value); } | |||
| remove { _leftGuildEvent.Remove(value); } | |||
| } | |||
| private AsyncEvent<Func<IGuild, Task>> _leftGuildEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> GuildAvailable | |||
| { | |||
| add { _guildAvailableEvent.Add(value); } | |||
| remove { _guildAvailableEvent.Remove(value); } | |||
| } | |||
| private AsyncEvent<Func<IGuild, Task>> _guildAvailableEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> GuildUnavailable | |||
| { | |||
| add { _guildUnavailableEvent.Add(value); } | |||
| remove { _guildUnavailableEvent.Remove(value); } | |||
| } | |||
| private AsyncEvent<Func<IGuild, Task>> _guildUnavailableEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> GuildDownloadedMembers | |||
| { | |||
| add { _guildDownloadedMembersEvent.Add(value); } | |||
| remove { _guildDownloadedMembersEvent.Remove(value); } | |||
| } | |||
| private AsyncEvent<Func<IGuild, Task>> _guildDownloadedMembersEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, IGuild, Task> GuildUpdated | |||
| { | |||
| add { _guildUpdatedEvent.Add(value); } | |||
| remove { _guildUpdatedEvent.Remove(value); } | |||
| } | |||
| private AsyncEvent<Func<IGuild, IGuild, Task>> _guildUpdatedEvent = new AsyncEvent<Func<IGuild, IGuild, Task>>(); | |||
| //Users | |||
| public event Func<IGuildUser, Task> UserJoined | |||
| { | |||
| add { _userJoinedEvent.Add(value); } | |||
| remove { _userJoinedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IGuildUser, Task>> _userJoinedEvent = new AsyncEvent<Func<IGuildUser, Task>>(); | |||
| public event Func<IGuildUser, Task> UserLeft | |||
| { | |||
| add { _userLeftEvent.Add(value); } | |||
| remove { _userLeftEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IGuildUser, Task>> _userLeftEvent = new AsyncEvent<Func<IGuildUser, Task>>(); | |||
| public event Func<IUser, IGuild, Task> UserBanned | |||
| { | |||
| add { _userBannedEvent.Add(value); } | |||
| remove { _userBannedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userBannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>(); | |||
| public event Func<IUser, IGuild, Task> UserUnbanned | |||
| { | |||
| add { _userUnbannedEvent.Add(value); } | |||
| remove { _userUnbannedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userUnbannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>(); | |||
| public event Func<IGuildUser, IGuildUser, Task> UserUpdated | |||
| { | |||
| add { _userUpdatedEvent.Add(value); } | |||
| remove { _userUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IGuildUser, IGuildUser, Task>> _userUpdatedEvent = new AsyncEvent<Func<IGuildUser, IGuildUser, Task>>(); | |||
| public event Func<IGuildUser, IPresence, IPresence, Task> UserPresenceUpdated | |||
| { | |||
| add { _userPresenceUpdatedEvent.Add(value); } | |||
| remove { _userPresenceUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IGuildUser, IPresence, IPresence, Task>> _userPresenceUpdatedEvent = new AsyncEvent<Func<IGuildUser, IPresence, IPresence, Task>>(); | |||
| public event Func<IGuildUser, IVoiceState, IVoiceState, Task> UserVoiceStateUpdated | |||
| { | |||
| add { _userVoiceStateUpdatedEvent.Add(value); } | |||
| remove { _userVoiceStateUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IGuildUser, IVoiceState, IVoiceState, Task>> _userVoiceStateUpdatedEvent = new AsyncEvent<Func<IGuildUser, IVoiceState, IVoiceState, Task>>(); | |||
| public event Func<ISelfUser, ISelfUser, Task> CurrentUserUpdated | |||
| { | |||
| add { _selfUpdatedEvent.Add(value); } | |||
| remove { _selfUpdatedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<ISelfUser, ISelfUser, Task>> _selfUpdatedEvent = new AsyncEvent<Func<ISelfUser, ISelfUser, Task>>(); | |||
| public event Func<IUser, IChannel, Task> UserIsTyping | |||
| { | |||
| add { _userIsTypingEvent.Add(value); } | |||
| remove { _userIsTypingEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<IUser, IChannel, Task>> _userIsTypingEvent = new AsyncEvent<Func<IUser, IChannel, Task>>(); | |||
| //TODO: Add PresenceUpdated? VoiceStateUpdated?, VoiceConnected, VoiceDisconnected; | |||
| } | |||
| } | |||
| @@ -19,77 +19,8 @@ namespace Discord | |||
| //TODO: Add event docstrings | |||
| //TODO: Add reconnect logic (+ensure the heartbeat task to shut down) | |||
| //TODO: Add resume logic | |||
| public class DiscordSocketClient : DiscordClient, IDiscordClient | |||
| public partial class DiscordSocketClient : DiscordClient, IDiscordClient | |||
| { | |||
| private object _eventLock = new object(); | |||
| //General | |||
| public event Func<Task> Connected { add { _connectedEvent.Add(value); } remove { _connectedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>(); | |||
| public event Func<Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<Task>> _disconnectedEvent = new AsyncEvent<Func<Task>>(); | |||
| public event Func<Task> Ready { add { _readyEvent.Add(value); } remove { _readyEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<Task>> _readyEvent = new AsyncEvent<Func<Task>>(); | |||
| public event Func<int, int, Task> LatencyUpdated { add { _latencyUpdatedEvent.Add(value); } remove { _latencyUpdatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>(); | |||
| //Channels | |||
| public event Func<IChannel, Task> ChannelCreated { add { _channelCreatedEvent.Add(value); } remove { _channelCreatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IChannel, Task>> _channelCreatedEvent = new AsyncEvent<Func<IChannel, Task>>(); | |||
| public event Func<IChannel, Task> ChannelDestroyed { add { _channelDestroyedEvent.Add(value); } remove { _channelDestroyedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IChannel, Task>> _channelDestroyedEvent = new AsyncEvent<Func<IChannel, Task>>(); | |||
| public event Func<IChannel, IChannel, Task> ChannelUpdated { add { _channelUpdatedEvent.Add(value); } remove { _channelUpdatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IChannel, IChannel, Task>> _channelUpdatedEvent = new AsyncEvent<Func<IChannel, IChannel, Task>>(); | |||
| //Messages | |||
| public event Func<IMessage, Task> MessageReceived { add { _messageReceivedEvent.Add(value); } remove { _messageReceivedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IMessage, Task>> _messageReceivedEvent = new AsyncEvent<Func<IMessage, Task>>(); | |||
| public event Func<ulong, Optional<IMessage>, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<ulong, Optional<IMessage>, Task>> _messageDeletedEvent = new AsyncEvent<Func<ulong, Optional<IMessage>, Task>>(); | |||
| public event Func<Optional<IMessage>, IMessage, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<Optional<IMessage>, IMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<IMessage>, IMessage, Task>>(); | |||
| //Roles | |||
| public event Func<IRole, Task> RoleCreated { add { _roleCreatedEvent.Add(value); } remove { _roleCreatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IRole, Task>> _roleCreatedEvent = new AsyncEvent<Func<IRole, Task>>(); | |||
| public event Func<IRole, Task> RoleDeleted { add { _roleDeletedEvent.Add(value); } remove { _roleDeletedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IRole, Task>> _roleDeletedEvent = new AsyncEvent<Func<IRole, Task>>(); | |||
| public event Func<IRole, IRole, Task> RoleUpdated { add { _roleUpdatedEvent.Add(value); } remove { _roleUpdatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IRole, IRole, Task>> _roleUpdatedEvent = new AsyncEvent<Func<IRole, IRole, Task>>(); | |||
| //Guilds | |||
| public event Func<IGuild, Task> JoinedGuild { add { _joinedGuildEvent.Add(value); } remove { _joinedGuildEvent.Remove(value); } } | |||
| private AsyncEvent<Func<IGuild, Task>> _joinedGuildEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> LeftGuild { add { _leftGuildEvent.Add(value); } remove { _leftGuildEvent.Remove(value); } } | |||
| private AsyncEvent<Func<IGuild, Task>> _leftGuildEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> GuildAvailable { add { _guildAvailableEvent.Add(value); } remove { _guildAvailableEvent.Remove(value); } } | |||
| private AsyncEvent<Func<IGuild, Task>> _guildAvailableEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> GuildUnavailable { add { _guildUnavailableEvent.Add(value); } remove { _guildUnavailableEvent.Remove(value); } } | |||
| private AsyncEvent<Func<IGuild, Task>> _guildUnavailableEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, Task> GuildDownloadedMembers { add { _guildDownloadedMembersEvent.Add(value); } remove { _guildDownloadedMembersEvent.Remove(value); } } | |||
| private AsyncEvent<Func<IGuild, Task>> _guildDownloadedMembersEvent = new AsyncEvent<Func<IGuild, Task>>(); | |||
| public event Func<IGuild, IGuild, Task> GuildUpdated { add { _guildUpdatedEvent.Add(value); } remove { _guildUpdatedEvent.Remove(value); } } | |||
| private AsyncEvent<Func<IGuild, IGuild, Task>> _guildUpdatedEvent = new AsyncEvent<Func<IGuild, IGuild, Task>>(); | |||
| //Users | |||
| public event Func<IGuildUser, Task> UserJoined { add { _userJoinedEvent.Add(value); } remove { _userJoinedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IGuildUser, Task>> _userJoinedEvent = new AsyncEvent<Func<IGuildUser, Task>>(); | |||
| public event Func<IGuildUser, Task> UserLeft { add { _userLeftEvent.Add(value); } remove { _userLeftEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IGuildUser, Task>> _userLeftEvent = new AsyncEvent<Func<IGuildUser, Task>>(); | |||
| public event Func<IUser, IGuild, Task> UserBanned { add { _userBannedEvent.Add(value); } remove { _userBannedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userBannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>(); | |||
| public event Func<IUser, IGuild, Task> UserUnbanned { add { _userUnbannedEvent.Add(value); } remove { _userUnbannedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userUnbannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>(); | |||
| public event Func<Optional<IGuildUser>, IGuildUser, Task> UserUpdated { add { _userUpdatedEvent.Add(value); } remove { _userUpdatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<Optional<IGuildUser>, IGuildUser, Task>> _userUpdatedEvent = new AsyncEvent<Func<Optional<IGuildUser>, IGuildUser, Task>>(); | |||
| public event Func<ISelfUser, ISelfUser, Task> CurrentUserUpdated { add { _selfUpdatedEvent.Add(value); } remove { _selfUpdatedEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<ISelfUser, ISelfUser, Task>> _selfUpdatedEvent = new AsyncEvent<Func<ISelfUser, ISelfUser, Task>>(); | |||
| public event Func<IUser, IChannel, Task> UserIsTyping { add { _userIsTypingEvent.Add(value); } remove { _userIsTypingEvent.Remove(value); } } | |||
| private readonly AsyncEvent<Func<IUser, IChannel, Task>> _userIsTypingEvent = new AsyncEvent<Func<IUser, IChannel, Task>>(); | |||
| //TODO: Add PresenceUpdated? VoiceStateUpdated?, VoiceConnected, VoiceDisconnected; | |||
| private readonly ConcurrentQueue<ulong> _largeGuilds; | |||
| private readonly Logger _gatewayLogger; | |||
| #if BENCHMARK | |||
| @@ -1066,19 +997,20 @@ namespace Discord | |||
| break; | |||
| } | |||
| IPresence before; | |||
| var user = guild.GetUser(data.User.Id); | |||
| if (user != null) | |||
| { | |||
| var before = user.Clone(); | |||
| before = user.Presence.Clone(); | |||
| user.Update(data, UpdateSource.WebSocket); | |||
| await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false); | |||
| } | |||
| else | |||
| { | |||
| before = new Presence(null, UserStatus.Offline); | |||
| user = guild.AddOrUpdateUser(data, DataStore); | |||
| user.Update(data, UpdateSource.WebSocket); | |||
| await _userUpdatedEvent.InvokeAsync(Optional.Create<IGuildUser>(), user).ConfigureAwait(false); | |||
| } | |||
| await _userPresenceUpdatedEvent.InvokeAsync(user, before, user).ConfigureAwait(false); | |||
| } | |||
| else | |||
| { | |||
| @@ -1103,6 +1035,26 @@ namespace Discord | |||
| } | |||
| break; | |||
| //Users | |||
| case "USER_UPDATE": | |||
| { | |||
| await _gatewayLogger.DebugAsync("Received Dispatch (USER_UPDATE)").ConfigureAwait(false); | |||
| var data = (payload as JToken).ToObject<API.User>(_serializer); | |||
| if (data.Id == CurrentUser.Id) | |||
| { | |||
| var before = CurrentUser.Clone(); | |||
| CurrentUser.Update(data, UpdateSource.WebSocket); | |||
| await _selfUpdatedEvent.InvokeAsync(before, CurrentUser).ConfigureAwait(false); | |||
| } | |||
| else | |||
| { | |||
| await _gatewayLogger.WarningAsync("Received USER_UPDATE for wrong user.").ConfigureAwait(false); | |||
| return; | |||
| } | |||
| } | |||
| break; | |||
| //Voice | |||
| case "VOICE_STATE_UPDATE": | |||
| { | |||
| @@ -1114,17 +1066,27 @@ namespace Discord | |||
| var guild = DataStore.GetGuild(data.GuildId.Value); | |||
| if (guild != null) | |||
| { | |||
| if (data.ChannelId == null) | |||
| guild.RemoveVoiceState(data.UserId); | |||
| VoiceState before, after; | |||
| if (data.ChannelId != null) | |||
| { | |||
| before = guild.GetVoiceState(data.UserId)?.Clone() ?? new VoiceState(null, null, false, false, false); | |||
| after = guild.AddOrUpdateVoiceState(data, DataStore); | |||
| } | |||
| else | |||
| guild.AddOrUpdateVoiceState(data, DataStore); | |||
| { | |||
| before = guild.RemoveVoiceState(data.UserId) ?? new VoiceState(null, null, false, false, false); | |||
| after = new VoiceState(null, data); | |||
| } | |||
| var user = guild.GetUser(data.UserId); | |||
| if (user != null) | |||
| { | |||
| var before = user.Clone(); | |||
| user.Update(data, UpdateSource.WebSocket); | |||
| await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false); | |||
| await _userVoiceStateUpdatedEvent.InvokeAsync(user, before, after).ConfigureAwait(false); | |||
| } | |||
| else | |||
| { | |||
| await _gatewayLogger.WarningAsync("VOICE_STATE_UPDATE referenced an unknown user.").ConfigureAwait(false); | |||
| return; | |||
| } | |||
| } | |||
| else | |||
| @@ -1136,26 +1098,6 @@ namespace Discord | |||
| } | |||
| break; | |||
| //Settings | |||
| case "USER_UPDATE": | |||
| { | |||
| await _gatewayLogger.DebugAsync("Received Dispatch (USER_UPDATE)").ConfigureAwait(false); | |||
| var data = (payload as JToken).ToObject<API.User>(_serializer); | |||
| if (data.Id == CurrentUser.Id) | |||
| { | |||
| var before = CurrentUser.Clone(); | |||
| CurrentUser.Update(data, UpdateSource.WebSocket); | |||
| await _selfUpdatedEvent.InvokeAsync(before, CurrentUser).ConfigureAwait(false); | |||
| } | |||
| else | |||
| { | |||
| await _gatewayLogger.WarningAsync("Received USER_UPDATE for wrong user.").ConfigureAwait(false); | |||
| return; | |||
| } | |||
| } | |||
| break; | |||
| //Ignored | |||
| case "USER_SETTINGS_UPDATE": | |||
| await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false); | |||
| @@ -15,9 +15,7 @@ namespace Discord | |||
| internal class GuildUser : IGuildUser, ISnowflakeEntity | |||
| { | |||
| private long? _joinedAtTicks; | |||
| public bool IsDeaf { get; private set; } | |||
| public bool IsMute { get; private set; } | |||
| public string Nickname { get; private set; } | |||
| public GuildPermissions GuildPermissions { get; private set; } | |||
| @@ -59,11 +57,7 @@ namespace Discord | |||
| public void Update(Model model, UpdateSource source) | |||
| { | |||
| if (source == UpdateSource.Rest && IsAttached) return; | |||
| //if (model.Deaf.IsSpecified) | |||
| IsDeaf = model.Deaf; | |||
| //if (model.Mute.IsSpecified) | |||
| IsMute = model.Mute; | |||
| //if (model.JoinedAt.IsSpecified) | |||
| _joinedAtTicks = model.JoinedAt.UtcTicks; | |||
| if (model.Nick.IsSpecified) | |||
| @@ -81,13 +75,6 @@ namespace Discord | |||
| if (model.Nick.IsSpecified) | |||
| Nickname = model.Nick.Value; | |||
| } | |||
| public void Update(VoiceStateModel model, UpdateSource source) | |||
| { | |||
| if (source == UpdateSource.Rest && IsAttached) return; | |||
| IsDeaf = model.Deaf; | |||
| IsMute = model.Mute; | |||
| } | |||
| private void UpdateRoles(ulong[] roleIds) | |||
| { | |||
| var roles = ImmutableArray.CreateBuilder<Role>(roleIds.Length + 1); | |||
| @@ -127,10 +114,6 @@ namespace Discord | |||
| if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) | |||
| { | |||
| await Discord.ApiClient.ModifyGuildMemberAsync(Guild.Id, Id, args).ConfigureAwait(false); | |||
| if (args.Deaf.IsSpecified) | |||
| IsDeaf = args.Deaf.Value; | |||
| if (args.Mute.IsSpecified) | |||
| IsMute = args.Mute.Value; | |||
| if (args.Nickname.IsSpecified) | |||
| Nickname = args.Nickname.Value ?? ""; | |||
| if (args.RoleIds.IsSpecified) | |||
| @@ -161,6 +144,8 @@ namespace Discord | |||
| IGuild IGuildUser.Guild => Guild; | |||
| IReadOnlyCollection<IRole> IGuildUser.Roles => Roles; | |||
| bool IVoiceState.IsDeafened => false; | |||
| bool IVoiceState.IsMuted => false; | |||
| bool IVoiceState.IsSelfDeafened => false; | |||
| bool IVoiceState.IsSelfMuted => false; | |||
| bool IVoiceState.IsSuppressed => false; | |||
| @@ -8,10 +8,6 @@ namespace Discord | |||
| /// <summary> A Guild-User pairing. </summary> | |||
| public interface IGuildUser : IUpdateable, IUser, IVoiceState | |||
| { | |||
| /// <summary> Returns true if the guild has deafened this user. </summary> | |||
| bool IsDeaf { get; } | |||
| /// <summary> Returns true if the guild has muted this user. </summary> | |||
| bool IsMute { get; } | |||
| /// <summary> Gets when this user joined this guild. </summary> | |||
| DateTimeOffset? JoinedAt { get; } | |||
| /// <summary> Gets the nickname for this user. </summary> | |||
| @@ -2,6 +2,10 @@ | |||
| { | |||
| public interface IVoiceState | |||
| { | |||
| /// <summary> Returns true if the guild has deafened this user. </summary> | |||
| bool IsDeafened { get; } | |||
| /// <summary> Returns true if the guild has muted this user. </summary> | |||
| bool IsMuted { get; } | |||
| /// <summary> Returns true if this user has marked themselves as deafened. </summary> | |||
| bool IsSelfDeafened { get; } | |||
| /// <summary> Returns true if this user has marked themselves as muted. </summary> | |||
| @@ -204,7 +204,7 @@ namespace Discord | |||
| public VoiceState AddOrUpdateVoiceState(VoiceStateModel model, DataStore dataStore, ConcurrentDictionary<ulong, VoiceState> voiceStates = null) | |||
| { | |||
| var voiceChannel = dataStore.GetChannel(model.ChannelId.Value) as CachedVoiceChannel; | |||
| var voiceState = new VoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Suppress); | |||
| var voiceState = new VoiceState(voiceChannel, model); | |||
| (voiceStates ?? _voiceStates)[model.UserId] = voiceState; | |||
| return voiceState; | |||
| } | |||
| @@ -3,17 +3,31 @@ using PresenceModel = Discord.API.Presence; | |||
| namespace Discord | |||
| { | |||
| //TODO: C#7 Candidate for record type | |||
| internal struct Presence : IPresence | |||
| { | |||
| public Game Game { get; } | |||
| public UserStatus Status { get; } | |||
| public Presence(Game game, UserStatus status) | |||
| { | |||
| Game = game; | |||
| Status = status; | |||
| } | |||
| public Presence Clone() => this; | |||
| } | |||
| internal class CachedGuildUser : GuildUser, ICachedUser | |||
| { | |||
| private Game _game; | |||
| private UserStatus _status; | |||
| public Presence Presence { get; private set; } | |||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | |||
| public new CachedGuild Guild => base.Guild as CachedGuild; | |||
| public new CachedGlobalUser User => base.User as CachedGlobalUser; | |||
| public override Game Game => _game; | |||
| public override UserStatus Status => _status; | |||
| public override Game Game => Presence.Game; | |||
| public override UserStatus Status => Presence.Status; | |||
| public VoiceState? VoiceState => Guild.GetVoiceState(Id); | |||
| public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; | |||
| @@ -34,8 +48,8 @@ namespace Discord | |||
| { | |||
| base.Update(model, source); | |||
| _status = model.Status; | |||
| _game = model.Game != null ? new Game(model.Game) : (Game)null; | |||
| var game = model.Game != null ? new Game(model.Game) : null; | |||
| Presence = new Presence(game, model.Status); | |||
| } | |||
| public CachedGuildUser Clone() => MemberwiseClone() as CachedGuildUser; | |||
| @@ -1,16 +1,20 @@ | |||
| using System; | |||
| using Model = Discord.API.VoiceState; | |||
| namespace Discord | |||
| { | |||
| //TODO: C#7 Candidate for record type | |||
| internal struct VoiceState : IVoiceState | |||
| { | |||
| [Flags] | |||
| private enum Flags : byte | |||
| { | |||
| None = 0x0, | |||
| Suppressed = 0x1, | |||
| SelfMuted = 0x2, | |||
| SelfDeafened = 0x4, | |||
| None = 0x00, | |||
| Suppressed = 0x01, | |||
| Muted = 0x02, | |||
| Deafened = 0x04, | |||
| SelfMuted = 0x08, | |||
| SelfDeafened = 0x10, | |||
| } | |||
| private readonly Flags _voiceStates; | |||
| @@ -18,10 +22,14 @@ namespace Discord | |||
| public CachedVoiceChannel VoiceChannel { get; } | |||
| public string VoiceSessionId { get; } | |||
| public bool IsMuted => (_voiceStates & Flags.Muted) != 0; | |||
| public bool IsDeafened => (_voiceStates & Flags.Deafened) != 0; | |||
| public bool IsSuppressed => (_voiceStates & Flags.Suppressed) != 0; | |||
| public bool IsSelfMuted => (_voiceStates & Flags.SelfMuted) != 0; | |||
| public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; | |||
| public bool IsSuppressed => (_voiceStates & Flags.Suppressed) != 0; | |||
| public VoiceState(CachedVoiceChannel voiceChannel, Model model) | |||
| : this(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Suppress) { } | |||
| public VoiceState(CachedVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isSuppressed) | |||
| { | |||
| VoiceChannel = voiceChannel; | |||
| @@ -37,6 +45,8 @@ namespace Discord | |||
| _voiceStates = voiceStates; | |||
| } | |||
| public VoiceState Clone() => this; | |||
| IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel; | |||
| } | |||
| } | |||