@@ -148,27 +148,6 @@ | |||||
<Compile Include="..\Discord.Net\Audio\VoiceBuffer.cs"> | <Compile Include="..\Discord.Net\Audio\VoiceBuffer.cs"> | ||||
<Link>Audio\VoiceBuffer.cs</Link> | <Link>Audio\VoiceBuffer.cs</Link> | ||||
</Compile> | </Compile> | ||||
<Compile Include="..\Discord.Net\Collections\AsyncCollection.cs"> | |||||
<Link>Collections\AsyncCollection.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Collections\Channels.cs"> | |||||
<Link>Collections\Channels.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Collections\Members.cs"> | |||||
<Link>Collections\Members.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Collections\Messages.cs"> | |||||
<Link>Collections\Messages.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Collections\Roles.cs"> | |||||
<Link>Collections\Roles.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Collections\Servers.cs"> | |||||
<Link>Collections\Servers.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Collections\Users.cs"> | |||||
<Link>Collections\Users.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\DiscordAPIClient.cs"> | <Compile Include="..\Discord.Net\DiscordAPIClient.cs"> | ||||
<Link>DiscordAPIClient.cs</Link> | <Link>DiscordAPIClient.cs</Link> | ||||
</Compile> | </Compile> | ||||
@@ -250,6 +229,9 @@ | |||||
<Compile Include="..\Discord.Net\HttpException.cs"> | <Compile Include="..\Discord.Net\HttpException.cs"> | ||||
<Link>HttpException.cs</Link> | <Link>HttpException.cs</Link> | ||||
</Compile> | </Compile> | ||||
<Compile Include="..\Discord.Net\Models\AsyncCollection.cs"> | |||||
<Link>Models\AsyncCollection.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Models\Channel.cs"> | <Compile Include="..\Discord.Net\Models\Channel.cs"> | ||||
<Link>Models\Channel.cs</Link> | <Link>Models\Channel.cs</Link> | ||||
</Compile> | </Compile> | ||||
@@ -1,47 +0,0 @@ | |||||
using System; | |||||
namespace Discord.Collections | |||||
{ | |||||
internal sealed class Channels : AsyncCollection<Channel> | |||||
{ | |||||
public Channels(DiscordClient client, object writerLock) | |||||
: base(client, writerLock) { } | |||||
public Channel GetOrAdd(string id, string serverId, string recipientId = null) | |||||
=> GetOrAdd(id, () => new Channel(_client, id, serverId, recipientId)); | |||||
protected override void OnCreated(Channel item) | |||||
{ | |||||
if (!item.IsPrivate) | |||||
item.Server.AddChannel(item.Id); | |||||
if (item.RecipientId != null) | |||||
{ | |||||
var user = item.Recipient; | |||||
if (user.PrivateChannelId != null) | |||||
throw new Exception("User already has a private channel."); | |||||
user.PrivateChannelId = item.Id; | |||||
user.AddRef(); | |||||
} | |||||
} | |||||
protected override void OnRemoved(Channel item) | |||||
{ | |||||
if (!item.IsPrivate) | |||||
{ | |||||
var server = item.Server; | |||||
if (server != null) | |||||
item.Server.RemoveChannel(item.Id); | |||||
} | |||||
if (item.RecipientId != null) | |||||
{ | |||||
var user = item.Recipient; | |||||
if (user != null) | |||||
{ | |||||
if (user.PrivateChannelId != item.Id) | |||||
throw new Exception("User has a different private channel."); | |||||
user.PrivateChannelId = null; | |||||
user.RemoveRef(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,42 +0,0 @@ | |||||
namespace Discord.Collections | |||||
{ | |||||
internal sealed class Members : AsyncCollection<Member> | |||||
{ | |||||
public Members(DiscordClient client, object writerLock) | |||||
: base(client, writerLock) { } | |||||
private string GetKey(string userId, string serverId) | |||||
=> serverId + '_' + userId; | |||||
public Member this[string userId, string serverId] | |||||
=> this[GetKey(userId, serverId)]; | |||||
public Member GetOrAdd(string userId, string serverId) | |||||
=> GetOrAdd(GetKey(userId, serverId), () => new Member(_client, userId, serverId)); | |||||
public Member TryRemove(string userId, string serverId) | |||||
=> TryRemove(GetKey(userId, serverId)); | |||||
protected override void OnCreated(Member item) | |||||
{ | |||||
item.Server.AddMember(item); | |||||
item.User.AddServer(item.ServerId); | |||||
item.User.AddRef(); | |||||
if (item.UserId == _client.CurrentUserId) | |||||
item.Server.CurrentMember = item; | |||||
} | |||||
protected override void OnRemoved(Member item) | |||||
{ | |||||
var server = item.Server; | |||||
if (server != null) | |||||
{ | |||||
server.RemoveMember(item); | |||||
if (item.UserId == _client.CurrentUserId) | |||||
server.CurrentMember = null; | |||||
} | |||||
var user = item.User; | |||||
if (user != null) | |||||
{ | |||||
user.RemoveServer(item.ServerId); | |||||
user.RemoveRef(); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,26 +0,0 @@ | |||||
namespace Discord.Collections | |||||
{ | |||||
internal sealed class Messages : AsyncCollection<Message> | |||||
{ | |||||
public Messages(DiscordClient client, object writerLock) | |||||
: base(client, writerLock) { } | |||||
public Message GetOrAdd(string id, string channelId, string userId) | |||||
=> GetOrAdd(id, () => new Message(_client, id, channelId, userId)); | |||||
protected override void OnCreated(Message item) | |||||
{ | |||||
item.Channel.AddMessage(item.Id); | |||||
item.User.AddRef(); | |||||
} | |||||
protected override void OnRemoved(Message item) | |||||
{ | |||||
var channel = item.Channel; | |||||
if (channel != null) | |||||
channel.RemoveMessage(item.Id); | |||||
var user = item.User; | |||||
if (user != null) | |||||
user.RemoveRef(); | |||||
} | |||||
} | |||||
} |
@@ -1,22 +0,0 @@ | |||||
namespace Discord.Collections | |||||
{ | |||||
internal sealed class Roles : AsyncCollection<Role> | |||||
{ | |||||
public Roles(DiscordClient client, object writerLock) | |||||
: base(client, writerLock) { } | |||||
public Role GetOrAdd(string id, string serverId) | |||||
=> GetOrAdd(id, () => new Role(_client, id, serverId)); | |||||
protected override void OnCreated(Role item) | |||||
{ | |||||
item.Server.AddRole(item.Id); | |||||
} | |||||
protected override void OnRemoved(Role item) | |||||
{ | |||||
var server = item.Server; | |||||
if (server != null) | |||||
item.Server.RemoveRole(item.Id); | |||||
} | |||||
} | |||||
} |
@@ -1,30 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
namespace Discord.Collections | |||||
{ | |||||
internal sealed class Servers : AsyncCollection<Server> | |||||
{ | |||||
public Servers(DiscordClient client, object writerLock) | |||||
: base(client, writerLock) { } | |||||
public Server GetOrAdd(string id) | |||||
=> base.GetOrAdd(id, () => new Server(_client, id)); | |||||
protected override void OnRemoved(Server item) | |||||
{ | |||||
var channels = _client.Channels; | |||||
foreach (var channelId in item.ChannelIds) | |||||
channels.TryRemove(channelId); | |||||
var members = _client.Members; | |||||
foreach (var userId in item.UserIds) | |||||
members.TryRemove(userId, item.Id); | |||||
var roles = _client.Roles; | |||||
foreach (var roleId in item.RoleIds) | |||||
roles.TryRemove(roleId); | |||||
} | |||||
} | |||||
} |
@@ -1,10 +0,0 @@ | |||||
namespace Discord.Collections | |||||
{ | |||||
internal sealed class Users : AsyncCollection<User> | |||||
{ | |||||
public Users(DiscordClient client, object writerLock) | |||||
: base(client, writerLock) { } | |||||
public User GetOrAdd(string id) => GetOrAdd(id, () => new User(_client, id)); | |||||
} | |||||
} |
@@ -5,6 +5,21 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public class BanEventArgs : EventArgs | |||||
{ | |||||
public User User { get; } | |||||
public string UserId { get; } | |||||
public Server Server { get; } | |||||
public string ServerId => Server.Id; | |||||
internal BanEventArgs(User user, string userId, Server server) | |||||
{ | |||||
User = user; | |||||
UserId = userId; | |||||
Server = server; | |||||
} | |||||
} | |||||
public partial class DiscordClient | public partial class DiscordClient | ||||
{ | { | ||||
public event EventHandler<BanEventArgs> BanAdded; | public event EventHandler<BanEventArgs> BanAdded; | ||||
@@ -1,4 +1,3 @@ | |||||
using Discord.Collections; | |||||
using Discord.Net; | using Discord.Net; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -8,7 +7,16 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public sealed class ChannelEventArgs : EventArgs | |||||
internal sealed class Channels : AsyncCollection<Channel> | |||||
{ | |||||
public Channels(DiscordClient client, object writerLock) | |||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||||
public Channel GetOrAdd(string id, string serverId, string recipientId = null) | |||||
=> GetOrAdd(id, () => new Channel(_client, id, serverId, recipientId)); | |||||
} | |||||
public class ChannelEventArgs : EventArgs | |||||
{ | { | ||||
public Channel Channel { get; } | public Channel Channel { get; } | ||||
public string ChannelId => Channel.Id; | public string ChannelId => Channel.Id; | ||||
@@ -1,4 +1,3 @@ | |||||
using Discord.Collections; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
@@ -6,54 +5,66 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public sealed class MemberTypingEventArgs : EventArgs | |||||
internal sealed class Members : AsyncCollection<Member> | |||||
{ | |||||
public Members(DiscordClient client, object writerLock) | |||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||||
private string GetKey(string userId, string serverId) | |||||
=> serverId + '_' + userId; | |||||
public Member this[string userId, string serverId] | |||||
=> this[GetKey(userId, serverId)]; | |||||
public Member GetOrAdd(string userId, string serverId) | |||||
=> GetOrAdd(GetKey(userId, serverId), () => new Member(_client, userId, serverId)); | |||||
public Member TryRemove(string userId, string serverId) | |||||
=> TryRemove(GetKey(userId, serverId)); | |||||
} | |||||
public class MemberEventArgs : EventArgs | |||||
{ | { | ||||
public Channel Channel { get; } | |||||
public string ChannelId => Channel.Id; | |||||
public Server Server => Channel.Server; | |||||
public string ServerId => Channel.ServerId; | |||||
public Member Member { get; } | public Member Member { get; } | ||||
public string UserId => User.Id; | |||||
public User User => Member.User; | public User User => Member.User; | ||||
public string UserId => Member.UserId; | |||||
public Server Server => Member.Server; | |||||
public string ServerId => Member.ServerId; | |||||
internal MemberEventArgs(Member member) { Member = member; } | |||||
} | |||||
public class MemberChannelEventArgs : MemberEventArgs | |||||
{ | |||||
public Channel Channel { get; } | |||||
public string ChannelId => Channel.Id; | |||||
internal MemberTypingEventArgs(Member member, Channel channel) | |||||
internal MemberChannelEventArgs(Member member, Channel channel) | |||||
: base(member) | |||||
{ | { | ||||
Member = member; | |||||
Channel = channel; | Channel = channel; | ||||
} | } | ||||
} | } | ||||
public sealed class MemberIsSpeakingEventArgs : EventArgs | |||||
public class MemberIsSpeakingEventArgs : MemberChannelEventArgs | |||||
{ | { | ||||
public Channel Channel => Member.VoiceChannel; | |||||
public string ChannelId => Member.VoiceChannelId; | |||||
public Server Server => Member.Server; | |||||
public string ServerId => Member.ServerId; | |||||
public User User => Member.User; | |||||
public string UserId => Member.UserId; | |||||
public Member Member { get; } | |||||
public bool IsSpeaking { get; } | public bool IsSpeaking { get; } | ||||
internal MemberIsSpeakingEventArgs(Member member, bool isSpeaking) | |||||
internal MemberIsSpeakingEventArgs(Member member, Channel channel, bool isSpeaking) | |||||
: base(member, channel) | |||||
{ | { | ||||
Member = member; | |||||
IsSpeaking = isSpeaking; | IsSpeaking = isSpeaking; | ||||
} | } | ||||
} | } | ||||
public partial class DiscordClient | public partial class DiscordClient | ||||
{ | { | ||||
public event EventHandler<MemberTypingEventArgs> UserIsTyping; | |||||
public event EventHandler<MemberChannelEventArgs> UserIsTyping; | |||||
private void RaiseUserIsTyping(Member member, Channel channel) | private void RaiseUserIsTyping(Member member, Channel channel) | ||||
{ | { | ||||
if (UserIsTyping != null) | if (UserIsTyping != null) | ||||
RaiseEvent(nameof(UserIsTyping), () => UserIsTyping(this, new MemberTypingEventArgs(member, channel))); | |||||
RaiseEvent(nameof(UserIsTyping), () => UserIsTyping(this, new MemberChannelEventArgs(member, channel))); | |||||
} | } | ||||
public event EventHandler<MemberIsSpeakingEventArgs> UserIsSpeaking; | public event EventHandler<MemberIsSpeakingEventArgs> UserIsSpeaking; | ||||
private void RaiseUserIsSpeaking(Member member, bool isSpeaking) | |||||
private void RaiseUserIsSpeaking(Member member, Channel channel, bool isSpeaking) | |||||
{ | { | ||||
if (UserIsSpeaking != null) | if (UserIsSpeaking != null) | ||||
RaiseEvent(nameof(UserIsSpeaking), () => UserIsSpeaking(this, new MemberIsSpeakingEventArgs(member, isSpeaking))); | |||||
RaiseEvent(nameof(UserIsSpeaking), () => UserIsSpeaking(this, new MemberIsSpeakingEventArgs(member, channel, isSpeaking))); | |||||
} | } | ||||
internal Members Members => _members; | internal Members Members => _members; | ||||
@@ -1,5 +1,4 @@ | |||||
using Discord.API; | using Discord.API; | ||||
using Discord.Collections; | |||||
using Discord.Net; | using Discord.Net; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -9,6 +8,30 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
internal sealed class Messages : AsyncCollection<Message> | |||||
{ | |||||
public Messages(DiscordClient client, object writerLock) | |||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||||
public Message GetOrAdd(string id, string channelId, string userId) | |||||
=> GetOrAdd(id, () => new Message(_client, id, channelId, userId)); | |||||
} | |||||
public class MessageEventArgs : EventArgs | |||||
{ | |||||
public Message Message { get; } | |||||
public string MessageId => Message.Id; | |||||
public Member Member => Message.Member; | |||||
public Channel Channel => Message.Channel; | |||||
public string ChannelId => Message.ChannelId; | |||||
public Server Server => Message.Server; | |||||
public string ServerId => Message.ServerId; | |||||
public User User => Member.User; | |||||
public string UserId => Message.UserId; | |||||
internal MessageEventArgs(Message msg) { Message = msg; } | |||||
} | |||||
public partial class DiscordClient | public partial class DiscordClient | ||||
{ | { | ||||
public const int MaxMessageSize = 2000; | public const int MaxMessageSize = 2000; | ||||
@@ -1,4 +1,3 @@ | |||||
using Discord.Collections; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
@@ -6,6 +5,25 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
internal sealed class Roles : AsyncCollection<Role> | |||||
{ | |||||
public Roles(DiscordClient client, object writerLock) | |||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||||
public Role GetOrAdd(string id, string serverId) | |||||
=> GetOrAdd(id, () => new Role(_client, id, serverId)); | |||||
} | |||||
public class RoleEventArgs : EventArgs | |||||
{ | |||||
public Role Role { get; } | |||||
public string RoleId => Role.Id; | |||||
public Server Server => Role.Server; | |||||
public string ServerId => Role.ServerId; | |||||
internal RoleEventArgs(Role role) { Role = role; } | |||||
} | |||||
public partial class DiscordClient | public partial class DiscordClient | ||||
{ | { | ||||
public event EventHandler<RoleEventArgs> RoleCreated; | public event EventHandler<RoleEventArgs> RoleCreated; | ||||
@@ -1,4 +1,3 @@ | |||||
using Discord.Collections; | |||||
using Discord.Net; | using Discord.Net; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -8,7 +7,16 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public sealed class ServerEventArgs : EventArgs | |||||
internal sealed class Servers : AsyncCollection<Server> | |||||
{ | |||||
public Servers(DiscordClient client, object writerLock) | |||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||||
public Server GetOrAdd(string id) | |||||
=> base.GetOrAdd(id, () => new Server(_client, id)); | |||||
} | |||||
public class ServerEventArgs : EventArgs | |||||
{ | { | ||||
public Server Server { get; } | public Server Server { get; } | ||||
public string ServerId => Server.Id; | public string ServerId => Server.Id; | ||||
@@ -1,5 +1,4 @@ | |||||
using Discord.API; | using Discord.API; | ||||
using Discord.Collections; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
@@ -7,6 +6,14 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
internal sealed class Users : AsyncCollection<User> | |||||
{ | |||||
public Users(DiscordClient client, object writerLock) | |||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||||
public User GetOrAdd(string id) => GetOrAdd(id, () => new User(_client, id)); | |||||
} | |||||
public sealed class UserEventArgs : EventArgs | public sealed class UserEventArgs : EventArgs | ||||
{ | { | ||||
public User User { get; } | public User User { get; } | ||||
@@ -1,5 +1,6 @@ | |||||
using Discord.Audio; | using Discord.Audio; | ||||
using System; | using System; | ||||
using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
@@ -1,5 +1,4 @@ | |||||
using Discord.API; | using Discord.API; | ||||
using Discord.Collections; | |||||
using Discord.Net.WebSockets; | using Discord.Net.WebSockets; | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | using System; | ||||
@@ -10,54 +9,6 @@ using System.Threading.Tasks; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public sealed class MessageEventArgs : EventArgs | |||||
{ | |||||
public Message Message { get; } | |||||
public string MessageId => Message.Id; | |||||
public Member Member => Message.Member; | |||||
public Channel Channel => Message.Channel; | |||||
public string ChannelId => Message.ChannelId; | |||||
public Server Server => Message.Server; | |||||
public string ServerId => Message.ServerId; | |||||
public User User => Member.User; | |||||
public string UserId => Message.UserId; | |||||
internal MessageEventArgs(Message msg) { Message = msg; } | |||||
} | |||||
public sealed class RoleEventArgs : EventArgs | |||||
{ | |||||
public Role Role { get; } | |||||
public string RoleId => Role.Id; | |||||
public Server Server => Role.Server; | |||||
public string ServerId => Role.ServerId; | |||||
internal RoleEventArgs(Role role) { Role = role; } | |||||
} | |||||
public sealed class BanEventArgs : EventArgs | |||||
{ | |||||
public User User { get; } | |||||
public string UserId { get; } | |||||
public Server Server { get; } | |||||
public string ServerId => Server.Id; | |||||
internal BanEventArgs(User user, string userId, Server server) | |||||
{ | |||||
User = user; | |||||
UserId = userId; | |||||
Server = server; | |||||
} | |||||
} | |||||
public sealed class MemberEventArgs : EventArgs | |||||
{ | |||||
public Member Member { get; } | |||||
public User User => Member.User; | |||||
public string UserId => Member.UserId; | |||||
public Server Server => Member.Server; | |||||
public string ServerId => Member.ServerId; | |||||
internal MemberEventArgs(Member member) { Member = member; } | |||||
} | |||||
/// <summary> Provides a connection to the DiscordApp service. </summary> | /// <summary> Provides a connection to the DiscordApp service. </summary> | ||||
public partial class DiscordClient : DiscordWSClient | public partial class DiscordClient : DiscordWSClient | ||||
{ | { | ||||
@@ -106,7 +57,7 @@ namespace Discord | |||||
if (member.ServerId == e.ServerId && member.IsSpeaking) | if (member.ServerId == e.ServerId && member.IsSpeaking) | ||||
{ | { | ||||
member.IsSpeaking = false; | member.IsSpeaking = false; | ||||
RaiseUserIsSpeaking(member, false); | |||||
RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false); | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
@@ -157,10 +108,10 @@ namespace Discord | |||||
$"Deleted Role: {e.Server?.Name ?? "[Private]"}/{e.Role.Name}" + | $"Deleted Role: {e.Server?.Name ?? "[Private]"}/{e.Role.Name}" + | ||||
(showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.RoleId})." : "")); | (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.RoleId})." : "")); | ||||
BanAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | BanAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Added Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? "Unknown"}" + | |||||
$"Added Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? e.UserId}" + | |||||
(showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); | (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); | ||||
BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? "Unknown"}" + | |||||
$"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? e.UserId}" + | |||||
(showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); | (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); | ||||
UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Added Member: {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + | $"Added Member: {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + | ||||
@@ -246,7 +197,8 @@ namespace Discord | |||||
if (member.IsSpeaking != value) | if (member.IsSpeaking != value) | ||||
{ | { | ||||
member.IsSpeaking = value; | member.IsSpeaking = value; | ||||
RaiseUserIsSpeaking(member, value); | |||||
var channel = _channels[_voiceSocket.CurrentChannelId]; | |||||
RaiseUserIsSpeaking(member, channel, value); | |||||
if (Config.TrackActivity) | if (Config.TrackActivity) | ||||
member.UpdateActivity(); | member.UpdateActivity(); | ||||
} | } | ||||
@@ -665,7 +617,7 @@ namespace Discord | |||||
if (data.ChannelId != member.VoiceChannelId && member.IsSpeaking) | if (data.ChannelId != member.VoiceChannelId && member.IsSpeaking) | ||||
{ | { | ||||
member.IsSpeaking = false; | member.IsSpeaking = false; | ||||
RaiseUserIsSpeaking(member, false); | |||||
RaiseUserIsSpeaking(member, _channels[member.VoiceChannelId], false); | |||||
} | } | ||||
member.Update(data); | member.Update(data); | ||||
RaiseUserVoiceStateUpdated(member); | RaiseUserVoiceStateUpdated(member); | ||||
@@ -4,7 +4,7 @@ using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
namespace Discord.Collections | |||||
namespace Discord | |||||
{ | { | ||||
internal abstract class AsyncCollection<TValue> : IEnumerable<TValue> | internal abstract class AsyncCollection<TValue> : IEnumerable<TValue> | ||||
where TValue : class | where TValue : class | ||||
@@ -52,13 +52,16 @@ namespace Discord.Collections | |||||
protected readonly DiscordClient _client; | protected readonly DiscordClient _client; | ||||
protected readonly ConcurrentDictionary<string, TValue> _dictionary; | protected readonly ConcurrentDictionary<string, TValue> _dictionary; | ||||
private readonly Action<TValue> _onCache, _onUncache; | |||||
protected AsyncCollection(DiscordClient client, object writerLock) | |||||
protected AsyncCollection(DiscordClient client, object writerLock, Action<TValue> onCache, Action<TValue> onUncache) | |||||
{ | { | ||||
_client = client; | _client = client; | ||||
_writerLock = writerLock; | _writerLock = writerLock; | ||||
_dictionary = new ConcurrentDictionary<string, TValue>(); | _dictionary = new ConcurrentDictionary<string, TValue>(); | ||||
} | |||||
_onCache = onCache; | |||||
_onUncache = onUncache; | |||||
} | |||||
public TValue this[string key] | public TValue this[string key] | ||||
{ | { | ||||
@@ -85,7 +88,7 @@ namespace Discord.Collections | |||||
result = _dictionary.GetOrAdd(key, newItem); | result = _dictionary.GetOrAdd(key, newItem); | ||||
if (result == newItem) | if (result == newItem) | ||||
{ | { | ||||
OnCreated(newItem); | |||||
_onCache(result); | |||||
RaiseItemCreated(result); | RaiseItemCreated(result); | ||||
} | } | ||||
} | } | ||||
@@ -100,7 +103,7 @@ namespace Discord.Collections | |||||
TValue result; | TValue result; | ||||
if (_dictionary.TryRemove(key, out result)) | if (_dictionary.TryRemove(key, out result)) | ||||
{ | { | ||||
OnRemoved(result); //TODO: If this object is accessed before OnRemoved finished firing, properties such as Server.Channels will have null elements | |||||
_onUncache(result); //TODO: If this object is accessed before OnRemoved finished firing, properties such as Server.Channels will have null elements | |||||
return result; | return result; | ||||
} | } | ||||
} | } | ||||
@@ -130,16 +133,7 @@ namespace Discord.Collections | |||||
} | } | ||||
} | } | ||||
protected virtual void OnCreated(TValue item) { } | |||||
protected virtual void OnRemoved(TValue item) { } | |||||
public IEnumerator<TValue> GetEnumerator() | |||||
{ | |||||
return _dictionary.Select(x => x.Value).GetEnumerator(); | |||||
} | |||||
IEnumerator IEnumerable.GetEnumerator() | |||||
{ | |||||
return GetEnumerator(); | |||||
} | |||||
public IEnumerator<TValue> GetEnumerator() => _dictionary.Select(x => x.Value).GetEnumerator(); | |||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |||||
} | } | ||||
} | } |
@@ -1,5 +1,6 @@ | |||||
using Discord.API; | using Discord.API; | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | |||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
@@ -29,6 +30,7 @@ namespace Discord | |||||
private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
private readonly ConcurrentDictionary<string, bool> _messages; | private readonly ConcurrentDictionary<string, bool> _messages; | ||||
private bool _areMembersStale; | private bool _areMembersStale; | ||||
private bool _hasRef; | |||||
/// <summary> Returns the unique identifier for this channel. </summary> | /// <summary> Returns the unique identifier for this channel. </summary> | ||||
public string Id { get; } | public string Id { get; } | ||||
@@ -101,6 +103,39 @@ namespace Discord | |||||
_permissionOverwrites = _initialPermissionsOverwrites; | _permissionOverwrites = _initialPermissionsOverwrites; | ||||
_areMembersStale = true; | _areMembersStale = true; | ||||
} | } | ||||
internal void OnCached() | |||||
{ | |||||
var server = Server; | |||||
if (server != null) | |||||
server.AddChannel(Id); | |||||
if (RecipientId != null) | |||||
{ | |||||
var user = Recipient; | |||||
if (user != null) | |||||
{ | |||||
user.PrivateChannelId = Id; | |||||
user.AddRef(); | |||||
_hasRef = true; | |||||
} | |||||
} | |||||
} | |||||
internal void OnUncached() | |||||
{ | |||||
var server = Server; | |||||
if (server != null) | |||||
server.RemoveChannel(Id); | |||||
if (RecipientId != null) | |||||
{ | |||||
var user = Recipient; | |||||
if (user != null) | |||||
{ | |||||
user.PrivateChannelId = null; | |||||
if (_hasRef) | |||||
user.RemoveRef(); | |||||
} | |||||
} | |||||
_hasRef = false; | |||||
} | |||||
internal void Update(ChannelReference model) | internal void Update(ChannelReference model) | ||||
{ | { | ||||
@@ -11,6 +11,7 @@ namespace Discord | |||||
{ | { | ||||
private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
private ConcurrentDictionary<string, PackedChannelPermissions> _permissions; | private ConcurrentDictionary<string, PackedChannelPermissions> _permissions; | ||||
private bool _hasRef; | |||||
/// <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; private set; } | public string Name { get; private set; } | ||||
@@ -77,6 +78,41 @@ namespace Discord | |||||
RoleIds = _initialRoleIds; | RoleIds = _initialRoleIds; | ||||
_permissions = new ConcurrentDictionary<string, PackedChannelPermissions>(); | _permissions = new ConcurrentDictionary<string, PackedChannelPermissions>(); | ||||
} | } | ||||
internal void OnCached() | |||||
{ | |||||
var server = Server; | |||||
if (server != null) | |||||
{ | |||||
server.AddMember(this); | |||||
if (UserId == _client.CurrentUserId) | |||||
server.CurrentMember = this; | |||||
} | |||||
var user = User; | |||||
if (user != null) | |||||
{ | |||||
user.AddServer(ServerId); | |||||
user.AddRef(); | |||||
_hasRef = true; | |||||
} | |||||
} | |||||
internal void OnUncached() | |||||
{ | |||||
var server = Server; | |||||
if (server != null) | |||||
{ | |||||
server.RemoveMember(this); | |||||
if (UserId == _client.CurrentUserId) | |||||
server.CurrentMember = null; | |||||
} | |||||
var user = User; | |||||
if (user != null) | |||||
{ | |||||
user.RemoveServer(ServerId); | |||||
if (_hasRef) | |||||
user.RemoveRef(); | |||||
} | |||||
_hasRef = false; | |||||
} | |||||
public override string ToString() => UserId; | public override string ToString() => UserId; | ||||
@@ -93,6 +93,7 @@ namespace Discord | |||||
private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
private string _cleanText; | private string _cleanText; | ||||
private bool _gotRef; | |||||
/// <summary> Returns the global unique identifier for this message. </summary> | /// <summary> Returns the global unique identifier for this message. </summary> | ||||
public string Id { get; internal set; } | public string Id { get; internal set; } | ||||
@@ -154,16 +155,7 @@ namespace Discord | |||||
public User User => _client.Users[UserId]; | public User User => _client.Users[UserId]; | ||||
/// <summary> Returns the author of this message. </summary> | /// <summary> Returns the author of this message. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public Member Member | |||||
{ | |||||
get | |||||
{ | |||||
if (!Channel.IsPrivate) | |||||
return _client.Members[UserId, ServerId]; | |||||
else | |||||
throw new InvalidOperationException("Unable to access Member in a private channel. Use User instead or check for Channel.IsPrivate."); | |||||
} | |||||
} | |||||
public Member Member => _client.Members[UserId, ServerId]; | |||||
internal Message(DiscordClient client, string id, string channelId, string userId) | internal Message(DiscordClient client, string id, string channelId, string userId) | ||||
{ | { | ||||
@@ -174,6 +166,28 @@ namespace Discord | |||||
Attachments = _initialAttachments; | Attachments = _initialAttachments; | ||||
Embeds = _initialEmbeds; | Embeds = _initialEmbeds; | ||||
MentionIds = _initialMentions; | MentionIds = _initialMentions; | ||||
} | |||||
internal void OnCached() | |||||
{ | |||||
var channel = Channel; | |||||
if (channel != null) | |||||
channel.AddMessage(Id); | |||||
var user = User; | |||||
if (user != null) | |||||
{ | |||||
user.AddRef(); | |||||
_gotRef = true; | |||||
} | |||||
} | |||||
internal void OnUncached() | |||||
{ | |||||
var channel = Channel; | |||||
if (channel != null) | |||||
channel.RemoveMessage(Id); | |||||
var user = User; | |||||
if (user != null && _gotRef) | |||||
user.RemoveRef(); | |||||
_gotRef = false; | |||||
} | } | ||||
internal void Update(MessageInfo model) | internal void Update(MessageInfo model) | ||||
@@ -52,7 +52,19 @@ namespace Discord | |||||
if (IsEveryone) | if (IsEveryone) | ||||
Position = int.MinValue; | Position = int.MinValue; | ||||
} | |||||
} | |||||
internal void OnCached() | |||||
{ | |||||
var server = Server; | |||||
if (server != null) | |||||
server.AddRole(Id); | |||||
} | |||||
internal void OnUncached() | |||||
{ | |||||
var server = Server; | |||||
if (server != null) | |||||
server.RemoveRole(Id); | |||||
} | |||||
internal void Update(RoleInfo model) | internal void Update(RoleInfo model) | ||||
{ | { | ||||
@@ -104,6 +104,23 @@ namespace Discord | |||||
_members = new ConcurrentDictionary<string, bool>(); | _members = new ConcurrentDictionary<string, bool>(); | ||||
_roles = new ConcurrentDictionary<string, bool>(); | _roles = new ConcurrentDictionary<string, bool>(); | ||||
} | } | ||||
internal void OnCached() | |||||
{ | |||||
} | |||||
internal void OnUncached() | |||||
{ | |||||
var channels = _client.Channels; | |||||
foreach (var channelId in ChannelIds) | |||||
channels.TryRemove(channelId); | |||||
var members = _client.Members; | |||||
foreach (var userId in UserIds) | |||||
members.TryRemove(userId, Id); | |||||
var roles = _client.Roles; | |||||
foreach (var roleId in RoleIds) | |||||
roles.TryRemove(roleId); | |||||
} | |||||
internal void Update(GuildInfo model) | internal void Update(GuildInfo model) | ||||
{ | { | ||||
@@ -80,7 +80,13 @@ namespace Discord | |||||
_client = client; | _client = client; | ||||
Id = id; | Id = id; | ||||
_servers = new ConcurrentDictionary<string, bool>(); | _servers = new ConcurrentDictionary<string, bool>(); | ||||
} | |||||
} | |||||
internal void OnCached() | |||||
{ | |||||
} | |||||
internal void OnUncached() | |||||
{ | |||||
} | |||||
internal void Update(UserReference model) | internal void Update(UserReference model) | ||||
{ | { | ||||