@@ -68,7 +68,7 @@ namespace Discord.Commands | |||||
client.MessageReceived += async (s, e) => | client.MessageReceived += async (s, e) => | ||||
{ | { | ||||
if (_allCommands.Count == 0) return; | 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; | string msg = e.Message.RawText; | ||||
if (msg.Length == 0) return; | if (msg.Length == 0) return; | ||||
@@ -9,8 +9,7 @@ namespace Discord.API.Client | |||||
{ | { | ||||
[JsonProperty("type")] | [JsonProperty("type")] | ||||
public string Type { get; set; } | public string Type { get; set; } | ||||
[JsonProperty("id")] | |||||
[JsonConverter(typeof(LongStringConverter))] | |||||
[JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] | |||||
public ulong Id { get; set; } | public ulong Id { get; set; } | ||||
[JsonProperty("deny")] | [JsonProperty("deny")] | ||||
public uint Deny { get; set; } | public uint Deny { get; set; } | ||||
@@ -7,8 +7,8 @@ namespace Discord.API.Client | |||||
{ | { | ||||
[JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] | [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] | ||||
public ulong Id { get; set; } | 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")] | [JsonProperty("name")] | ||||
public string Name { get; set; } | public string Name { get; set; } | ||||
[JsonProperty("type")] | [JsonProperty("type")] | ||||
@@ -5,7 +5,7 @@ namespace Discord.API.Client | |||||
{ | { | ||||
public class MemberReference | public class MemberReference | ||||
{ | { | ||||
[JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] | |||||
[JsonProperty("guild_id"), JsonConverter(typeof(NullableLongStringConverter))] | |||||
public ulong? GuildId { get; set; } | public ulong? GuildId { get; set; } | ||||
[JsonProperty("user")] | [JsonProperty("user")] | ||||
public UserReference User { get; set; } | public UserReference User { get; set; } | ||||
@@ -171,10 +171,7 @@ namespace Discord | |||||
State = ConnectionState.Connecting; | State = ConnectionState.Connecting; | ||||
_disconnectedEvent.Reset(); | _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); | await GatewaySocket.Connect().ConfigureAwait(false); | ||||
List<Task> tasks = new List<Task>(); | List<Task> tasks = new List<Task>(); | ||||
@@ -233,6 +230,9 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
ClientAPI.Token = token; | |||||
GatewaySocket.Token = token; | |||||
//Get gateway and check token | //Get gateway and check token | ||||
try | try | ||||
{ | { | ||||
@@ -309,22 +309,10 @@ namespace Discord | |||||
} | } | ||||
#region Channels | #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; | _channels[channel.Id] = channel; | ||||
return channel; | |||||
} | |||||
} | |||||
private Channel RemoveChannel(ulong id) | private Channel RemoveChannel(ulong id) | ||||
{ | { | ||||
Channel channel; | Channel channel; | ||||
@@ -337,20 +325,26 @@ namespace Discord | |||||
} | } | ||||
return channel; | return channel; | ||||
} | } | ||||
internal Channel GetChannel(ulong id) | |||||
public Channel GetChannel(ulong id) | |||||
{ | { | ||||
Channel channel; | Channel channel; | ||||
_channels.TryGetValue(id, out channel); | _channels.TryGetValue(id, out channel); | ||||
return 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) | internal Channel GetPrivateChannel(ulong recipientId) | ||||
{ | { | ||||
Channel channel; | Channel channel; | ||||
_privateChannels.TryGetValue(recipientId, out channel); | _privateChannels.TryGetValue(recipientId, out channel); | ||||
return channel; | return channel; | ||||
} | } | ||||
internal async Task<Channel> CreatePrivateChannel(User user) | internal async Task<Channel> CreatePrivateChannel(User user) | ||||
{ | { | ||||
var channel = GetPrivateChannel(user.Id); | var channel = GetPrivateChannel(user.Id); | ||||
@@ -358,8 +352,8 @@ namespace Discord | |||||
var request = new CreatePrivateChannelRequest() { RecipientId = user.Id }; | var request = new CreatePrivateChannelRequest() { RecipientId = user.Id }; | ||||
var response = await ClientAPI.Send(request).ConfigureAwait(false); | 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); | channel.Update(response); | ||||
return channel; | return channel; | ||||
} | } | ||||
@@ -453,6 +447,7 @@ namespace Discord | |||||
SessionId = data.SessionId; | SessionId = data.SessionId; | ||||
PrivateUser = new User(this, data.User.Id, null); | PrivateUser = new User(this, data.User.Id, null); | ||||
PrivateUser.Update(data.User); | PrivateUser.Update(data.User); | ||||
CurrentUser = new Profile(this, data.User.Id); | |||||
CurrentUser.Update(data.User); | CurrentUser.Update(data.User); | ||||
foreach (var model in data.Guilds) | foreach (var model in data.Guilds) | ||||
{ | { | ||||
@@ -464,7 +459,7 @@ namespace Discord | |||||
} | } | ||||
foreach (var model in data.PrivateChannels) | 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); | channel.Update(model); | ||||
} | } | ||||
} | } | ||||
@@ -523,10 +518,22 @@ namespace Discord | |||||
case "CHANNEL_CREATE": | case "CHANNEL_CREATE": | ||||
{ | { | ||||
var data = e.Payload.ToObject<ChannelCreateEvent>(_serializer); | 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; | break; | ||||
case "CHANNEL_UPDATE": | case "CHANNEL_UPDATE": | ||||
@@ -27,15 +27,22 @@ namespace Discord | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||||
public static bool HasBit(this uint value, byte bit) => ((value >> bit) & 1U) == 1; | 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) | TKey key, Func<TKey, TValue> factory, out TValue result) | ||||
where TValue : class | |||||
{ | { | ||||
TValue newValue = null; | |||||
while (true) | while (true) | ||||
{ | { | ||||
if (d.TryGetValue(key, out result)) | if (d.TryGetValue(key, out result)) | ||||
return false; | return false; | ||||
if (d.TryAdd(key, factory(key))) | |||||
if (newValue == null) | |||||
newValue = factory(key); | |||||
if (d.TryAdd(key, newValue)) | |||||
{ | |||||
result = newValue; | |||||
return true; | return true; | ||||
} | |||||
} | } | ||||
} | } | ||||
@@ -112,7 +112,6 @@ namespace Discord | |||||
: this(client, id) | : this(client, id) | ||||
{ | { | ||||
Recipient = recipient; | Recipient = recipient; | ||||
Name = $"@{recipient}"; | |||||
AddUser(client.PrivateUser); | AddUser(client.PrivateUser); | ||||
AddUser(recipient); | AddUser(recipient); | ||||
} | } | ||||
@@ -143,7 +142,10 @@ namespace Discord | |||||
if (model.Topic != null) | if (model.Topic != null) | ||||
Topic = model.Topic; | Topic = model.Topic; | ||||
if (model.Recipient != null) | if (model.Recipient != null) | ||||
{ | |||||
Recipient.Update(model.Recipient); | Recipient.Update(model.Recipient); | ||||
Name = $"@{Recipient}"; | |||||
} | |||||
if (model.PermissionOverwrites != null) | if (model.PermissionOverwrites != null) | ||||
{ | { | ||||
@@ -263,9 +265,13 @@ namespace Discord | |||||
public Message GetMessage(ulong id) | 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, | public async Task<Message[]> DownloadMessages(int limit = 100, ulong? relativeMessageId = null, | ||||
RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) | RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) | ||||
@@ -556,6 +562,17 @@ namespace Discord | |||||
} | } | ||||
public User GetUser(ulong id) | 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; | Member result; | ||||
_users.TryGetValue(id, out result); | _users.TryGetValue(id, out result); | ||||
return result.User; | return result.User; | ||||
@@ -28,7 +28,6 @@ namespace Discord | |||||
public static readonly Color LightGrey = PresetColor(0x979C9F); | public static readonly Color LightGrey = PresetColor(0x979C9F); | ||||
public static readonly Color DarkerGrey = PresetColor(0x546E7A); | public static readonly Color DarkerGrey = PresetColor(0x546E7A); | ||||
private static Color PresetColor(uint packedValue) | private static Color PresetColor(uint packedValue) | ||||
{ | { | ||||
Color color = new Color(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) | internal Message(ulong id, Channel channel, ulong userId) | ||||
{ | { | ||||
Id = id; | |||||
Channel = channel; | |||||
_userId = userId; | |||||
Attachments = _initialAttachments; | Attachments = _initialAttachments; | ||||
Embeds = _initialEmbeds; | Embeds = _initialEmbeds; | ||||
} | } | ||||
@@ -11,20 +11,21 @@ namespace Discord | |||||
internal DiscordClient Client { get; } | internal DiscordClient Client { get; } | ||||
/// <summary> Gets the unique identifier for this user. </summary> | /// <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> | /// <summary> Gets the email for this user. </summary> | ||||
public string Email { get; private set; } | public string Email { get; private set; } | ||||
/// <summary> Gets if the email for this user has been verified. </summary> | /// <summary> Gets if the email for this user has been verified. </summary> | ||||
public bool? IsVerified { get; private set; } | public bool? IsVerified { get; private set; } | ||||
internal Profile(DiscordClient client) | |||||
internal Profile(DiscordClient client, ulong id) | |||||
{ | { | ||||
Client = client; | Client = client; | ||||
Id = id; | |||||
} | } | ||||
internal void Update(APIUser model) | internal void Update(APIUser model) | ||||
{ | { | ||||
Id = model.Id; | |||||
Email = model.Email; | Email = model.Email; | ||||
IsVerified = model.IsVerified; | IsVerified = model.IsVerified; | ||||
} | } | ||||
@@ -52,6 +52,7 @@ namespace Discord | |||||
{ | { | ||||
Id = id; | Id = id; | ||||
Server = server; | Server = server; | ||||
Permissions = new ServerPermissions(0); | Permissions = new ServerPermissions(0); | ||||
Permissions.Lock(); | Permissions.Lock(); | ||||
Color = new Color(0); | Color = new Color(0); | ||||
@@ -78,6 +78,7 @@ namespace Discord | |||||
{ | { | ||||
Client = client; | Client = client; | ||||
Id = id; | Id = id; | ||||
_channels = new ConcurrentDictionary<ulong, Channel>(); | _channels = new ConcurrentDictionary<ulong, Channel>(); | ||||
_roles = new ConcurrentDictionary<ulong, Role>(); | _roles = new ConcurrentDictionary<ulong, Role>(); | ||||
_users = new ConcurrentDictionary<ulong, Member>(); | _users = new ConcurrentDictionary<ulong, Member>(); | ||||
@@ -97,7 +98,7 @@ namespace Discord | |||||
if (model.AFKTimeout != null) | if (model.AFKTimeout != null) | ||||
AFKTimeout = model.AFKTimeout.Value; | AFKTimeout = model.AFKTimeout.Value; | ||||
_afkChannelId = model.AFKChannelId.Value; //Can be null | |||||
_afkChannelId = model.AFKChannelId; //Can be null | |||||
if (model.JoinedAt != null) | if (model.JoinedAt != null) | ||||
JoinedAt = model.JoinedAt.Value; | JoinedAt = model.JoinedAt.Value; | ||||
if (model.OwnerId != null) | if (model.OwnerId != null) | ||||
@@ -196,7 +197,11 @@ namespace Discord | |||||
#region Channels | #region Channels | ||||
internal Channel AddChannel(ulong id) | 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) | internal Channel RemoveChannel(ulong id) | ||||
{ | { | ||||
Channel channel; | Channel channel; | ||||
@@ -136,9 +136,10 @@ namespace Discord | |||||
internal User(DiscordClient client, ulong id, Server server) | internal User(DiscordClient client, ulong id, Server server) | ||||
{ | { | ||||
Client = client; | Client = client; | ||||
Id = id; | |||||
Server = server; | Server = server; | ||||
_roles = new Dictionary<ulong, Role>(); | |||||
_roles = new Dictionary<ulong, Role>(); | |||||
Status = UserStatus.Offline; | Status = UserStatus.Offline; | ||||
if (server == null) | if (server == null) | ||||