| @@ -68,7 +68,7 @@ namespace Discord.Commands | |||
| client.MessageReceived += async (s, e) => | |||
| { | |||
| if (_allCommands.Count == 0) return; | |||
| if (e.Message.User.Id == _client.CurrentUser.Id) return; | |||
| if (e.Message.User == null || e.Message.User.Id == _client.CurrentUser.Id) return; | |||
| string msg = e.Message.RawText; | |||
| if (msg.Length == 0) return; | |||
| @@ -9,8 +9,7 @@ namespace Discord.API.Client | |||
| { | |||
| [JsonProperty("type")] | |||
| public string Type { get; set; } | |||
| [JsonProperty("id")] | |||
| [JsonConverter(typeof(LongStringConverter))] | |||
| [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] | |||
| public ulong Id { get; set; } | |||
| [JsonProperty("deny")] | |||
| public uint Deny { get; set; } | |||
| @@ -7,8 +7,8 @@ namespace Discord.API.Client | |||
| { | |||
| [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] | |||
| public ulong Id { get; set; } | |||
| [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] | |||
| public ulong GuildId { get; set; } | |||
| [JsonProperty("guild_id"), JsonConverter(typeof(NullableLongStringConverter))] | |||
| public ulong? GuildId { get; set; } | |||
| [JsonProperty("name")] | |||
| public string Name { get; set; } | |||
| [JsonProperty("type")] | |||
| @@ -5,7 +5,7 @@ namespace Discord.API.Client | |||
| { | |||
| public class MemberReference | |||
| { | |||
| [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] | |||
| [JsonProperty("guild_id"), JsonConverter(typeof(NullableLongStringConverter))] | |||
| public ulong? GuildId { get; set; } | |||
| [JsonProperty("user")] | |||
| public UserReference User { get; set; } | |||
| @@ -171,10 +171,7 @@ namespace Discord | |||
| State = ConnectionState.Connecting; | |||
| _disconnectedEvent.Reset(); | |||
| await Login(email, password, token).ConfigureAwait(false); | |||
| ClientAPI.Token = token; | |||
| GatewaySocket.Token = token; | |||
| await Login(email, password, token).ConfigureAwait(false); | |||
| await GatewaySocket.Connect().ConfigureAwait(false); | |||
| List<Task> tasks = new List<Task>(); | |||
| @@ -233,6 +230,9 @@ namespace Discord | |||
| } | |||
| } | |||
| ClientAPI.Token = token; | |||
| GatewaySocket.Token = token; | |||
| //Get gateway and check token | |||
| try | |||
| { | |||
| @@ -309,22 +309,10 @@ namespace Discord | |||
| } | |||
| #region Channels | |||
| private Channel AddChannel(ulong id, ulong? guildId, ulong? recipientId) | |||
| internal void AddChannel(Channel channel) | |||
| { | |||
| Channel channel; | |||
| if (recipientId != null) | |||
| { | |||
| channel = _privateChannels.GetOrAdd(recipientId.Value, | |||
| x => new Channel(this, x, new User(this, recipientId.Value, null))); | |||
| } | |||
| else | |||
| { | |||
| var server = GetServer(guildId.Value); | |||
| channel = server.AddChannel(id); | |||
| } | |||
| _channels[channel.Id] = channel; | |||
| return channel; | |||
| } | |||
| } | |||
| private Channel RemoveChannel(ulong id) | |||
| { | |||
| Channel channel; | |||
| @@ -337,20 +325,26 @@ namespace Discord | |||
| } | |||
| return channel; | |||
| } | |||
| internal Channel GetChannel(ulong id) | |||
| public Channel GetChannel(ulong id) | |||
| { | |||
| Channel channel; | |||
| _channels.TryGetValue(id, out channel); | |||
| return channel; | |||
| } | |||
| private Channel AddPrivateChannel(ulong id, ulong recipientId) | |||
| { | |||
| Channel channel; | |||
| if (_privateChannels.TryGetOrAdd(recipientId, x => new Channel(this, id, new User(this, x, null)), out channel)) | |||
| AddChannel(channel); | |||
| return channel; | |||
| } | |||
| internal Channel GetPrivateChannel(ulong recipientId) | |||
| { | |||
| Channel channel; | |||
| _privateChannels.TryGetValue(recipientId, out channel); | |||
| return channel; | |||
| } | |||
| internal async Task<Channel> CreatePrivateChannel(User user) | |||
| { | |||
| var channel = GetPrivateChannel(user.Id); | |||
| @@ -358,8 +352,8 @@ namespace Discord | |||
| var request = new CreatePrivateChannelRequest() { RecipientId = user.Id }; | |||
| var response = await ClientAPI.Send(request).ConfigureAwait(false); | |||
| channel = AddChannel(response.Id, null, response.Recipient.Id); | |||
| channel = AddPrivateChannel(response.Id, user.Id); | |||
| channel.Update(response); | |||
| return channel; | |||
| } | |||
| @@ -453,6 +447,7 @@ namespace Discord | |||
| SessionId = data.SessionId; | |||
| PrivateUser = new User(this, data.User.Id, null); | |||
| PrivateUser.Update(data.User); | |||
| CurrentUser = new Profile(this, data.User.Id); | |||
| CurrentUser.Update(data.User); | |||
| foreach (var model in data.Guilds) | |||
| { | |||
| @@ -464,7 +459,7 @@ namespace Discord | |||
| } | |||
| foreach (var model in data.PrivateChannels) | |||
| { | |||
| var channel = AddChannel(model.Id, null, model.Recipient.Id); | |||
| var channel = AddPrivateChannel(model.Id, model.Recipient.Id); | |||
| channel.Update(model); | |||
| } | |||
| } | |||
| @@ -523,10 +518,22 @@ namespace Discord | |||
| case "CHANNEL_CREATE": | |||
| { | |||
| var data = e.Payload.ToObject<ChannelCreateEvent>(_serializer); | |||
| Channel channel = AddChannel(data.Id, data.GuildId, data.Recipient.Id); | |||
| channel.Update(data); | |||
| Logger.Info($"Channel Created: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); | |||
| OnChannelCreated(channel); | |||
| Channel channel = null; | |||
| if (data.GuildId != null) | |||
| { | |||
| var server = GetServer(data.GuildId.Value); | |||
| if (server != null) | |||
| channel = server.AddChannel(data.Id); | |||
| } | |||
| else | |||
| channel = AddPrivateChannel(data.Id, data.Recipient.Id); | |||
| if (channel != null) | |||
| { | |||
| channel.Update(data); | |||
| Logger.Info($"Channel Created: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); | |||
| OnChannelCreated(channel); | |||
| } | |||
| } | |||
| break; | |||
| case "CHANNEL_UPDATE": | |||
| @@ -27,15 +27,22 @@ namespace Discord | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static bool HasBit(this uint value, byte bit) => ((value >> bit) & 1U) == 1; | |||
| public static bool TryGetAdd<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> d, | |||
| public static bool TryGetOrAdd<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> d, | |||
| TKey key, Func<TKey, TValue> factory, out TValue result) | |||
| where TValue : class | |||
| { | |||
| TValue newValue = null; | |||
| while (true) | |||
| { | |||
| if (d.TryGetValue(key, out result)) | |||
| return false; | |||
| if (d.TryAdd(key, factory(key))) | |||
| if (newValue == null) | |||
| newValue = factory(key); | |||
| if (d.TryAdd(key, newValue)) | |||
| { | |||
| result = newValue; | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| @@ -112,7 +112,6 @@ namespace Discord | |||
| : this(client, id) | |||
| { | |||
| Recipient = recipient; | |||
| Name = $"@{recipient}"; | |||
| AddUser(client.PrivateUser); | |||
| AddUser(recipient); | |||
| } | |||
| @@ -143,7 +142,10 @@ namespace Discord | |||
| if (model.Topic != null) | |||
| Topic = model.Topic; | |||
| if (model.Recipient != null) | |||
| { | |||
| Recipient.Update(model.Recipient); | |||
| Name = $"@{Recipient}"; | |||
| } | |||
| if (model.PermissionOverwrites != null) | |||
| { | |||
| @@ -263,9 +265,13 @@ namespace Discord | |||
| public Message GetMessage(ulong id) | |||
| { | |||
| Message result; | |||
| _messages.TryGetValue(id, out result); | |||
| return result; | |||
| if (Client.Config.MessageCacheSize > 0) | |||
| { | |||
| Message result; | |||
| _messages.TryGetValue(id, out result); | |||
| return result; | |||
| } | |||
| return null; | |||
| } | |||
| public async Task<Message[]> DownloadMessages(int limit = 100, ulong? relativeMessageId = null, | |||
| RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) | |||
| @@ -556,6 +562,17 @@ namespace Discord | |||
| } | |||
| public User GetUser(ulong id) | |||
| { | |||
| if (!Client.Config.UsePermissionsCache) | |||
| { | |||
| var user = Server.GetUser(id); | |||
| ChannelPermissions perms = new ChannelPermissions(); | |||
| UpdatePermissions(user, perms); | |||
| if (perms.ReadMessages) | |||
| return user; | |||
| else | |||
| return null; | |||
| } | |||
| Member result; | |||
| _users.TryGetValue(id, out result); | |||
| return result.User; | |||
| @@ -28,7 +28,6 @@ namespace Discord | |||
| public static readonly Color LightGrey = PresetColor(0x979C9F); | |||
| public static readonly Color DarkerGrey = PresetColor(0x546E7A); | |||
| private static Color PresetColor(uint packedValue) | |||
| { | |||
| Color color = new Color(packedValue); | |||
| @@ -1,79 +0,0 @@ | |||
| using Newtonsoft.Json; | |||
| using System.Collections.Concurrent; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using APIUser = Discord.API.Client.User; | |||
| namespace Discord | |||
| { | |||
| /*public sealed class GlobalUser : CachedObject<ulong> | |||
| { | |||
| /// <summary> Returns the email for this user. Note: this field is only ever populated for the current logged in user. </summary> | |||
| [JsonIgnore] | |||
| public string Email { get; private set; } | |||
| /// <summary> Returns if the email for this user has been verified. Note: this field is only ever populated for the current logged in user. </summary> | |||
| [JsonIgnore] | |||
| public bool? IsVerified { get; private set; } | |||
| /// <summary> Returns the private messaging channel with this user, if one exists. </summary> | |||
| [JsonIgnore] | |||
| public Channel PrivateChannel | |||
| { | |||
| get { return _privateChannel; } | |||
| set | |||
| { | |||
| _privateChannel = value; | |||
| if (value == null) | |||
| CheckUser(); | |||
| } | |||
| } | |||
| [JsonProperty] | |||
| private ulong? PrivateChannelId => _privateChannel?.Id; | |||
| private Channel _privateChannel; | |||
| /// <summary> Returns a collection of all server-specific data for every server this user is a member of. </summary> | |||
| [JsonIgnore] | |||
| public IEnumerable<User> Memberships => _users.Select(x => x.Value); | |||
| [JsonProperty] | |||
| private IEnumerable<ulong> ServerIds => _users.Select(x => x.Key); | |||
| private readonly ConcurrentDictionary<ulong, User> _users; | |||
| /// <summary> Returns the string used to mention this user. </summary> | |||
| public string Mention => $"<@{Id}>"; | |||
| internal GlobalUser(DiscordClient client, ulong id) | |||
| : base(client, id) | |||
| { | |||
| _users = new ConcurrentDictionary<ulong, User>(); | |||
| } | |||
| internal override bool LoadReferences() { return true; } | |||
| internal override void UnloadReferences() | |||
| { | |||
| //Don't need to clean _users - they're considered owned by server | |||
| } | |||
| internal void Update(APIUser model) | |||
| { | |||
| if (model.Email != null) | |||
| Email = model.Email; | |||
| if (model.IsVerified != null) | |||
| IsVerified = model.IsVerified; | |||
| } | |||
| internal void AddUser(User user) => _users.TryAdd(user.Server?.Id ?? 0, user); | |||
| internal void RemoveUser(User user) | |||
| { | |||
| if (_users.TryRemove(user.Server?.Id ?? 0, out user)) | |||
| CheckUser(); | |||
| } | |||
| internal void CheckUser() | |||
| { | |||
| if (_users.Count == 0 && PrivateChannel == null) | |||
| _client.GlobalUsers.TryRemove(Id); | |||
| } | |||
| public override bool Equals(object obj) => obj is GlobalUser && (obj as GlobalUser).Id == Id; | |||
| public override int GetHashCode() => unchecked(Id.GetHashCode() + 7891); | |||
| public override string ToString() => IdConvert.ToString(Id); | |||
| }*/ | |||
| } | |||
| @@ -201,6 +201,10 @@ namespace Discord | |||
| internal Message(ulong id, Channel channel, ulong userId) | |||
| { | |||
| Id = id; | |||
| Channel = channel; | |||
| _userId = userId; | |||
| Attachments = _initialAttachments; | |||
| Embeds = _initialEmbeds; | |||
| } | |||
| @@ -11,20 +11,21 @@ namespace Discord | |||
| internal DiscordClient Client { get; } | |||
| /// <summary> Gets the unique identifier for this user. </summary> | |||
| public ulong Id { get; private set; } | |||
| public ulong Id { get; } | |||
| /// <summary> Gets the email for this user. </summary> | |||
| public string Email { get; private set; } | |||
| /// <summary> Gets if the email for this user has been verified. </summary> | |||
| public bool? IsVerified { get; private set; } | |||
| internal Profile(DiscordClient client) | |||
| internal Profile(DiscordClient client, ulong id) | |||
| { | |||
| Client = client; | |||
| Id = id; | |||
| } | |||
| internal void Update(APIUser model) | |||
| { | |||
| Id = model.Id; | |||
| Email = model.Email; | |||
| IsVerified = model.IsVerified; | |||
| } | |||
| @@ -52,6 +52,7 @@ namespace Discord | |||
| { | |||
| Id = id; | |||
| Server = server; | |||
| Permissions = new ServerPermissions(0); | |||
| Permissions.Lock(); | |||
| Color = new Color(0); | |||
| @@ -78,6 +78,7 @@ namespace Discord | |||
| { | |||
| Client = client; | |||
| Id = id; | |||
| _channels = new ConcurrentDictionary<ulong, Channel>(); | |||
| _roles = new ConcurrentDictionary<ulong, Role>(); | |||
| _users = new ConcurrentDictionary<ulong, Member>(); | |||
| @@ -97,7 +98,7 @@ namespace Discord | |||
| if (model.AFKTimeout != null) | |||
| AFKTimeout = model.AFKTimeout.Value; | |||
| _afkChannelId = model.AFKChannelId.Value; //Can be null | |||
| _afkChannelId = model.AFKChannelId; //Can be null | |||
| if (model.JoinedAt != null) | |||
| JoinedAt = model.JoinedAt.Value; | |||
| if (model.OwnerId != null) | |||
| @@ -196,7 +197,11 @@ namespace Discord | |||
| #region Channels | |||
| internal Channel AddChannel(ulong id) | |||
| => _channels.GetOrAdd(id, x => new Channel(Client, x, this)); | |||
| { | |||
| var channel = _channels.GetOrAdd(id, x => new Channel(Client, x, this)); | |||
| Client.AddChannel(channel); | |||
| return channel; | |||
| } | |||
| internal Channel RemoveChannel(ulong id) | |||
| { | |||
| Channel channel; | |||
| @@ -136,9 +136,10 @@ namespace Discord | |||
| internal User(DiscordClient client, ulong id, Server server) | |||
| { | |||
| Client = client; | |||
| Id = id; | |||
| Server = server; | |||
| _roles = new Dictionary<ulong, Role>(); | |||
| _roles = new Dictionary<ulong, Role>(); | |||
| Status = UserStatus.Offline; | |||
| if (server == null) | |||