@@ -5,6 +5,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace Discord
namespace Discord
@@ -12,12 +13,14 @@ namespace Discord
/// <summary> Provides a connection to the DiscordApp service. </summary>
/// <summary> Provides a connection to the DiscordApp service. </summary>
public sealed partial class DiscordClient : DiscordWSClient
public sealed partial class DiscordClient : DiscordWSClient
{
{
public static readonly string Version = typeof(DiscordClientConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3);
private readonly DiscordAPIClient _api;
private readonly DiscordAPIClient _api;
private readonly Random _rand;
private readonly Random _rand;
private readonly JsonSerializer _socketSerializer, _ messageImporter;
private readonly JsonSerializer _messageImporter;
private readonly ConcurrentQueue<Message> _pendingMessages;
private readonly ConcurrentQueue<Message> _pendingMessages;
private readonly ConcurrentDictionary<long, DiscordWSClient> _voiceClients;
private readonly ConcurrentDictionary<long, DiscordWSClient> _voiceClients;
private readonly Dictionary<Type, IService> _service s;
private readonly Dictionary<Type, object> _singleton s;
private bool _sentInitialLog;
private bool _sentInitialLog;
private uint _nextVoiceClientId;
private uint _nextVoiceClientId;
private UserStatus _status;
private UserStatus _status;
@@ -47,7 +50,7 @@ namespace Discord
_roles = new Roles(this, cacheLock);
_roles = new Roles(this, cacheLock);
_servers = new Servers(this, cacheLock);
_servers = new Servers(this, cacheLock);
_globalUsers = new GlobalUsers(this, cacheLock);
_globalUsers = new GlobalUsers(this, cacheLock);
_services = new Dictionary<Type, IService >();
_singletons = new Dictionary<Type, object >();
_status = UserStatus.Online;
_status = UserStatus.Online;
@@ -162,16 +165,9 @@ namespace Discord
if (Config.UseMessageQueue)
if (Config.UseMessageQueue)
_pendingMessages = new ConcurrentQueue<Message>();
_pendingMessages = new ConcurrentQueue<Message>();
_socketSerializer = new JsonSerializer();
_socketSerializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
#if TEST_RESPONSES
_serializer.CheckAdditionalContent = true;
_serializer.MissingMemberHandling = MissingMemberHandling.Error;
#endif
_messageImporter = new JsonSerializer();
_messageImporter = new JsonSerializer();
_messageImporter.ContractResolver = new MessageImporter Resolver();
_messageImporter.ContractResolver = new Message.ImportResolver();
}
}
internal override VoiceWebSocket CreateVoiceSocket()
internal override VoiceWebSocket CreateVoiceSocket()
{
{
@@ -276,25 +272,34 @@ namespace Discord
_privateUser = null;
_privateUser = null;
}
}
public T AddSingleton<T>(T obj)
where T : class
{
_singletons.Add(typeof(T), obj);
return obj;
}
public T GetSingleton<T>(bool required = true)
where T : class
{
object singleton;
T singletonT = null;
if (_singletons.TryGetValue(typeof(T), out singleton))
singletonT = singleton as T;
if (singletonT == null && required)
throw new InvalidOperationException($"This operation requires {nameof(T)} to be added to {nameof(DiscordClient)}.");
return singletonT;
}
public T AddService<T>(T obj)
public T AddService<T>(T obj)
where T : class, IService
where T : class, IService
{
{
_services.Add(typeof(T), obj);
AddSingleton( obj);
obj.Install(this);
obj.Install(this);
return obj;
return obj;
}
}
public T GetService<T>(bool required = true)
public T GetService<T>(bool required = true)
where T : class, IService
where T : class, IService
{
IService service;
T serviceT = null;
if (_services.TryGetValue(typeof(T), out service))
serviceT = service as T;
if (serviceT == null && required)
throw new InvalidOperationException($"This operation requires {nameof(T)} to be added to {nameof(DiscordClient)}.");
return serviceT;
}
=> GetSingleton<T>(required);
protected override IEnumerable<Task> GetTasks()
protected override IEnumerable<Task> GetTasks()
{
{
@@ -314,7 +319,7 @@ namespace Discord
case "READY": //Resync
case "READY": //Resync
{
{
base.OnReceivedEvent(e).Wait(); //This cannot be an await, or we'll get later messages before we're ready
base.OnReceivedEvent(e).Wait(); //This cannot be an await, or we'll get later messages before we're ready
var data = e.Payload.ToObject<ReadyEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<ReadyEvent>(_dataS ocketSerializer);
_privateUser = _users.GetOrAdd(data.User.Id, null);
_privateUser = _users.GetOrAdd(data.User.Id, null);
_privateUser.Update(data.User);
_privateUser.Update(data.User);
_privateUser.Global.Update(data.User);
_privateUser.Global.Update(data.User);
@@ -339,7 +344,7 @@ namespace Discord
//Servers
//Servers
case "GUILD_CREATE":
case "GUILD_CREATE":
{
{
var data = e.Payload.ToObject<GuildCreateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<GuildCreateEvent>(_dataS ocketSerializer);
if (data.Unavailable != true)
if (data.Unavailable != true)
{
{
var server = _servers.GetOrAdd(data.Id);
var server = _servers.GetOrAdd(data.Id);
@@ -353,7 +358,7 @@ namespace Discord
break;
break;
case "GUILD_UPDATE":
case "GUILD_UPDATE":
{
{
var data = e.Payload.ToObject<GuildUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<GuildUpdateEvent>(_dataS ocketSerializer);
var server = _servers[data.Id];
var server = _servers[data.Id];
if (server != null)
if (server != null)
{
{
@@ -364,7 +369,7 @@ namespace Discord
break;
break;
case "GUILD_DELETE":
case "GUILD_DELETE":
{
{
var data = e.Payload.ToObject<GuildDeleteEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<GuildDeleteEvent>(_dataS ocketSerializer);
var server = _servers.TryRemove(data.Id);
var server = _servers.TryRemove(data.Id);
if (server != null)
if (server != null)
{
{
@@ -379,7 +384,7 @@ namespace Discord
//Channels
//Channels
case "CHANNEL_CREATE":
case "CHANNEL_CREATE":
{
{
var data = e.Payload.ToObject<ChannelCreateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<ChannelCreateEvent>(_dataS ocketSerializer);
Channel channel;
Channel channel;
if (data.IsPrivate)
if (data.IsPrivate)
{
{
@@ -395,7 +400,7 @@ namespace Discord
break;
break;
case "CHANNEL_UPDATE":
case "CHANNEL_UPDATE":
{
{
var data = e.Payload.ToObject<ChannelUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<ChannelUpdateEvent>(_dataS ocketSerializer);
var channel = _channels[data.Id];
var channel = _channels[data.Id];
if (channel != null)
if (channel != null)
{
{
@@ -406,7 +411,7 @@ namespace Discord
break;
break;
case "CHANNEL_DELETE":
case "CHANNEL_DELETE":
{
{
var data = e.Payload.ToObject<ChannelDeleteEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<ChannelDeleteEvent>(_dataS ocketSerializer);
var channel = _channels.TryRemove(data.Id);
var channel = _channels.TryRemove(data.Id);
if (channel != null)
if (channel != null)
RaiseChannelDestroyed(channel);
RaiseChannelDestroyed(channel);
@@ -416,7 +421,7 @@ namespace Discord
//Members
//Members
case "GUILD_MEMBER_ADD":
case "GUILD_MEMBER_ADD":
{
{
var data = e.Payload.ToObject<MemberAddEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MemberAddEvent>(_dataS ocketSerializer);
var user = _users.GetOrAdd(data.User.Id, data.GuildId);
var user = _users.GetOrAdd(data.User.Id, data.GuildId);
user.Update(data);
user.Update(data);
if (Config.TrackActivity)
if (Config.TrackActivity)
@@ -426,7 +431,7 @@ namespace Discord
break;
break;
case "GUILD_MEMBER_UPDATE":
case "GUILD_MEMBER_UPDATE":
{
{
var data = e.Payload.ToObject<MemberUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MemberUpdateEvent>(_dataS ocketSerializer);
var user = _users[data.User.Id, data.GuildId];
var user = _users[data.User.Id, data.GuildId];
if (user != null)
if (user != null)
{
{
@@ -437,7 +442,7 @@ namespace Discord
break;
break;
case "GUILD_MEMBER_REMOVE":
case "GUILD_MEMBER_REMOVE":
{
{
var data = e.Payload.ToObject<MemberRemoveEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MemberRemoveEvent>(_dataS ocketSerializer);
var user = _users.TryRemove(data.UserId, data.GuildId);
var user = _users.TryRemove(data.UserId, data.GuildId);
if (user != null)
if (user != null)
RaiseUserLeft(user);
RaiseUserLeft(user);
@@ -445,7 +450,7 @@ namespace Discord
break;
break;
case "GUILD_MEMBERS_CHUNK":
case "GUILD_MEMBERS_CHUNK":
{
{
var data = e.Payload.ToObject<MembersChunkEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MembersChunkEvent>(_dataS ocketSerializer);
foreach (var memberData in data.Members)
foreach (var memberData in data.Members)
{
{
var user = _users.GetOrAdd(memberData.User.Id, memberData.GuildId);
var user = _users.GetOrAdd(memberData.User.Id, memberData.GuildId);
@@ -458,7 +463,7 @@ namespace Discord
//Roles
//Roles
case "GUILD_ROLE_CREATE":
case "GUILD_ROLE_CREATE":
{
{
var data = e.Payload.ToObject<RoleCreateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<RoleCreateEvent>(_dataS ocketSerializer);
var role = _roles.GetOrAdd(data.Data.Id, data.GuildId);
var role = _roles.GetOrAdd(data.Data.Id, data.GuildId);
role.Update(data.Data);
role.Update(data.Data);
var server = _servers[data.GuildId];
var server = _servers[data.GuildId];
@@ -469,7 +474,7 @@ namespace Discord
break;
break;
case "GUILD_ROLE_UPDATE":
case "GUILD_ROLE_UPDATE":
{
{
var data = e.Payload.ToObject<RoleUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<RoleUpdateEvent>(_dataS ocketSerializer);
var role = _roles[data.Data.Id];
var role = _roles[data.Data.Id];
if (role != null)
if (role != null)
{
{
@@ -480,7 +485,7 @@ namespace Discord
break;
break;
case "GUILD_ROLE_DELETE":
case "GUILD_ROLE_DELETE":
{
{
var data = e.Payload.ToObject<RoleDeleteEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<RoleDeleteEvent>(_dataS ocketSerializer);
var role = _roles.TryRemove(data.RoleId);
var role = _roles.TryRemove(data.RoleId);
if (role != null)
if (role != null)
{
{
@@ -495,7 +500,7 @@ namespace Discord
//Bans
//Bans
case "GUILD_BAN_ADD":
case "GUILD_BAN_ADD":
{
{
var data = e.Payload.ToObject<BanAddEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<BanAddEvent>(_dataS ocketSerializer);
var server = _servers[data.GuildId];
var server = _servers[data.GuildId];
if (server != null)
if (server != null)
{
{
@@ -507,7 +512,7 @@ namespace Discord
break;
break;
case "GUILD_BAN_REMOVE":
case "GUILD_BAN_REMOVE":
{
{
var data = e.Payload.ToObject<BanRemoveEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<BanRemoveEvent>(_dataS ocketSerializer);
var server = _servers[data.GuildId];
var server = _servers[data.GuildId];
if (server != null)
if (server != null)
{
{
@@ -521,7 +526,7 @@ namespace Discord
//Messages
//Messages
case "MESSAGE_CREATE":
case "MESSAGE_CREATE":
{
{
var data = e.Payload.ToObject<MessageCreateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MessageCreateEvent>(_dataS ocketSerializer);
Message msg = null;
Message msg = null;
bool isAuthor = data.Author.Id == _userId;
bool isAuthor = data.Author.Id == _userId;
@@ -548,7 +553,7 @@ namespace Discord
break;
break;
case "MESSAGE_UPDATE":
case "MESSAGE_UPDATE":
{
{
var data = e.Payload.ToObject<MessageUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MessageUpdateEvent>(_dataS ocketSerializer);
var msg = _messages[data.Id];
var msg = _messages[data.Id];
if (msg != null)
if (msg != null)
{
{
@@ -559,7 +564,7 @@ namespace Discord
break;
break;
case "MESSAGE_DELETE":
case "MESSAGE_DELETE":
{
{
var data = e.Payload.ToObject<MessageDeleteEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MessageDeleteEvent>(_dataS ocketSerializer);
var msg = _messages.TryRemove(data.Id);
var msg = _messages.TryRemove(data.Id);
if (msg != null)
if (msg != null)
RaiseMessageDeleted(msg);
RaiseMessageDeleted(msg);
@@ -567,7 +572,7 @@ namespace Discord
break;
break;
case "MESSAGE_ACK":
case "MESSAGE_ACK":
{
{
var data = e.Payload.ToObject<MessageAckEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MessageAckEvent>(_dataS ocketSerializer);
var msg = GetMessage(data.MessageId);
var msg = GetMessage(data.MessageId);
if (msg != null)
if (msg != null)
RaiseMessageReadRemotely(msg);
RaiseMessageReadRemotely(msg);
@@ -577,7 +582,7 @@ namespace Discord
//Statuses
//Statuses
case "PRESENCE_UPDATE":
case "PRESENCE_UPDATE":
{
{
var data = e.Payload.ToObject<PresenceUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<PresenceUpdateEvent>(_dataS ocketSerializer);
var user = _users.GetOrAdd(data.User.Id, data.GuildId);
var user = _users.GetOrAdd(data.User.Id, data.GuildId);
if (user != null)
if (user != null)
{
{
@@ -588,7 +593,7 @@ namespace Discord
break;
break;
case "TYPING_START":
case "TYPING_START":
{
{
var data = e.Payload.ToObject<TypingStartEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<TypingStartEvent>(_dataS ocketSerializer);
var channel = _channels[data.ChannelId];
var channel = _channels[data.ChannelId];
if (channel != null)
if (channel != null)
{
{
@@ -614,7 +619,7 @@ namespace Discord
//Voice
//Voice
case "VOICE_STATE_UPDATE":
case "VOICE_STATE_UPDATE":
{
{
var data = e.Payload.ToObject<MemberVoiceStateUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<MemberVoiceStateUpdateEvent>(_dataS ocketSerializer);
var user = _users[data.UserId, data.GuildId];
var user = _users[data.UserId, data.GuildId];
if (user != null)
if (user != null)
{
{
@@ -633,7 +638,7 @@ namespace Discord
//Settings
//Settings
case "USER_UPDATE":
case "USER_UPDATE":
{
{
var data = e.Payload.ToObject<UserUpdateEvent>(_s ocketSerializer);
var data = e.Payload.ToObject<UserUpdateEvent>(_dataS ocketSerializer);
var user = _globalUsers[data.Id];
var user = _globalUsers[data.Id];
if (user != null)
if (user != null)
{
{