@@ -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) | |||