| @@ -11,12 +11,12 @@ namespace Discord.API | |||||
| [JsonProperty("nick")] | [JsonProperty("nick")] | ||||
| public Optional<string> Nick { get; set; } | public Optional<string> Nick { get; set; } | ||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| public ulong[] Roles { get; set; } | |||||
| public Optional<ulong[]> Roles { get; set; } | |||||
| [JsonProperty("joined_at")] | [JsonProperty("joined_at")] | ||||
| public Optional<DateTimeOffset> JoinedAt { get; set; } | public Optional<DateTimeOffset> JoinedAt { get; set; } | ||||
| [JsonProperty("deaf")] | [JsonProperty("deaf")] | ||||
| public bool Deaf { get; set; } | |||||
| public Optional<bool> Deaf { get; set; } | |||||
| [JsonProperty("mute")] | [JsonProperty("mute")] | ||||
| public bool Mute { get; set; } | |||||
| public Optional<bool> Mute { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -50,9 +50,12 @@ namespace Discord.Rest | |||||
| _joinedAtTicks = model.JoinedAt.Value.UtcTicks; | _joinedAtTicks = model.JoinedAt.Value.UtcTicks; | ||||
| if (model.Nick.IsSpecified) | if (model.Nick.IsSpecified) | ||||
| Nickname = model.Nick.Value; | Nickname = model.Nick.Value; | ||||
| IsDeafened = model.Deaf; | |||||
| IsMuted = model.Mute; | |||||
| UpdateRoles(model.Roles); | |||||
| if (model.Deaf.IsSpecified) | |||||
| IsDeafened = model.Deaf.Value; | |||||
| if (model.Mute.IsSpecified) | |||||
| IsMuted = model.Mute.Value; | |||||
| if (model.Roles.IsSpecified) | |||||
| UpdateRoles(model.Roles.Value); | |||||
| } | } | ||||
| private void UpdateRoles(ulong[] roleIds) | private void UpdateRoles(ulong[] roleIds) | ||||
| { | { | ||||
| @@ -1336,14 +1336,30 @@ namespace Discord.WebSocket | |||||
| var user = guild.GetUser(data.User.Id); | var user = guild.GetUser(data.User.Id); | ||||
| if (user == null) | if (user == null) | ||||
| guild.AddOrUpdateUser(data); | |||||
| user = guild.AddOrUpdateUser(data); | |||||
| else | |||||
| { | |||||
| var globalBefore = user.GlobalUser.Clone(); | |||||
| if (user.GlobalUser.Update(State, data.User)) | |||||
| { | |||||
| //Global data was updated, trigger UserUpdated | |||||
| await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), globalBefore, user).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| var before = user.Clone(); | |||||
| user.Update(State, data, true); | |||||
| await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false); | |||||
| } | |||||
| else | |||||
| { | |||||
| //TODO: Add as part of friends list update | |||||
| /*var globalUser = GetOrCreateUser(State, data.User); | |||||
| var before = globalUser.Clone(); | |||||
| globalUser.Update(State, data.User); | |||||
| globalUser.Update(State, data); | |||||
| await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), before, globalUser).ConfigureAwait(false);*/ | |||||
| } | } | ||||
| var globalUser = GetOrCreateUser(State, data.User); //TODO: Memory leak, users removed from friends list will never RemoveRef. | |||||
| var before = globalUser.Clone(); | |||||
| globalUser.Update(State, data); | |||||
| await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), before, globalUser).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| case "TYPING_START": | case "TYPING_START": | ||||
| @@ -163,7 +163,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| SocketGuildUser member; | SocketGuildUser member; | ||||
| if (members.TryGetValue(model.Presences[i].User.Id, out member)) | if (members.TryGetValue(model.Presences[i].User.Id, out member)) | ||||
| member.Update(state, model.Presences[i]); | |||||
| member.Update(state, model.Presences[i], true); | |||||
| else | else | ||||
| Debug.Assert(false); | Debug.Assert(false); | ||||
| } | } | ||||
| @@ -249,7 +249,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| SocketGuildUser member; | SocketGuildUser member; | ||||
| if (members.TryGetValue(model.Presences[i].User.Id, out member)) | if (members.TryGetValue(model.Presences[i].User.Id, out member)) | ||||
| member.Update(state, model.Presences[i]); | |||||
| member.Update(state, model.Presences[i], true); | |||||
| else | else | ||||
| Debug.Assert(false); | Debug.Assert(false); | ||||
| } | } | ||||
| @@ -392,7 +392,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| SocketGuildUser member; | SocketGuildUser member; | ||||
| if (_members.TryGetValue(model.User.Id, out member)) | if (_members.TryGetValue(model.User.Id, out member)) | ||||
| member.Update(Discord.State, model); | |||||
| member.Update(Discord.State, model, false); | |||||
| else | else | ||||
| { | { | ||||
| member = SocketGuildUser.Create(this, Discord.State, model); | member = SocketGuildUser.Create(this, Discord.State, model); | ||||
| @@ -26,7 +26,7 @@ namespace Discord.WebSocket | |||||
| public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } } | public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } } | ||||
| public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } } | public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } } | ||||
| public GuildPermissions GuildPermissions => new GuildPermissions(Permissions.ResolveGuild(Guild, this)); | public GuildPermissions GuildPermissions => new GuildPermissions(Permissions.ResolveGuild(Guild, this)); | ||||
| internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } } | |||||
| internal override SocketPresence Presence { get; set; } | |||||
| public override bool IsWebhook => false; | public override bool IsWebhook => false; | ||||
| public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; | public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; | ||||
| @@ -78,7 +78,7 @@ namespace Discord.WebSocket | |||||
| internal static SocketGuildUser Create(SocketGuild guild, ClientState state, PresenceModel model) | internal static SocketGuildUser Create(SocketGuild guild, ClientState state, PresenceModel model) | ||||
| { | { | ||||
| var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User)); | var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User)); | ||||
| entity.Update(state, model); | |||||
| entity.Update(state, model, false); | |||||
| return entity; | return entity; | ||||
| } | } | ||||
| internal void Update(ClientState state, Model model) | internal void Update(ClientState state, Model model) | ||||
| @@ -88,15 +88,19 @@ namespace Discord.WebSocket | |||||
| _joinedAtTicks = model.JoinedAt.Value.UtcTicks; | _joinedAtTicks = model.JoinedAt.Value.UtcTicks; | ||||
| if (model.Nick.IsSpecified) | if (model.Nick.IsSpecified) | ||||
| Nickname = model.Nick.Value; | Nickname = model.Nick.Value; | ||||
| UpdateRoles(model.Roles); | |||||
| if (model.Roles.IsSpecified) | |||||
| UpdateRoles(model.Roles.Value); | |||||
| } | } | ||||
| internal override void Update(ClientState state, PresenceModel model) | internal override void Update(ClientState state, PresenceModel model) | ||||
| => Update(state, model, true); | |||||
| internal void Update(ClientState state, PresenceModel model, bool updatePresence) | |||||
| { | { | ||||
| base.Update(state, model); | |||||
| if (model.Roles.IsSpecified) | |||||
| UpdateRoles(model.Roles.Value); | |||||
| if (updatePresence) | |||||
| base.Update(state, model); | |||||
| if (model.Nick.IsSpecified) | if (model.Nick.IsSpecified) | ||||
| Nickname = model.Nick.Value; | Nickname = model.Nick.Value; | ||||
| if (model.Roles.IsSpecified) | |||||
| UpdateRoles(model.Roles.Value); | |||||
| } | } | ||||
| private void UpdateRoles(ulong[] roleIds) | private void UpdateRoles(ulong[] roleIds) | ||||
| { | { | ||||
| @@ -33,16 +33,25 @@ namespace Discord.WebSocket | |||||
| entity.Update(state, model); | entity.Update(state, model); | ||||
| return entity; | return entity; | ||||
| } | } | ||||
| internal override void Update(ClientState state, Model model) | |||||
| internal override bool Update(ClientState state, Model model) | |||||
| { | { | ||||
| base.Update(state, model); | |||||
| bool hasGlobalChanges = base.Update(state, model); | |||||
| if (model.Email.IsSpecified) | if (model.Email.IsSpecified) | ||||
| { | |||||
| Email = model.Email.Value; | Email = model.Email.Value; | ||||
| hasGlobalChanges = true; | |||||
| } | |||||
| if (model.Verified.IsSpecified) | if (model.Verified.IsSpecified) | ||||
| { | |||||
| IsVerified = model.Verified.Value; | IsVerified = model.Verified.Value; | ||||
| hasGlobalChanges = true; | |||||
| } | |||||
| if (model.MfaEnabled.IsSpecified) | if (model.MfaEnabled.IsSpecified) | ||||
| { | |||||
| IsMfaEnabled = model.MfaEnabled.Value; | IsMfaEnabled = model.MfaEnabled.Value; | ||||
| hasGlobalChanges = true; | |||||
| } | |||||
| return hasGlobalChanges; | |||||
| } | } | ||||
| public Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions options = null) | public Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions options = null) | ||||
| @@ -1,7 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
| using PresenceModel = Discord.API.Presence; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| @@ -29,18 +28,6 @@ namespace Discord.WebSocket | |||||
| return entity; | return entity; | ||||
| } | } | ||||
| internal override void Update(ClientState state, PresenceModel model) | |||||
| { | |||||
| if (model.User.Avatar.IsSpecified) | |||||
| AvatarId = model.User.Avatar.Value; | |||||
| if (model.User.Discriminator.IsSpecified) | |||||
| DiscriminatorValue = ushort.Parse(model.User.Discriminator.Value); | |||||
| if (model.User.Bot.IsSpecified) | |||||
| IsBot = model.User.Bot.Value; | |||||
| if (model.User.Username.IsSpecified) | |||||
| Username = model.User.Username.Value; | |||||
| } | |||||
| internal new SocketUnknownUser Clone() => MemberwiseClone() as SocketUnknownUser; | internal new SocketUnknownUser Clone() => MemberwiseClone() as SocketUnknownUser; | ||||
| } | } | ||||
| } | } | ||||
| @@ -26,21 +26,39 @@ namespace Discord.WebSocket | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal virtual void Update(ClientState state, Model model) | |||||
| internal virtual bool Update(ClientState state, Model model) | |||||
| { | { | ||||
| if (model.Avatar.IsSpecified) | |||||
| bool hasChanges = false; | |||||
| if (model.Avatar.IsSpecified && model.Avatar.Value != AvatarId) | |||||
| { | |||||
| AvatarId = model.Avatar.Value; | AvatarId = model.Avatar.Value; | ||||
| hasChanges = true; | |||||
| } | |||||
| if (model.Discriminator.IsSpecified) | if (model.Discriminator.IsSpecified) | ||||
| DiscriminatorValue = ushort.Parse(model.Discriminator.Value); | |||||
| if (model.Bot.IsSpecified) | |||||
| { | |||||
| var newVal = ushort.Parse(model.Discriminator.Value); | |||||
| if (newVal != DiscriminatorValue) | |||||
| { | |||||
| DiscriminatorValue = ushort.Parse(model.Discriminator.Value); | |||||
| hasChanges = true; | |||||
| } | |||||
| } | |||||
| if (model.Bot.IsSpecified && model.Bot.Value != IsBot) | |||||
| { | |||||
| IsBot = model.Bot.Value; | IsBot = model.Bot.Value; | ||||
| if (model.Username.IsSpecified) | |||||
| hasChanges = true; | |||||
| } | |||||
| if (model.Username.IsSpecified && model.Username.Value != Username) | |||||
| { | |||||
| Username = model.Username.Value; | Username = model.Username.Value; | ||||
| hasChanges = true; | |||||
| } | |||||
| return hasChanges; | |||||
| } | } | ||||
| internal virtual void Update(ClientState state, PresenceModel model) | internal virtual void Update(ClientState state, PresenceModel model) | ||||
| { | { | ||||
| Presence = SocketPresence.Create(model); | Presence = SocketPresence.Create(model); | ||||
| Update(state, model.User); | |||||
| //Update(state, model.User); | |||||
| } | } | ||||
| public Task<RestDMChannel> CreateDMChannelAsync(RequestOptions options = null) | public Task<RestDMChannel> CreateDMChannelAsync(RequestOptions options = null) | ||||
| @@ -4,7 +4,6 @@ using System.Collections.Immutable; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
| using PresenceModel = Discord.API.Presence; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| @@ -36,18 +35,6 @@ namespace Discord.WebSocket | |||||
| return entity; | return entity; | ||||
| } | } | ||||
| internal override void Update(ClientState state, PresenceModel model) | |||||
| { | |||||
| if (model.User.Avatar.IsSpecified) | |||||
| AvatarId = model.User.Avatar.Value; | |||||
| if (model.User.Discriminator.IsSpecified) | |||||
| DiscriminatorValue = ushort.Parse(model.User.Discriminator.Value); | |||||
| if (model.User.Bot.IsSpecified) | |||||
| IsBot = model.User.Bot.Value; | |||||
| if (model.User.Username.IsSpecified) | |||||
| Username = model.User.Username.Value; | |||||
| } | |||||
| internal new SocketWebhookUser Clone() => MemberwiseClone() as SocketWebhookUser; | internal new SocketWebhookUser Clone() => MemberwiseClone() as SocketWebhookUser; | ||||