| @@ -47,9 +47,9 @@ namespace Discord.API | |||||
| public class ExtendedMemberInfo : MemberInfo | public class ExtendedMemberInfo : MemberInfo | ||||
| { | { | ||||
| [JsonProperty("mute")] | [JsonProperty("mute")] | ||||
| public bool IsMuted; | |||||
| public bool? IsMuted; | |||||
| [JsonProperty("deaf")] | [JsonProperty("deaf")] | ||||
| public bool IsDeafened; | |||||
| public bool? IsDeafened; | |||||
| } | } | ||||
| public class PresenceMemberInfo : MemberReference | public class PresenceMemberInfo : MemberReference | ||||
| { | { | ||||
| @@ -71,9 +71,9 @@ namespace Discord.API | |||||
| [JsonProperty("self_deaf")] | [JsonProperty("self_deaf")] | ||||
| public bool? IsSelfDeafened; | public bool? IsSelfDeafened; | ||||
| [JsonProperty("mute")] | [JsonProperty("mute")] | ||||
| public bool IsMuted; | |||||
| public bool? IsMuted; | |||||
| [JsonProperty("deaf")] | [JsonProperty("deaf")] | ||||
| public bool IsDeafened; | |||||
| public bool? IsDeafened; | |||||
| [JsonProperty("token")] | [JsonProperty("token")] | ||||
| public string Token; | public string Token; | ||||
| } | } | ||||
| @@ -228,11 +228,11 @@ namespace Discord.API | |||||
| } | } | ||||
| [JsonProperty("tts")] | [JsonProperty("tts")] | ||||
| public bool IsTextToSpeech; | |||||
| public bool? IsTextToSpeech; | |||||
| [JsonProperty("mention_everyone")] | [JsonProperty("mention_everyone")] | ||||
| public bool IsMentioningEveryone; | |||||
| public bool? IsMentioningEveryone; | |||||
| [JsonProperty("timestamp")] | [JsonProperty("timestamp")] | ||||
| public DateTime Timestamp; | |||||
| public DateTime? Timestamp; | |||||
| [JsonProperty("edited_timestamp")] | [JsonProperty("edited_timestamp")] | ||||
| public DateTime? EditedTimestamp; | public DateTime? EditedTimestamp; | ||||
| [JsonProperty("mentions")] | [JsonProperty("mentions")] | ||||
| @@ -284,16 +284,16 @@ namespace Discord.API | |||||
| public class ExtendedInvite : Invite | public class ExtendedInvite : Invite | ||||
| { | { | ||||
| [JsonProperty("max_age")] | [JsonProperty("max_age")] | ||||
| public int MaxAge; | |||||
| public int ?MaxAge; | |||||
| [JsonProperty("max_uses")] | [JsonProperty("max_uses")] | ||||
| public int MaxUses; | |||||
| public int? MaxUses; | |||||
| [JsonProperty("revoked")] | [JsonProperty("revoked")] | ||||
| public bool IsRevoked; | |||||
| public bool? IsRevoked; | |||||
| [JsonProperty("temporary")] | [JsonProperty("temporary")] | ||||
| public bool IsTemporary; | |||||
| public bool? IsTemporary; | |||||
| [JsonProperty("uses")] | [JsonProperty("uses")] | ||||
| public int Uses; | |||||
| public int? Uses; | |||||
| [JsonProperty("created_at")] | [JsonProperty("created_at")] | ||||
| public DateTime CreatedAt; | |||||
| public DateTime? CreatedAt; | |||||
| } | } | ||||
| } | } | ||||
| @@ -368,8 +368,6 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case "RESUMED": | |||||
| break; | |||||
| //Servers | //Servers | ||||
| case "GUILD_CREATE": | case "GUILD_CREATE": | ||||
| @@ -678,6 +676,10 @@ namespace Discord | |||||
| } | } | ||||
| break; | break; | ||||
| //Internal (handled in DataWebSocket) | |||||
| case "RESUMED": | |||||
| break; | |||||
| //Internal (handled in DiscordSimpleClient) | //Internal (handled in DiscordSimpleClient) | ||||
| case "VOICE_SERVER_UPDATE": | case "VOICE_SERVER_UPDATE": | ||||
| break; | break; | ||||
| @@ -9,14 +9,24 @@ namespace Discord | |||||
| { | { | ||||
| public sealed class PermissionOverwrite | public sealed class PermissionOverwrite | ||||
| { | { | ||||
| public string Type { get; internal set; } | |||||
| public string Id { get; internal set; } | |||||
| public PackedChannelPermissions Deny { get; internal set; } | |||||
| public PackedChannelPermissions Allow { get; internal set; } | |||||
| public string Type { get; } | |||||
| public string TargetId { get; } | |||||
| public PackedChannelPermissions Allow { get; } | |||||
| public PackedChannelPermissions Deny { get; } | |||||
| internal PermissionOverwrite(string type, string targetId, uint allow, uint deny) | |||||
| { | |||||
| Type = type; | |||||
| TargetId = targetId; | |||||
| Allow = new PackedChannelPermissions(allow); | |||||
| Deny = new PackedChannelPermissions( deny); | |||||
| Allow.Lock(); | |||||
| Deny.Lock(); | |||||
| } | |||||
| } | } | ||||
| private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
| private ConcurrentDictionary<string, bool> _messages; | |||||
| private readonly ConcurrentDictionary<string, bool> _messages; | |||||
| internal bool _areMembersStale; | internal bool _areMembersStale; | ||||
| /// <summary> Returns the unique identifier for this channel. </summary> | /// <summary> Returns the unique identifier for this channel. </summary> | ||||
| @@ -27,13 +37,13 @@ namespace Discord | |||||
| public string Name { get { return !IsPrivate ? $"{_name}" : $"@{Recipient.Name}"; } internal set { _name = value; } } | public string Name { get { return !IsPrivate ? $"{_name}" : $"@{Recipient.Name}"; } internal set { _name = value; } } | ||||
| /// <summary> Returns the topic associated with this channel. </summary> | /// <summary> Returns the topic associated with this channel. </summary> | ||||
| public string Topic { get; internal set; } | |||||
| public string Topic { get; private set; } | |||||
| /// <summary> Returns the position of this channel in the channel list for this server. </summary> | /// <summary> Returns the position of this channel in the channel list for this server. </summary> | ||||
| public int Position { get; internal set; } | |||||
| public int Position { get; private set; } | |||||
| /// <summary> Returns false is this is a public chat and true if this is a private chat with another user (see Recipient). </summary> | /// <summary> Returns false is this is a public chat and true if this is a private chat with another user (see Recipient). </summary> | ||||
| public bool IsPrivate => ServerId == null; | public bool IsPrivate => ServerId == null; | ||||
| /// <summary> Returns the type of this channel (see ChannelTypes). </summary> | /// <summary> Returns the type of this channel (see ChannelTypes). </summary> | ||||
| public string Type { get; internal set; } | |||||
| public string Type { get; private set; } | |||||
| /// <summary> Returns the id of the server containing this channel. </summary> | /// <summary> Returns the id of the server containing this channel. </summary> | ||||
| public string ServerId { get; } | public string ServerId { get; } | ||||
| @@ -42,7 +52,7 @@ namespace Discord | |||||
| public Server Server => _client.Servers[ServerId]; | public Server Server => _client.Servers[ServerId]; | ||||
| /// For private chats, returns the Id of the target user, otherwise null. | /// For private chats, returns the Id of the target user, otherwise null. | ||||
| public string RecipientId { get; internal set; } | |||||
| public string RecipientId { get; set; } | |||||
| /// For private chats, returns the target user, otherwise null. | /// For private chats, returns the target user, otherwise null. | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User Recipient => _client.Users[RecipientId]; | public User Recipient => _client.Users[RecipientId]; | ||||
| @@ -74,6 +84,7 @@ namespace Discord | |||||
| public IEnumerable<Message> Messages => _messages.Select(x => _client.Messages[x.Key]); | public IEnumerable<Message> Messages => _messages.Select(x => _client.Messages[x.Key]); | ||||
| /// <summary> Returns a collection of all custom permissions used for this channel. </summary> | /// <summary> Returns a collection of all custom permissions used for this channel. </summary> | ||||
| private static readonly PermissionOverwrite[] _initialPermissionsOverwrites = new PermissionOverwrite[0]; | |||||
| private PermissionOverwrite[] _permissionOverwrites; | private PermissionOverwrite[] _permissionOverwrites; | ||||
| public IEnumerable<PermissionOverwrite> PermissionOverwrites => _permissionOverwrites; | public IEnumerable<PermissionOverwrite> PermissionOverwrites => _permissionOverwrites; | ||||
| @@ -85,6 +96,7 @@ namespace Discord | |||||
| RecipientId = recipientId; | RecipientId = recipientId; | ||||
| _messages = new ConcurrentDictionary<string, bool>(); | _messages = new ConcurrentDictionary<string, bool>(); | ||||
| _areMembersStale = true; | _areMembersStale = true; | ||||
| _permissionOverwrites = _initialPermissionsOverwrites; | |||||
| } | } | ||||
| internal void Update(API.ChannelReference model) | internal void Update(API.ChannelReference model) | ||||
| @@ -105,16 +117,10 @@ namespace Discord | |||||
| if (model.PermissionOverwrites != null) | if (model.PermissionOverwrites != null) | ||||
| { | { | ||||
| _permissionOverwrites = model.PermissionOverwrites.Select(x => new PermissionOverwrite | |||||
| { | |||||
| Type = x.Type, | |||||
| Id = x.Id, | |||||
| Deny = new PackedChannelPermissions(true, x.Deny), | |||||
| Allow = new PackedChannelPermissions(true, x.Allow) | |||||
| }).ToArray(); | |||||
| _permissionOverwrites = model.PermissionOverwrites | |||||
| .Select(x => new PermissionOverwrite(x.Type, x.Id, x.Allow, x.Deny)) | |||||
| .ToArray(); | |||||
| } | } | ||||
| else | |||||
| _permissionOverwrites = null; | |||||
| } | } | ||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| @@ -10,15 +10,15 @@ namespace Discord | |||||
| public string Id { get; } | public string Id { get; } | ||||
| /// <summary> Time (in seconds) until the invite expires. Set to 0 to never expire. </summary> | /// <summary> Time (in seconds) until the invite expires. Set to 0 to never expire. </summary> | ||||
| public int MaxAge { get; internal set; } | |||||
| public int MaxAge { get; private set; } | |||||
| /// <summary> The amount of times this invite has been used. </summary> | /// <summary> The amount of times this invite has been used. </summary> | ||||
| public int Uses { get; internal set; } | |||||
| public int Uses { get; private set; } | |||||
| /// <summary> The max amount of times this invite may be used. </summary> | /// <summary> The max amount of times this invite may be used. </summary> | ||||
| public int MaxUses { get; internal set; } | |||||
| public int MaxUses { get; private set; } | |||||
| /// <summary> Returns true if this invite has been destroyed, or you are banned from that server. </summary> | /// <summary> Returns true if this invite has been destroyed, or you are banned from that server. </summary> | ||||
| public bool IsRevoked { get; internal set; } | |||||
| public bool IsRevoked { get; private set; } | |||||
| /// <summary> If true, a user accepting this invite will be kicked from the server after closing their client. </summary> | /// <summary> If true, a user accepting this invite will be kicked from the server after closing their client. </summary> | ||||
| public bool IsTemporary { get; internal set; } | |||||
| public bool IsTemporary { get; private set; } | |||||
| /// <summary> Returns, if enabled, an alternative human-readable code for URLs. </summary> | /// <summary> Returns, if enabled, an alternative human-readable code for URLs. </summary> | ||||
| public string XkcdPass { get; } | public string XkcdPass { get; } | ||||
| @@ -26,19 +26,19 @@ namespace Discord | |||||
| public string Url => API.Endpoints.InviteUrl(XkcdPass ?? Id); | public string Url => API.Endpoints.InviteUrl(XkcdPass ?? Id); | ||||
| /// <summary> Returns the id of the user that created this invite. </summary> | /// <summary> Returns the id of the user that created this invite. </summary> | ||||
| public string InviterId { get; internal set; } | |||||
| public string InviterId { get; private set; } | |||||
| /// <summary> Returns the user that created this invite. </summary> | /// <summary> Returns the user that created this invite. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User Inviter => _client.Users[InviterId]; | public User Inviter => _client.Users[InviterId]; | ||||
| /// <summary> Returns the id of the server this invite is to. </summary> | /// <summary> Returns the id of the server this invite is to. </summary> | ||||
| public string ServerId { get; internal set; } | |||||
| public string ServerId { get; } | |||||
| /// <summary> Returns the server this invite is to. </summary> | /// <summary> Returns the server this invite is to. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Server Server => _client.Servers[ServerId]; | public Server Server => _client.Servers[ServerId]; | ||||
| /// <summary> Returns the id of the channel this invite is to. </summary> | /// <summary> Returns the id of the channel this invite is to. </summary> | ||||
| public string ChannelId { get; internal set; } | |||||
| public string ChannelId { get; private set; } | |||||
| /// <summary> Returns the channel this invite is to. </summary> | /// <summary> Returns the channel this invite is to. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Channel Channel => _client.Channels[ChannelId]; | public Channel Channel => _client.Channels[ChannelId]; | ||||
| @@ -55,19 +55,25 @@ namespace Discord | |||||
| internal void Update(API.Invite model) | internal void Update(API.Invite model) | ||||
| { | { | ||||
| ChannelId = model.Channel.Id; | |||||
| InviterId = model.Inviter?.Id; | |||||
| ServerId = model.Guild.Id; | |||||
| if (model.Channel != null) | |||||
| ChannelId = model.Channel.Id; | |||||
| if (model.Inviter != null) | |||||
| InviterId = model.Inviter.Id; | |||||
| } | } | ||||
| internal void Update(API.ExtendedInvite model) | internal void Update(API.ExtendedInvite model) | ||||
| { | { | ||||
| Update(model as API.Invite); | Update(model as API.Invite); | ||||
| IsRevoked = model.IsRevoked; | |||||
| IsTemporary = model.IsTemporary; | |||||
| MaxAge = model.MaxAge; | |||||
| MaxUses = model.MaxUses; | |||||
| Uses = model.Uses; | |||||
| if (model.IsRevoked != null) | |||||
| IsRevoked = model.IsRevoked.Value; | |||||
| if (model.IsTemporary != null) | |||||
| IsTemporary = model.IsTemporary.Value; | |||||
| if (model.MaxAge != null) | |||||
| MaxAge = model.MaxAge.Value; | |||||
| if (model.MaxUses != null) | |||||
| MaxUses = model.MaxUses.Value; | |||||
| if (model.Uses != null) | |||||
| Uses = model.Uses.Value; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -12,30 +12,30 @@ namespace Discord | |||||
| private ConcurrentDictionary<string, PackedChannelPermissions> _permissions; | private ConcurrentDictionary<string, PackedChannelPermissions> _permissions; | ||||
| /// <summary> Returns the name of this user on this server. </summary> | /// <summary> Returns the name of this user on this server. </summary> | ||||
| public string Name { get; internal set; } | |||||
| public string Name { get; private set; } | |||||
| /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | ||||
| public string Discriminator { get; internal set; } | |||||
| public string Discriminator { get; private set; } | |||||
| /// <summary> Returns the unique identifier for this user's current avatar. </summary> | /// <summary> Returns the unique identifier for this user's current avatar. </summary> | ||||
| public string AvatarId { get; internal set; } | |||||
| public string AvatarId { get; private set; } | |||||
| /// <summary> Returns the URL to this user's current avatar. </summary> | /// <summary> Returns the URL to this user's current avatar. </summary> | ||||
| public string AvatarUrl => API.Endpoints.UserAvatar(UserId, AvatarId); | public string AvatarUrl => API.Endpoints.UserAvatar(UserId, AvatarId); | ||||
| /// <summary> Returns the datetime that this user joined this server. </summary> | /// <summary> Returns the datetime that this user joined this server. </summary> | ||||
| public DateTime JoinedAt { get; internal set; } | |||||
| public DateTime JoinedAt { get; private set; } | |||||
| public bool IsMuted { get; internal set; } | |||||
| public bool IsDeafened { get; internal set; } | |||||
| public bool IsSelfMuted { get; internal set; } | |||||
| public bool IsSelfDeafened { get; internal set; } | |||||
| public bool IsSuppressed { get; internal set; } | |||||
| public bool IsMuted { get; private set; } | |||||
| public bool IsDeafened { get; private set; } | |||||
| public bool IsSelfMuted { get; private set; } | |||||
| public bool IsSelfDeafened { get; private set; } | |||||
| public bool IsSuppressed { get; private set; } | |||||
| public bool IsSpeaking { get; internal set; } | public bool IsSpeaking { get; internal set; } | ||||
| public string SessionId { get; internal set; } | |||||
| public string Token { get; internal set; } | |||||
| public string SessionId { get; private set; } | |||||
| public string Token { get; private set; } | |||||
| /// <summary> Returns the id for the game this user is currently playing. </summary> | /// <summary> Returns the id for the game this user is currently playing. </summary> | ||||
| public string GameId { get; internal set; } | |||||
| public string GameId { get; private set; } | |||||
| /// <summary> Returns the current status for this user. </summary> | /// <summary> Returns the current status for this user. </summary> | ||||
| public string Status { get; internal set; } | |||||
| public string Status { get; private set; } | |||||
| /// <summary> Returns the time this user last sent/edited a message, started typing or sent voice data in this server. </summary> | /// <summary> Returns the time this user last sent/edited a message, started typing or sent voice data in this server. </summary> | ||||
| public DateTime? LastActivityAt { get; private set; } | public DateTime? LastActivityAt { get; private set; } | ||||
| /// <summary> Returns the time this user was last seen online in this server. </summary> | /// <summary> Returns the time this user was last seen online in this server. </summary> | ||||
| @@ -50,11 +50,12 @@ namespace Discord | |||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Server Server => _client.Servers[ServerId]; | public Server Server => _client.Servers[ServerId]; | ||||
| public string VoiceChannelId { get; internal set; } | |||||
| public string VoiceChannelId { get; private set; } | |||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Channel VoiceChannel => _client.Channels[VoiceChannelId]; | public Channel VoiceChannel => _client.Channels[VoiceChannelId]; | ||||
| public string[] RoleIds { get; internal set; } | |||||
| private static readonly string[] _initialRoleIds = new string[0]; | |||||
| public string[] RoleIds { get; private set; } | |||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Role> Roles => RoleIds.Select(x => _client.Roles[x]); | public IEnumerable<Role> Roles => RoleIds.Select(x => _client.Roles[x]); | ||||
| @@ -67,8 +68,9 @@ namespace Discord | |||||
| UserId = userId; | UserId = userId; | ||||
| ServerId = serverId; | ServerId = serverId; | ||||
| Status = UserStatus.Offline; | Status = UserStatus.Offline; | ||||
| RoleIds = _initialRoleIds; | |||||
| _permissions = new ConcurrentDictionary<string, PackedChannelPermissions>(); | _permissions = new ConcurrentDictionary<string, PackedChannelPermissions>(); | ||||
| } | |||||
| } | |||||
| public override string ToString() => UserId; | public override string ToString() => UserId; | ||||
| @@ -100,11 +102,14 @@ namespace Discord | |||||
| internal void Update(API.ExtendedMemberInfo model) | internal void Update(API.ExtendedMemberInfo model) | ||||
| { | { | ||||
| Update(model as API.MemberInfo); | Update(model as API.MemberInfo); | ||||
| IsDeafened = model.IsDeafened; | |||||
| IsMuted = model.IsMuted; | |||||
| if (model.IsDeafened != null) | |||||
| IsDeafened = model.IsDeafened.Value; | |||||
| if (model.IsMuted != null) | |||||
| IsMuted = model.IsMuted.Value; | |||||
| } | } | ||||
| internal void Update(API.PresenceMemberInfo model) | internal void Update(API.PresenceMemberInfo model) | ||||
| { | { | ||||
| //Allows null | |||||
| if (Status != model.Status) | if (Status != model.Status) | ||||
| { | { | ||||
| Status = model.Status; | Status = model.Status; | ||||
| @@ -115,17 +120,22 @@ namespace Discord | |||||
| } | } | ||||
| internal void Update(API.VoiceMemberInfo model) | internal void Update(API.VoiceMemberInfo model) | ||||
| { | { | ||||
| IsDeafened = model.IsDeafened; | |||||
| IsMuted = model.IsMuted; | |||||
| SessionId = model.SessionId; | |||||
| Token = model.Token; | |||||
| VoiceChannelId = model.ChannelId; | |||||
| if (model.IsSelfDeafened.HasValue) | |||||
| if (model.IsDeafened != null) | |||||
| IsDeafened = model.IsDeafened.Value; | |||||
| if (model.IsMuted != null) | |||||
| IsMuted = model.IsMuted.Value; | |||||
| if (model.SessionId != null) | |||||
| SessionId = model.SessionId; | |||||
| if (model.Token != null) | |||||
| Token = model.Token; | |||||
| if (model.ChannelId != null) | |||||
| VoiceChannelId = model.ChannelId; | |||||
| if (model.IsSelfDeafened != null) | |||||
| IsSelfDeafened = model.IsSelfDeafened.Value; | IsSelfDeafened = model.IsSelfDeafened.Value; | ||||
| if (model.IsSelfMuted.HasValue) | |||||
| if (model.IsSelfMuted != null) | |||||
| IsSelfMuted = model.IsSelfMuted.Value; | IsSelfMuted = model.IsSelfMuted.Value; | ||||
| if (model.IsSuppressed.HasValue) | |||||
| if (model.IsSuppressed != null) | |||||
| IsSuppressed = model.IsSuppressed.Value; | IsSuppressed = model.IsSuppressed.Value; | ||||
| } | } | ||||
| @@ -137,7 +147,9 @@ namespace Discord | |||||
| internal void AddChannel(string channelId) | internal void AddChannel(string channelId) | ||||
| { | { | ||||
| _permissions.TryAdd(channelId, new PackedChannelPermissions()); | |||||
| var perms = new PackedChannelPermissions(); | |||||
| perms.Lock(); | |||||
| _permissions.TryAdd(channelId, perms); | |||||
| UpdatePermissions(channelId); | UpdatePermissions(channelId); | ||||
| } | } | ||||
| internal bool RemoveChannel(string channelId) | internal bool RemoveChannel(string channelId) | ||||
| @@ -171,13 +183,13 @@ namespace Discord | |||||
| foreach (var serverRole in Roles) | foreach (var serverRole in Roles) | ||||
| newPermissions |= serverRole.Permissions.RawValue; | newPermissions |= serverRole.Permissions.RawValue; | ||||
| foreach (var denyRole in channelOverwrites.Where(x => x.Type == PermissionTarget.Role && x.Deny.RawValue != 0 && RoleIds.Contains(x.Id))) | |||||
| foreach (var denyRole in channelOverwrites.Where(x => x.Type == PermissionTarget.Role && x.Deny.RawValue != 0 && RoleIds.Contains(x.TargetId))) | |||||
| newPermissions &= ~denyRole.Deny.RawValue; | newPermissions &= ~denyRole.Deny.RawValue; | ||||
| foreach (var allowRole in channelOverwrites.Where(x => x.Type == PermissionTarget.Role && x.Allow.RawValue != 0 && RoleIds.Contains(x.Id))) | |||||
| foreach (var allowRole in channelOverwrites.Where(x => x.Type == PermissionTarget.Role && x.Allow.RawValue != 0 && RoleIds.Contains(x.TargetId))) | |||||
| newPermissions |= allowRole.Allow.RawValue; | newPermissions |= allowRole.Allow.RawValue; | ||||
| foreach (var denyMembers in channelOverwrites.Where(x => x.Type == PermissionTarget.Member && x.Id == UserId && x.Deny.RawValue != 0)) | |||||
| foreach (var denyMembers in channelOverwrites.Where(x => x.Type == PermissionTarget.Member && x.TargetId == UserId && x.Deny.RawValue != 0)) | |||||
| newPermissions &= ~denyMembers.Deny.RawValue; | newPermissions &= ~denyMembers.Deny.RawValue; | ||||
| foreach (var allowMembers in channelOverwrites.Where(x => x.Type == PermissionTarget.Member && x.Id == UserId && x.Allow.RawValue != 0)) | |||||
| foreach (var allowMembers in channelOverwrites.Where(x => x.Type == PermissionTarget.Member && x.TargetId == UserId && x.Allow.RawValue != 0)) | |||||
| newPermissions |= allowMembers.Allow.RawValue; | newPermissions |= allowMembers.Allow.RawValue; | ||||
| if (((newPermissions >> (PackedChannelPermissions.GlobalBit - 1)) & 1) == 1) | if (((newPermissions >> (PackedChannelPermissions.GlobalBit - 1)) & 1) == 1) | ||||
| @@ -10,46 +10,84 @@ namespace Discord | |||||
| public sealed class Attachment : File | public sealed class Attachment : File | ||||
| { | { | ||||
| /// <summary> Unique identifier for this file. </summary> | /// <summary> Unique identifier for this file. </summary> | ||||
| public string Id { get; internal set; } | |||||
| public string Id { get; } | |||||
| /// <summary> Size, in bytes, of this file file. </summary> | /// <summary> Size, in bytes, of this file file. </summary> | ||||
| public int Size { get; internal set; } | |||||
| public int Size { get; } | |||||
| /// <summary> Filename of this file. </summary> | /// <summary> Filename of this file. </summary> | ||||
| public string Filename { get; internal set; } | |||||
| public string Filename { get; } | |||||
| internal Attachment(string id, string url, string proxyUrl, | |||||
| int? width, int? height, int size, string filename) | |||||
| : base(url, proxyUrl, width, height) | |||||
| { | |||||
| Id = id; | |||||
| Size = size; | |||||
| Filename = filename; | |||||
| } | |||||
| } | } | ||||
| public sealed class Embed | public sealed class Embed | ||||
| { | { | ||||
| /// <summary> URL of this embed. </summary> | /// <summary> URL of this embed. </summary> | ||||
| public string Url { get; internal set; } | |||||
| public string Url { get; } | |||||
| /// <summary> Type of this embed. </summary> | /// <summary> Type of this embed. </summary> | ||||
| public string Type { get; internal set; } | |||||
| public string Type { get; } | |||||
| /// <summary> Title for this embed. </summary> | /// <summary> Title for this embed. </summary> | ||||
| public string Title { get; internal set; } | |||||
| public string Title { get; } | |||||
| /// <summary> Summary of this embed. </summary> | /// <summary> Summary of this embed. </summary> | ||||
| public string Description { get; internal set; } | |||||
| public string Description { get; } | |||||
| /// <summary> Returns information about the author of this embed. </summary> | /// <summary> Returns information about the author of this embed. </summary> | ||||
| public EmbedReference Author { get; internal set; } | |||||
| public EmbedReference Author { get; } | |||||
| /// <summary> Returns information about the providing website of this embed. </summary> | /// <summary> Returns information about the providing website of this embed. </summary> | ||||
| public EmbedReference Provider { get; internal set; } | |||||
| public EmbedReference Provider { get; } | |||||
| /// <summary> Returns the thumbnail of this embed. </summary> | /// <summary> Returns the thumbnail of this embed. </summary> | ||||
| public File Thumbnail { get; internal set; } | |||||
| public File Thumbnail { get; } | |||||
| internal Embed(string url, string type, string title, string description, | |||||
| EmbedReference author, EmbedReference provider, File thumbnail) | |||||
| { | |||||
| Url = url; | |||||
| Type = type; | |||||
| Title = title; | |||||
| Description = description; | |||||
| Author = author; | |||||
| Provider = provider; | |||||
| Thumbnail = thumbnail; | |||||
| } | |||||
| } | } | ||||
| public sealed class EmbedReference | public sealed class EmbedReference | ||||
| { | { | ||||
| /// <summary> URL of this embed provider. </summary> | /// <summary> URL of this embed provider. </summary> | ||||
| public string Url { get; internal set; } | |||||
| public string Url { get; } | |||||
| /// <summary> Name of this embed provider. </summary> | /// <summary> Name of this embed provider. </summary> | ||||
| public string Name { get; internal set; } | |||||
| public string Name { get; } | |||||
| internal EmbedReference(string url, string name) | |||||
| { | |||||
| Url = url; | |||||
| Name = name; | |||||
| } | |||||
| } | } | ||||
| public class File | public class File | ||||
| { | { | ||||
| /// <summary> Download url for this file. </summary> | /// <summary> Download url for this file. </summary> | ||||
| public string Url { get; internal set; } | |||||
| public string Url { get; } | |||||
| /// <summary> Preview url for this file. </summary> | /// <summary> Preview url for this file. </summary> | ||||
| public string ProxyUrl { get; internal set; } | |||||
| public string ProxyUrl { get; } | |||||
| /// <summary> Width of the this file, if it is an image. </summary> | /// <summary> Width of the this file, if it is an image. </summary> | ||||
| public int? Width { get; internal set; } | |||||
| public int? Width { get; } | |||||
| /// <summary> Height of this file, if it is an image. </summary> | /// <summary> Height of this file, if it is an image. </summary> | ||||
| public int? Height { get; internal set; } | |||||
| public int? Height { get; } | |||||
| internal File(string url, string proxyUrl, int? width, int? height) | |||||
| { | |||||
| Url = url; | |||||
| ProxyUrl = proxyUrl; | |||||
| Width = width; | |||||
| Height = height; | |||||
| } | |||||
| } | } | ||||
| private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
| @@ -62,31 +100,34 @@ namespace Discord | |||||
| /// <summary> Returns true if the logged-in user was mentioned. </summary> | /// <summary> Returns true if the logged-in user was mentioned. </summary> | ||||
| /// <remarks> This is not set to true if the user was mentioned with @everyone (see IsMentioningEverone). </remarks> | /// <remarks> This is not set to true if the user was mentioned with @everyone (see IsMentioningEverone). </remarks> | ||||
| public bool IsMentioningMe { get; internal set; } | |||||
| public bool IsMentioningMe { get; private set; } | |||||
| /// <summary> Returns true if @everyone was mentioned by someone with permissions to do so. </summary> | /// <summary> Returns true if @everyone was mentioned by someone with permissions to do so. </summary> | ||||
| public bool IsMentioningEveryone { get; internal set; } | |||||
| public bool IsMentioningEveryone { get; private set; } | |||||
| /// <summary> Returns true if the message was sent as text-to-speech by someone with permissions to do so. </summary> | /// <summary> Returns true if the message was sent as text-to-speech by someone with permissions to do so. </summary> | ||||
| public bool IsTTS { get; internal set; } | |||||
| public bool IsTTS { get; private set; } | |||||
| /// <summary> Returns true if the message is still in the outgoing message queue. </summary> | /// <summary> Returns true if the message is still in the outgoing message queue. </summary> | ||||
| public bool IsQueued { get; internal set; } | public bool IsQueued { get; internal set; } | ||||
| /// <summary> Returns true if the message was rejected by the server. </summary> | /// <summary> Returns true if the message was rejected by the server. </summary> | ||||
| public bool HasFailed { get; internal set; } | public bool HasFailed { get; internal set; } | ||||
| /// <summary> Returns the raw content of this message as it was received from the server.. </summary> | /// <summary> Returns the raw content of this message as it was received from the server.. </summary> | ||||
| public string RawText { get; internal set; } | |||||
| public string RawText { get; private set; } | |||||
| /// <summary> Returns the content of this message with any special references such as mentions converted. </summary> | /// <summary> Returns the content of this message with any special references such as mentions converted. </summary> | ||||
| /// <remarks> This value is lazy loaded and only processed on first request. Each subsequent request will pull from cache. </remarks> | /// <remarks> This value is lazy loaded and only processed on first request. Each subsequent request will pull from cache. </remarks> | ||||
| public string Text => _cleanText != null ? _cleanText : (_cleanText = _client.Messages.CleanText(RawText)); | public string Text => _cleanText != null ? _cleanText : (_cleanText = _client.Messages.CleanText(RawText)); | ||||
| /// <summary> Returns the timestamp for when this message was sent. </summary> | /// <summary> Returns the timestamp for when this message was sent. </summary> | ||||
| public DateTime Timestamp { get; internal set; } | |||||
| public DateTime Timestamp { get; private set; } | |||||
| /// <summary> Returns the timestamp for when this message was last edited. </summary> | /// <summary> Returns the timestamp for when this message was last edited. </summary> | ||||
| public DateTime? EditedTimestamp { get; internal set; } | |||||
| public DateTime? EditedTimestamp { get; private set; } | |||||
| /// <summary> Returns the attachments included in this message. </summary> | /// <summary> Returns the attachments included in this message. </summary> | ||||
| public Attachment[] Attachments { get; internal set; } | |||||
| public Attachment[] Attachments { get; private set; } | |||||
| private static readonly Attachment[] _initialAttachments = new Attachment[0]; | |||||
| /// <summary> Returns a collection of all embeded content in this message. </summary> | /// <summary> Returns a collection of all embeded content in this message. </summary> | ||||
| public Embed[] Embeds { get; internal set; } | |||||
| public Embed[] Embeds { get; private set; } | |||||
| private static readonly Embed[] _initialEmbeds = new Embed[0]; | |||||
| private static readonly string[] _initialMentions = new string[0]; | |||||
| /// <summary> Returns a collection of all user ids mentioned in this message. </summary> | /// <summary> Returns a collection of all user ids mentioned in this message. </summary> | ||||
| public string[] MentionIds { get; internal set; } | |||||
| public string[] MentionIds { get; private set; } | |||||
| /// <summary> Returns a collection of all users mentioned in this message. </summary> | /// <summary> Returns a collection of all users mentioned in this message. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<User> Mentions => MentionIds.Select(x => _client.Users[x]).Where(x => x != null); | public IEnumerable<User> Mentions => MentionIds.Select(x => _client.Users[x]).Where(x => x != null); | ||||
| @@ -106,7 +147,7 @@ namespace Discord | |||||
| /// <summary> Returns true if the current user created this message. </summary> | /// <summary> Returns true if the current user created this message. </summary> | ||||
| public bool IsAuthor => _client.CurrentUserId == UserId; | public bool IsAuthor => _client.CurrentUserId == UserId; | ||||
| /// <summary> Returns the id of the author of this message. </summary> | /// <summary> Returns the id of the author of this message. </summary> | ||||
| public string UserId { get; internal set; } | |||||
| public string UserId { get; } | |||||
| /// <summary> Returns the author of this message. </summary> | /// <summary> Returns the author of this message. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User User => _client.Users[UserId]; | public User User => _client.Users[UserId]; | ||||
| @@ -120,77 +161,55 @@ namespace Discord | |||||
| Id = id; | Id = id; | ||||
| ChannelId = channelId; | ChannelId = channelId; | ||||
| UserId = userId; | UserId = userId; | ||||
| } | |||||
| Attachments = _initialAttachments; | |||||
| Embeds = _initialEmbeds; | |||||
| MentionIds = _initialMentions; | |||||
| } | |||||
| internal void Update(API.Message model) | internal void Update(API.Message model) | ||||
| { | { | ||||
| if (model.Attachments != null) | if (model.Attachments != null) | ||||
| { | { | ||||
| Attachments = model.Attachments.Select(x => new Attachment | |||||
| { | |||||
| Id = x.Id, | |||||
| Url = x.Url, | |||||
| ProxyUrl = x.ProxyUrl, | |||||
| Size = x.Size, | |||||
| Filename = x.Filename, | |||||
| Width = x.Width, | |||||
| Height = x.Height | |||||
| }).ToArray(); | |||||
| Attachments = model.Attachments | |||||
| .Select(x => new Attachment(x.Id, x.Url, x.ProxyUrl, x.Width, x.Height, x.Size, x.Filename)) | |||||
| .ToArray(); | |||||
| } | } | ||||
| else | |||||
| Attachments = new Attachment[0]; | |||||
| if (model.Embeds != null) | if (model.Embeds != null) | ||||
| { | { | ||||
| Embeds = model.Embeds.Select(x => | Embeds = model.Embeds.Select(x => | ||||
| { | { | ||||
| var embed = new Embed | |||||
| { | |||||
| Url = x.Url, | |||||
| Type = x.Type, | |||||
| Description = x.Description, | |||||
| Title = x.Title | |||||
| }; | |||||
| if (x.Provider != null) | |||||
| { | |||||
| embed.Provider = new EmbedReference | |||||
| { | |||||
| Url = x.Provider.Url, | |||||
| Name = x.Provider.Name | |||||
| }; | |||||
| } | |||||
| EmbedReference author = null, provider = null; | |||||
| File thumbnail = null; | |||||
| if (x.Author != null) | if (x.Author != null) | ||||
| { | |||||
| embed.Author = new EmbedReference | |||||
| { | |||||
| Url = x.Author.Url, | |||||
| Name = x.Author.Name | |||||
| }; | |||||
| } | |||||
| author = new EmbedReference(x.Author.Url, x.Author.Name); | |||||
| if (x.Provider != null) | |||||
| provider = new EmbedReference(x.Provider.Url, x.Provider.Name); | |||||
| if (x.Thumbnail != null) | if (x.Thumbnail != null) | ||||
| { | |||||
| embed.Thumbnail = new File | |||||
| { | |||||
| Url = x.Thumbnail.Url, | |||||
| ProxyUrl = x.Thumbnail.ProxyUrl, | |||||
| Width = x.Thumbnail.Width, | |||||
| Height = x.Thumbnail.Height | |||||
| }; | |||||
| } | |||||
| return embed; | |||||
| thumbnail = new File(x.Thumbnail.Url, x.Thumbnail.ProxyUrl, x.Thumbnail.Width, x.Thumbnail.Height); | |||||
| return new Embed(x.Url, x.Type, x.Title, x.Description, author, provider, thumbnail); | |||||
| }).ToArray(); | }).ToArray(); | ||||
| } | } | ||||
| else | |||||
| Embeds = new Embed[0]; | |||||
| IsMentioningEveryone = model.IsMentioningEveryone; | |||||
| IsTTS = model.IsTextToSpeech; | |||||
| MentionIds = model.Mentions?.Select(x => x.Id)?.ToArray() ?? new string[0]; | |||||
| IsMentioningMe = MentionIds.Contains(_client.CurrentUserId); | |||||
| RawText = model.Content; | |||||
| _cleanText = null; | |||||
| Timestamp = model.Timestamp; | |||||
| EditedTimestamp = model.EditedTimestamp; | |||||
| if (model.Author != null) | |||||
| UserId = model.Author.Id; | |||||
| if (model.IsMentioningEveryone != null) | |||||
| IsMentioningEveryone = model.IsMentioningEveryone.Value; | |||||
| if (model.IsTextToSpeech != null) | |||||
| IsTTS = model.IsTextToSpeech.Value; | |||||
| if (model.Timestamp != null) | |||||
| Timestamp = model.Timestamp.Value; | |||||
| if (model.EditedTimestamp != null) | |||||
| EditedTimestamp = model.EditedTimestamp; | |||||
| if (model.Mentions != null) | |||||
| { | |||||
| MentionIds = model.Mentions.Select(x => x.Id)?.ToArray(); | |||||
| IsMentioningMe = MentionIds.Contains(_client.CurrentUserId); | |||||
| } | |||||
| if (model.Content != null) | |||||
| { | |||||
| RawText = model.Content; | |||||
| _cleanText = null; | |||||
| } | |||||
| } | } | ||||
| public override string ToString() => User.ToString() + ": " + RawText; | public override string ToString() => User.ToString() + ": " + RawText; | ||||
| @@ -7,8 +7,7 @@ namespace Discord | |||||
| internal const int GlobalBit = 4; //ManagePermissions implicitly gives all permissions | internal const int GlobalBit = 4; //ManagePermissions implicitly gives all permissions | ||||
| public static readonly uint Mask = Convert.ToUInt32("00000011111100111111110000111111", 2); | public static readonly uint Mask = Convert.ToUInt32("00000011111100111111110000111111", 2); | ||||
| public PackedServerPermissions(uint rawValue = 0) : base(false, rawValue) { } | |||||
| internal PackedServerPermissions(bool isLocked, uint rawValue) : base(isLocked, rawValue) { } | |||||
| public PackedServerPermissions(uint rawValue = 0) : base(rawValue) { } | |||||
| /// <summary> If True, a user may ban users from the server. </summary> | /// <summary> If True, a user may ban users from the server. </summary> | ||||
| public bool General_BanMembers { get { return GetBit(2); } set { SetBit(2, value); } } | public bool General_BanMembers { get { return GetBit(2); } set { SetBit(2, value); } } | ||||
| @@ -21,7 +20,7 @@ namespace Discord | |||||
| /// <summary> If True, a user may adjust server properties. </summary> | /// <summary> If True, a user may adjust server properties. </summary> | ||||
| public bool General_ManageServer { get { return GetBit(6); } set { SetBit(6, value); } } | public bool General_ManageServer { get { return GetBit(6); } set { SetBit(6, value); } } | ||||
| public PackedServerPermissions Copy() => new PackedServerPermissions(false, _rawValue); | |||||
| public PackedServerPermissions Copy() => new PackedServerPermissions(RawValue); | |||||
| } | } | ||||
| public sealed class PackedChannelPermissions : PackedPermissions | public sealed class PackedChannelPermissions : PackedPermissions | ||||
| @@ -29,21 +28,20 @@ namespace Discord | |||||
| internal const int GlobalBit = 4; //ManagePermissions implicitly gives all permissions | internal const int GlobalBit = 4; //ManagePermissions implicitly gives all permissions | ||||
| public static readonly uint Mask = Convert.ToUInt32("00000011111100111111110000011001", 2); | public static readonly uint Mask = Convert.ToUInt32("00000011111100111111110000011001", 2); | ||||
| public PackedChannelPermissions(uint rawValue = 0) : base(false, rawValue) { } | |||||
| internal PackedChannelPermissions(bool isLocked, uint rawValue) : base(isLocked, rawValue) { } | |||||
| public PackedChannelPermissions(uint rawValue = 0) : base(rawValue) { } | |||||
| /// <summary> If True, a user may adjust permissions. This also implictly grants all other permissions. </summary> | /// <summary> If True, a user may adjust permissions. This also implictly grants all other permissions. </summary> | ||||
| public bool General_ManagePermissions { get { return GetBit(4); } set { SetBit(4, value); } } | public bool General_ManagePermissions { get { return GetBit(4); } set { SetBit(4, value); } } | ||||
| /// <summary> If True, a user may create, delete and modify this channel. </summary> | /// <summary> If True, a user may create, delete and modify this channel. </summary> | ||||
| public bool General_ManageChannel { get { return GetBit(5); } set { SetBit(5, value); } } | public bool General_ManageChannel { get { return GetBit(5); } set { SetBit(5, value); } } | ||||
| public PackedChannelPermissions Copy() => new PackedChannelPermissions(false, _rawValue); | |||||
| public PackedChannelPermissions Copy() => new PackedChannelPermissions(RawValue); | |||||
| } | } | ||||
| public abstract class PackedPermissions | public abstract class PackedPermissions | ||||
| { | { | ||||
| private bool _isLocked; | private bool _isLocked; | ||||
| protected uint _rawValue; | |||||
| private uint _rawValue; | |||||
| public uint RawValue | public uint RawValue | ||||
| { | { | ||||
| get { return _rawValue; } | get { return _rawValue; } | ||||
| @@ -55,7 +53,7 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| protected PackedPermissions(bool isLocked, uint rawValue) { _isLocked = isLocked; _rawValue = rawValue; } | |||||
| protected PackedPermissions(uint rawValue) { _rawValue = rawValue; } | |||||
| /// <summary> If True, a user may create invites. </summary> | /// <summary> If True, a user may create invites. </summary> | ||||
| public bool General_CreateInstantInvite { get { return GetBit(1); } set { SetBit(1, value); } } | public bool General_CreateInstantInvite { get { return GetBit(1); } set { SetBit(1, value); } } | ||||
| @@ -101,6 +99,7 @@ namespace Discord | |||||
| //6 Unused | //6 Unused | ||||
| internal void Lock() => _isLocked = true; | |||||
| internal void SetRawValue(uint rawValue) | internal void SetRawValue(uint rawValue) | ||||
| { | { | ||||
| //Bypasses isLocked for API changes. | //Bypasses isLocked for API changes. | ||||
| @@ -11,7 +11,7 @@ namespace Discord | |||||
| /// <summary> Returns the unique identifier for this role. </summary> | /// <summary> Returns the unique identifier for this role. </summary> | ||||
| public string Id { get; } | public string Id { get; } | ||||
| /// <summary> Returns the name of this role. </summary> | /// <summary> Returns the name of this role. </summary> | ||||
| public string Name { get; internal set; } | |||||
| public string Name { get; private set; } | |||||
| /// <summary> Returns the the permissions contained by this role. </summary> | /// <summary> Returns the the permissions contained by this role. </summary> | ||||
| public PackedServerPermissions Permissions { get; } | public PackedServerPermissions Permissions { get; } | ||||
| @@ -25,16 +25,17 @@ namespace Discord | |||||
| /// <summary> Returns true if this is the role representing all users in a server. </summary> | /// <summary> Returns true if this is the role representing all users in a server. </summary> | ||||
| public bool IsEveryone { get; } | public bool IsEveryone { get; } | ||||
| /// <summary> Returns a list of the ids of all members in this role. </summary> | /// <summary> Returns a list of the ids of all members in this role. </summary> | ||||
| public IEnumerable<string> MemberIds { get { return IsEveryone ? Server.UserIds : Server.Members.Where(x => x.RoleIds.Contains(Id)).Select(x => x.UserId); } } | |||||
| public IEnumerable<string> MemberIds => IsEveryone ? Server.UserIds : Server.Members.Where(x => x.RoleIds.Contains(Id)).Select(x => x.UserId); | |||||
| /// <summary> Returns a list of all members in this role. </summary> | /// <summary> Returns a list of all members in this role. </summary> | ||||
| public IEnumerable<Member> Members { get { return IsEveryone ? Server.Members : Server.Members.Where(x => x.RoleIds.Contains(Id)); } } | |||||
| public IEnumerable<Member> Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.RoleIds.Contains(Id)); | |||||
| internal Role(DiscordClient client, string id, string serverId, bool isEveryone) | internal Role(DiscordClient client, string id, string serverId, bool isEveryone) | ||||
| { | { | ||||
| _client = client; | _client = client; | ||||
| Id = id; | Id = id; | ||||
| ServerId = serverId; | ServerId = serverId; | ||||
| Permissions = new PackedServerPermissions(true, 0); | |||||
| Permissions = new PackedServerPermissions(0); | |||||
| Permissions.Lock(); | |||||
| IsEveryone = isEveryone; | IsEveryone = isEveryone; | ||||
| } | } | ||||
| @@ -9,34 +9,34 @@ namespace Discord | |||||
| public sealed class Server | public sealed class Server | ||||
| { | { | ||||
| private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
| private ConcurrentDictionary<string, bool> _bans, _channels, _invites, _members, _roles; | |||||
| private readonly ConcurrentDictionary<string, bool> _bans, _channels, _invites, _members, _roles; | |||||
| /// <summary> Returns the unique identifier for this server. </summary> | /// <summary> Returns the unique identifier for this server. </summary> | ||||
| public string Id { get; } | public string Id { get; } | ||||
| /// <summary> Returns the name of this channel. </summary> | /// <summary> Returns the name of this channel. </summary> | ||||
| public string Name { get; internal set; } | |||||
| public string Name { get; private set; } | |||||
| /// <summary> Returns the current logged-in user's data for this server. </summary> | /// <summary> Returns the current logged-in user's data for this server. </summary> | ||||
| public Member CurrentMember { get; internal set; } | public Member CurrentMember { get; internal set; } | ||||
| /// <summary> Returns the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel (see AFKChannel). </summary> | /// <summary> Returns the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel (see AFKChannel). </summary> | ||||
| public int AFKTimeout { get; internal set; } | |||||
| public int AFKTimeout { get; private set; } | |||||
| /// <summary> Returns the date and time your joined this server. </summary> | /// <summary> Returns the date and time your joined this server. </summary> | ||||
| public DateTime JoinedAt { get; internal set; } | |||||
| public DateTime JoinedAt { get; private set; } | |||||
| /// <summary> Returns the region for this server (see Regions). </summary> | /// <summary> Returns the region for this server (see Regions). </summary> | ||||
| public string Region { get; internal set; } | |||||
| public string Region { get; private set; } | |||||
| /*/// <summary> Returns the endpoint for this server's voice server. </summary> | /*/// <summary> Returns the endpoint for this server's voice server. </summary> | ||||
| internal string VoiceServer { get; set; }*/ | internal string VoiceServer { get; set; }*/ | ||||
| /// <summary> Returns true if the current user created this server. </summary> | /// <summary> Returns true if the current user created this server. </summary> | ||||
| public bool IsOwner => _client.CurrentUserId == OwnerId; | public bool IsOwner => _client.CurrentUserId == OwnerId; | ||||
| /// <summary> Returns the id of the user that first created this server. </summary> | /// <summary> Returns the id of the user that first created this server. </summary> | ||||
| public string OwnerId { get; internal set; } | |||||
| public string OwnerId { get; private set; } | |||||
| /// <summary> Returns the user that first created this server. </summary> | /// <summary> Returns the user that first created this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User Owner => _client.Users[OwnerId]; | public User Owner => _client.Users[OwnerId]; | ||||
| /// <summary> Returns the id of the AFK voice channel for this server (see AFKTimeout). </summary> | /// <summary> Returns the id of the AFK voice channel for this server (see AFKTimeout). </summary> | ||||
| public string AFKChannelId { get; internal set; } | |||||
| public string AFKChannelId { get; private set; } | |||||
| /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Channel AFKChannel => _client.Channels[AFKChannelId]; | public Channel AFKChannel => _client.Channels[AFKChannelId]; | ||||
| @@ -10,32 +10,32 @@ namespace Discord | |||||
| public sealed class User | public sealed class User | ||||
| { | { | ||||
| private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
| private int _refs; | |||||
| private readonly ConcurrentDictionary<string, bool> _servers; | |||||
| private int _refCount; | |||||
| private DateTime? _lastPrivateActivity; | private DateTime? _lastPrivateActivity; | ||||
| private ConcurrentDictionary<string, bool> _servers; | |||||
| /// <summary> Returns the unique identifier for this user. </summary> | /// <summary> Returns the unique identifier for this user. </summary> | ||||
| public string Id { get; } | public string Id { get; } | ||||
| /// <summary> Returns the name of this user on this server. </summary> | /// <summary> Returns the name of this user on this server. </summary> | ||||
| public string Name { get; internal set; } | |||||
| public string Name { get; private set; } | |||||
| /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | ||||
| public string Discriminator { get; internal set; } | |||||
| public string Discriminator { get; private set; } | |||||
| /// <summary> Returns the unique identifier for this user's current avatar. </summary> | /// <summary> Returns the unique identifier for this user's current avatar. </summary> | ||||
| public string AvatarId { get; internal set; } | |||||
| public string AvatarId { get; private set; } | |||||
| /// <summary> Returns the URL to this user's current avatar. </summary> | /// <summary> Returns the URL to this user's current avatar. </summary> | ||||
| public string AvatarUrl => API.Endpoints.UserAvatar(Id, AvatarId); | public string AvatarUrl => API.Endpoints.UserAvatar(Id, AvatarId); | ||||
| /// <summary> Returns the email for this user. </summary> | /// <summary> Returns the email for this user. </summary> | ||||
| /// <remarks> This field is only ever populated for the current logged in user. </remarks> | /// <remarks> This field is only ever populated for the current logged in user. </remarks> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public string Email { get; internal set; } | |||||
| public string Email { get; private set; } | |||||
| /// <summary> Returns if the email for this user has been verified. </summary> | /// <summary> Returns if the email for this user has been verified. </summary> | ||||
| /// <remarks> This field is only ever populated for the current logged in user. </remarks> | /// <remarks> This field is only ever populated for the current logged in user. </remarks> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public bool? IsVerified { get; internal set; } | |||||
| public bool? IsVerified { get; private set; } | |||||
| /// <summary> Returns the Id of the private messaging channel with this user, if one exists. </summary> | /// <summary> Returns the Id of the private messaging channel with this user, if one exists. </summary> | ||||
| public string PrivateChannelId { get; set; } | |||||
| public string PrivateChannelId { get; internal set; } | |||||
| /// <summary> Returns the private messaging channel with this user, if one exists. </summary> | /// <summary> Returns the private messaging channel with this user, if one exists. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Channel PrivateChannel => _client.Channels[PrivateChannelId]; | public Channel PrivateChannel => _client.Channels[PrivateChannelId]; | ||||
| @@ -112,11 +112,11 @@ namespace Discord | |||||
| public void AddRef() | public void AddRef() | ||||
| { | { | ||||
| Interlocked.Increment(ref _refs); | |||||
| Interlocked.Increment(ref _refCount); | |||||
| } | } | ||||
| public void RemoveRef() | public void RemoveRef() | ||||
| { | { | ||||
| if (Interlocked.Decrement(ref _refs) == 0) | |||||
| if (Interlocked.Decrement(ref _refCount) == 0) | |||||
| _client.Users.TryRemove(Id); | _client.Users.TryRemove(Id); | ||||
| } | } | ||||
| } | } | ||||