@@ -89,11 +89,11 @@ | |||
<Compile Include="..\Discord.Net\DiscordClientConfig.cs"> | |||
<Link>DiscordClientConfig.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordTextWebSocket.cs"> | |||
<Link>DiscordTextWebSocket.cs</Link> | |||
<Compile Include="..\Discord.Net\DiscordDataSocket.cs"> | |||
<Link>DiscordDataSocket.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordTextWebSocket.Events.cs"> | |||
<Link>DiscordTextWebSocket.Events.cs</Link> | |||
<Compile Include="..\Discord.Net\DiscordDataSocket.Events.cs"> | |||
<Link>DiscordDataSocket.Events.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordVoiceSocket.cs"> | |||
<Link>DiscordVoiceSocket.cs</Link> | |||
@@ -2,248 +2,310 @@ | |||
namespace Discord | |||
{ | |||
public partial class DiscordClient | |||
public enum DebugMessageType : byte | |||
{ | |||
//Debug | |||
public sealed class LogMessageEventArgs : EventArgs | |||
Connection, | |||
Event, | |||
Cache, | |||
WebSocketRawInput, | |||
WebSocketUnknownInput, | |||
WebSocketEvent, | |||
WebSocketUnknownEvent, | |||
VoiceOutput | |||
} | |||
public sealed class LogMessageEventArgs : EventArgs | |||
{ | |||
public readonly DebugMessageType Type; | |||
public readonly string Message; | |||
internal LogMessageEventArgs(DebugMessageType type, string msg) { Type = type; Message = msg; } | |||
} | |||
public sealed class ServerEventArgs : EventArgs | |||
{ | |||
public readonly Server Server; | |||
internal ServerEventArgs(Server server) { Server = server; } | |||
} | |||
public sealed class ChannelEventArgs : EventArgs | |||
{ | |||
public readonly Channel Channel; | |||
internal ChannelEventArgs(Channel channel) { Channel = channel; } | |||
} | |||
public sealed class UserEventArgs : EventArgs | |||
{ | |||
public readonly User User; | |||
internal UserEventArgs(User user) { User = user; } | |||
} | |||
public sealed class MessageEventArgs : EventArgs | |||
{ | |||
public readonly Message Message; | |||
internal MessageEventArgs(Message msg) { Message = msg; } | |||
} | |||
public sealed class RoleEventArgs : EventArgs | |||
{ | |||
public readonly Role Role; | |||
internal RoleEventArgs(Role role) { Role = role; } | |||
} | |||
public sealed class BanEventArgs : EventArgs | |||
{ | |||
public readonly User User; | |||
public readonly Server Server; | |||
internal BanEventArgs(User user, Server server) | |||
{ | |||
public readonly string Message; | |||
internal LogMessageEventArgs(string msg) { Message = msg; } | |||
User = user; | |||
Server = server; | |||
} | |||
public event EventHandler<LogMessageEventArgs> DebugMessage; | |||
private void RaiseOnDebugMessage(string message) | |||
} | |||
public sealed class MemberEventArgs : EventArgs | |||
{ | |||
public readonly Membership Member; | |||
internal MemberEventArgs(Membership member) { Member = member; } | |||
} | |||
public sealed class UserTypingEventArgs : EventArgs | |||
{ | |||
public readonly User User; | |||
public readonly Channel Channel; | |||
internal UserTypingEventArgs(User user, Channel channel) | |||
{ | |||
if (DebugMessage != null) | |||
DebugMessage(this, new LogMessageEventArgs(message)); | |||
User = user; | |||
Channel = channel; | |||
} | |||
public event EventHandler<LogMessageEventArgs> VoiceDebugMessage; | |||
private void RaiseOnVoiceDebugMessage(string message) | |||
} | |||
public sealed class VoiceServerUpdatedEventArgs : EventArgs | |||
{ | |||
public readonly Server Server; | |||
public readonly string Endpoint; | |||
internal VoiceServerUpdatedEventArgs(Server server, string endpoint) | |||
{ | |||
if (VoiceDebugMessage != null) | |||
VoiceDebugMessage(this, new LogMessageEventArgs(message)); | |||
Server = server; | |||
Endpoint = endpoint; | |||
} | |||
} | |||
public partial class DiscordClient | |||
{ | |||
//Debug | |||
public event EventHandler<LogMessageEventArgs> DebugMessage; | |||
internal void RaiseOnDebugMessage(DebugMessageType type, string message) | |||
{ | |||
if (DebugMessage != null) | |||
DebugMessage(this, new LogMessageEventArgs(type, message)); | |||
} | |||
//General | |||
public event EventHandler Connected; | |||
private void RaiseConnected() | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"Connected"); | |||
if (Connected != null) | |||
Connected(this, EventArgs.Empty); | |||
} | |||
public event EventHandler Disconnected; | |||
private void RaiseDisconnected() | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"Disconnected"); | |||
if (Disconnected != null) | |||
Disconnected(this, EventArgs.Empty); | |||
} | |||
//Server | |||
public sealed class ServerEventArgs : EventArgs | |||
{ | |||
public readonly Server Server; | |||
internal ServerEventArgs(Server server) { Server = server; } | |||
} | |||
public event EventHandler<ServerEventArgs> ServerCreated; | |||
private void RaiseServerCreated(Server server) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"ServerCreated {server.Name} ({server.Id})"); | |||
if (ServerCreated != null) | |||
ServerCreated(this, new ServerEventArgs(server)); | |||
} | |||
public event EventHandler<ServerEventArgs> ServerDestroyed; | |||
private void RaiseServerDestroyed(Server server) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"ServerDestroyed {server.Name} ({server.Id})"); | |||
if (ServerDestroyed != null) | |||
ServerDestroyed(this, new ServerEventArgs(server)); | |||
} | |||
public event EventHandler<ServerEventArgs> ServerUpdated; | |||
private void RaiseServerUpdated(Server server) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"ServerUpdated {server.Name} ({server.Id})"); | |||
if (ServerUpdated != null) | |||
ServerUpdated(this, new ServerEventArgs(server)); | |||
} | |||
//Channel | |||
public sealed class ChannelEventArgs : EventArgs | |||
{ | |||
public readonly Channel Channel; | |||
internal ChannelEventArgs(Channel channel) { Channel = channel; } | |||
} | |||
public event EventHandler<ChannelEventArgs> ChannelCreated; | |||
private void RaiseChannelCreated(Channel channel) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"ChannelCreated {channel.Name} ({channel.Id})"); | |||
if (ChannelCreated != null) | |||
ChannelCreated(this, new ChannelEventArgs(channel)); | |||
} | |||
public event EventHandler<ChannelEventArgs> ChannelDestroyed; | |||
private void RaiseChannelDestroyed(Channel channel) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"ChannelDestroyed {channel.Name} ({channel.Id})"); | |||
if (ChannelDestroyed != null) | |||
ChannelDestroyed(this, new ChannelEventArgs(channel)); | |||
} | |||
public event EventHandler<ChannelEventArgs> ChannelUpdated; | |||
private void RaiseChannelUpdated(Channel channel) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"ChannelUpdated {channel.Name} ({channel.Id})"); | |||
if (ChannelUpdated != null) | |||
ChannelUpdated(this, new ChannelEventArgs(channel)); | |||
} | |||
//User | |||
public sealed class UserEventArgs : EventArgs | |||
{ | |||
public readonly User User; | |||
internal UserEventArgs(User user) { User = user; } | |||
} | |||
public event EventHandler<UserEventArgs> UserUpdated; | |||
private void RaiseUserUpdated(User user) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"UserUpdated {user.Name} ({user.Id})"); | |||
if (UserUpdated != null) | |||
UserUpdated(this, new UserEventArgs(user)); | |||
} | |||
//Message | |||
public sealed class MessageEventArgs : EventArgs | |||
{ | |||
public readonly Message Message; | |||
internal MessageEventArgs(Message msg) { Message = msg; } | |||
} | |||
public event EventHandler<MessageEventArgs> MessageCreated; | |||
private void RaiseMessageCreated(Message msg) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MessageCreated {msg.Id}"); | |||
if (MessageCreated != null) | |||
MessageCreated(this, new MessageEventArgs(msg)); | |||
} | |||
public event EventHandler<MessageEventArgs> MessageDeleted; | |||
private void RaiseMessageDeleted(Message msg) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MessageDeleted {msg.Id}"); | |||
if (MessageDeleted != null) | |||
MessageDeleted(this, new MessageEventArgs(msg)); | |||
} | |||
public event EventHandler<MessageEventArgs> MessageUpdated; | |||
private void RaiseMessageUpdated(Message msg) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MessageUpdated {msg.Id}"); | |||
if (MessageUpdated != null) | |||
MessageUpdated(this, new MessageEventArgs(msg)); | |||
} | |||
public event EventHandler<MessageEventArgs> MessageRead; | |||
private void RaiseMessageRead(Message msg) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MessageRead {msg.Id}"); | |||
if (MessageRead != null) | |||
MessageRead(this, new MessageEventArgs(msg)); | |||
} | |||
public event EventHandler<MessageEventArgs> MessageSent; | |||
private void RaiseMessageSent(Message msg) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MessageSent {msg.Id}"); | |||
if (MessageSent != null) | |||
MessageSent(this, new MessageEventArgs(msg)); | |||
} | |||
//Role | |||
public sealed class RoleEventArgs : EventArgs | |||
{ | |||
public readonly Role Role; | |||
internal RoleEventArgs(Role role) { Role = role; } | |||
} | |||
public event EventHandler<RoleEventArgs> RoleCreated; | |||
private void RaiseRoleCreated(Role role) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"RoleCreated {role.Name} ({role.Id})"); | |||
if (RoleCreated != null) | |||
RoleCreated(this, new RoleEventArgs(role)); | |||
} | |||
public event EventHandler<RoleEventArgs> RoleUpdated; | |||
private void RaiseRoleDeleted(Role role) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"RoleDeleted {role.Name} ({role.Id})"); | |||
if (RoleDeleted != null) | |||
RoleDeleted(this, new RoleEventArgs(role)); | |||
} | |||
public event EventHandler<RoleEventArgs> RoleDeleted; | |||
private void RaiseRoleUpdated(Role role) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"RoleUpdated {role.Name} ({role.Id})"); | |||
if (RoleUpdated != null) | |||
RoleUpdated(this, new RoleEventArgs(role)); | |||
} | |||
//Ban | |||
public sealed class BanEventArgs : EventArgs | |||
{ | |||
public readonly User User; | |||
public readonly Server Server; | |||
internal BanEventArgs(User user, Server server) | |||
{ | |||
User = user; | |||
Server = server; | |||
} | |||
} | |||
public event EventHandler<BanEventArgs> BanAdded; | |||
private void RaiseBanAdded(User user, Server server) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"BanAdded {user.Name} ({user.Id}) on {server.Name} ({server.Id})"); | |||
if (BanAdded != null) | |||
BanAdded(this, new BanEventArgs(user, server)); | |||
} | |||
public event EventHandler<BanEventArgs> BanRemoved; | |||
private void RaiseBanRemoved(User user, Server server) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"BanRemoved {user.Name} ({user.Id}) on {server.Name} ({server.Id})"); | |||
if (BanRemoved != null) | |||
BanRemoved(this, new BanEventArgs(user, server)); | |||
} | |||
//Member | |||
public sealed class MemberEventArgs : EventArgs | |||
{ | |||
public readonly Membership Membership; | |||
internal MemberEventArgs(Membership membership) { Membership = membership; } | |||
} | |||
public event EventHandler<MemberEventArgs> MemberAdded; | |||
private void RaiseMemberAdded(Membership membership) | |||
private void RaiseMemberAdded(Membership member) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MemberAdded {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); | |||
if (MemberAdded != null) | |||
MemberAdded(this, new MemberEventArgs(membership)); | |||
MemberAdded(this, new MemberEventArgs(member)); | |||
} | |||
public event EventHandler<MemberEventArgs> MemberRemoved; | |||
private void RaiseMemberRemoved(Membership membership) | |||
private void RaiseMemberRemoved(Membership member) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MemberRemoved {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); | |||
if (MemberRemoved != null) | |||
MemberRemoved(this, new MemberEventArgs(membership)); | |||
MemberRemoved(this, new MemberEventArgs(member)); | |||
} | |||
public event EventHandler<MemberEventArgs> MemberUpdated; | |||
private void RaiseMemberUpdated(Membership membership) | |||
private void RaiseMemberUpdated(Membership member) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"MemberUpdated {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); | |||
if (MemberUpdated != null) | |||
MemberUpdated(this, new MemberEventArgs(membership)); | |||
MemberUpdated(this, new MemberEventArgs(member)); | |||
} | |||
//Status | |||
public sealed class UserTypingEventArgs : EventArgs | |||
{ | |||
public readonly User User; | |||
public readonly Channel Channel; | |||
internal UserTypingEventArgs(User user, Channel channel) | |||
{ | |||
User = user; | |||
Channel = channel; | |||
} | |||
} | |||
public event EventHandler<MemberEventArgs> PresenceUpdated; | |||
private void RaisePresenceUpdated(Membership member) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"PresenceUpdated {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); | |||
if (PresenceUpdated != null) | |||
PresenceUpdated(this, new MemberEventArgs(member)); | |||
} | |||
public event EventHandler<MemberEventArgs> VoiceStateUpdated; | |||
private void RaiseVoiceStateUpdated(Membership member) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceStateUpdated {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); | |||
if (VoiceStateUpdated != null) | |||
VoiceStateUpdated(this, new MemberEventArgs(member)); | |||
} | |||
public event EventHandler<UserTypingEventArgs> UserTyping; | |||
private void RaiseUserTyping(User user, Channel channel) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceStateUpdated {user.Name} ({user.Id}) on {channel.Name} ({channel.Id})"); | |||
if (UserTyping != null) | |||
UserTyping(this, new UserTypingEventArgs(user, channel)); | |||
} | |||
@@ -252,29 +314,24 @@ namespace Discord | |||
public event EventHandler VoiceConnected; | |||
private void RaiseVoiceConnected() | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceConnected"); | |||
if (VoiceConnected != null) | |||
VoiceConnected(this, EventArgs.Empty); | |||
} | |||
public event EventHandler VoiceDisconnected; | |||
private void RaiseVoiceDisconnected() | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceDisconnected"); | |||
if (VoiceDisconnected != null) | |||
VoiceDisconnected(this, EventArgs.Empty); | |||
} | |||
public sealed class VoiceServerUpdatedEventArgs : EventArgs | |||
{ | |||
public readonly Server Server; | |||
public readonly string Endpoint; | |||
internal VoiceServerUpdatedEventArgs(Server server, string endpoint) | |||
{ | |||
Server = server; | |||
Endpoint = endpoint; | |||
} | |||
} | |||
public event EventHandler<VoiceServerUpdatedEventArgs> VoiceServerUpdated; | |||
private void RaiseVoiceServerUpdated(Server server, string endpoint) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceServerUpdated {server.Name} ({server.Id})"); | |||
if (VoiceServerUpdated != null) | |||
VoiceServerUpdated(this, new VoiceServerUpdatedEventArgs(server, endpoint)); | |||
} | |||
@@ -17,8 +17,7 @@ namespace Discord | |||
/// <summary> Provides a connection to the DiscordApp service. </summary> | |||
public partial class DiscordClient | |||
{ | |||
private readonly DiscordClientConfig _config; | |||
private readonly DiscordTextWebSocket _webSocket; | |||
private readonly DiscordDataSocket _webSocket; | |||
#if !DNXCORE50 | |||
private readonly DiscordVoiceSocket _voiceWebSocket; | |||
#endif | |||
@@ -36,6 +35,9 @@ namespace Discord | |||
public string UserId { get; private set; } | |||
public string SessionId { get; private set; } | |||
public DiscordClientConfig Config => _config; | |||
private readonly DiscordClientConfig _config; | |||
/// <summary> Returns a collection of all users the client can see across all servers. </summary> | |||
/// <remarks> This collection does not guarantee any ordering. </remarks> | |||
public IEnumerable<User> Users => _users; | |||
@@ -113,7 +115,12 @@ namespace Discord | |||
}); | |||
_servers = new AsyncCache<Server, API.Models.ServerReference>( | |||
(key, parentKey) => new Server(key, this), | |||
(key, parentKey) => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created server {key}."); | |||
return new Server(key, this); | |||
}, | |||
(server, model) => | |||
{ | |||
server.Name = model.Name; | |||
@@ -141,12 +148,28 @@ namespace Discord | |||
foreach (var membership in extendedModel.Presences) | |||
server.UpdateMember(membership); | |||
} | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated server {server.Name} ({server.Id})."); | |||
}, | |||
server => { } | |||
server => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed server {server.Name} ({server.Id})."); | |||
} | |||
); | |||
_channels = new AsyncCache<Channel, API.Models.ChannelReference>( | |||
(key, parentKey) => new Channel(key, parentKey, this), | |||
(key, parentKey) => | |||
{ | |||
if (_config.EnableDebug) | |||
{ | |||
if (parentKey != null) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created channel {key} in server {parentKey}."); | |||
else | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created private channel {key}."); | |||
} | |||
return new Channel(key, parentKey, this); | |||
}, | |||
(channel, model) => | |||
{ | |||
channel.Name = model.Name; | |||
@@ -176,6 +199,13 @@ namespace Discord | |||
else | |||
channel.PermissionOverwrites = null; | |||
} | |||
if (_config.EnableDebug) | |||
{ | |||
if (channel.IsPrivate) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated private channel {channel.Name} ({channel.Id})."); | |||
else | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated channel {channel.Name} ({channel.Id}) in server {channel.Server?.Name} ({channel.ServerId})."); | |||
} | |||
}, | |||
channel => | |||
{ | |||
@@ -184,10 +214,22 @@ namespace Discord | |||
var user = channel.Recipient; | |||
if (user.PrivateChannelId == channel.Id) | |||
user.PrivateChannelId = null; | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed private channel {channel.Name} ({channel.Id})."); | |||
} | |||
else | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed channel {channel.Name} ({channel.Id}) in server {channel.Server?.Name} ({channel.ServerId})."); | |||
} | |||
}); | |||
_messages = new AsyncCache<Message, API.Models.MessageReference>( | |||
(key, parentKey) => new Message(key, parentKey, this), | |||
(key, parentKey) => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created message {key} in channel {parentKey}."); | |||
return new Message(key, parentKey, this); | |||
}, | |||
(message, model) => | |||
{ | |||
if (model is API.Models.Message) | |||
@@ -260,21 +302,44 @@ namespace Discord | |||
if (extendedModel.Author != null) | |||
message.UserId = extendedModel.Author.Id; | |||
} | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated message {message.Id} in channel {message.Channel?.Name} ({message.ChannelId})."); | |||
}, | |||
message => { } | |||
message => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed message {message.Id} in channel {message.Channel?.Name} ({message.ChannelId})."); | |||
} | |||
); | |||
_pendingMessages = new ConcurrentQueue<Message>(); | |||
if (_config.UseMessageQueue) | |||
_pendingMessages = new ConcurrentQueue<Message>(); | |||
_roles = new AsyncCache<Role, API.Models.Role>( | |||
(key, parentKey) => new Role(key, parentKey, this), | |||
(key, parentKey) => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created role {key} in server {parentKey}."); | |||
return new Role(key, parentKey, this); | |||
}, | |||
(role, model) => | |||
{ | |||
role.Name = model.Name; | |||
role.Permissions.RawValue = (uint)model.Permissions; | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated role {role.Name} ({role.Id}) in server {role.Server?.Name} ({role.ServerId})."); | |||
}, | |||
role => { } | |||
role => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed role {role.Name} ({role.Id}) in server {role.Server?.Name} ({role.ServerId})."); | |||
} | |||
); | |||
_users = new AsyncCache<User, API.Models.UserReference>( | |||
(key, parentKey) => new User(key, this), | |||
(key, parentKey) => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created user {key}."); | |||
return new User(key, this); | |||
}, | |||
(user, model) => | |||
{ | |||
user.AvatarId = model.Avatar; | |||
@@ -286,53 +351,94 @@ namespace Discord | |||
user.Email = extendedModel.Email; | |||
user.IsVerified = extendedModel.IsVerified; | |||
} | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated user {user?.Name} ({user.Id})."); | |||
}, | |||
user => { } | |||
user => | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed user {user?.Name} ({user.Id})."); | |||
} | |||
); | |||
_webSocket = new DiscordTextWebSocket(this, _config.ConnectionTimeout, _config.WebSocketInterval); | |||
_webSocket = new DiscordDataSocket(this, _config.ConnectionTimeout, _config.WebSocketInterval); | |||
_webSocket.Connected += (s, e) => RaiseConnected(); | |||
_webSocket.Disconnected += async (s, e) => | |||
_webSocket.Disconnected += async (s, e) => | |||
{ | |||
//Reconnect if we didn't cause the disconnect | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket disconnected."); | |||
RaiseDisconnected(); | |||
//Reconnect if we didn't cause the disconnect | |||
while (!_disconnectToken.IsCancellationRequested) | |||
{ | |||
try | |||
{ | |||
await Task.Delay(_config.ReconnectDelay); | |||
await _webSocket.ReconnectAsync(); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); | |||
await _webSocket.Login(); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket logged in."); | |||
break; | |||
} | |||
catch (Exception ex) | |||
{ | |||
RaiseOnDebugMessage($"Reconnect Failed: {ex.Message}"); | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket reconnect failed: {ex.Message}"); | |||
//Net is down? We can keep trying to reconnect until the user runs Disconnect() | |||
await Task.Delay(_config.FailedReconnectDelay); | |||
} | |||
} | |||
}; | |||
_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message); | |||
if (_config.EnableDebug) | |||
_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Type, e.Message); | |||
#if !DNXCORE50 | |||
if (_config.EnableVoice) | |||
{ | |||
_voiceWebSocket = new DiscordVoiceSocket(this, _config.VoiceConnectionTimeout, _config.WebSocketInterval); | |||
_voiceWebSocket.Connected += (s, e) => RaiseVoiceConnected(); | |||
_voiceWebSocket.Disconnected += (s, e) => | |||
_voiceWebSocket.Disconnected += async (s, e) => | |||
{ | |||
//TODO: Reconnect if we didn't cause the disconnect | |||
RaiseVoiceDisconnected(); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket disconnected."); | |||
RaiseVoiceDisconnected(); | |||
//Reconnect if we didn't cause the disconnect | |||
while (!_disconnectToken.IsCancellationRequested) | |||
{ | |||
try | |||
{ | |||
await Task.Delay(_config.ReconnectDelay); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket connected."); | |||
await _voiceWebSocket.ReconnectAsync(); | |||
break; | |||
} | |||
catch (Exception ex) | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket reconnect failed: {ex.Message}"); | |||
//Net is down? We can keep trying to reconnect until the user runs Disconnect() | |||
await Task.Delay(_config.FailedReconnectDelay); | |||
} | |||
} | |||
}; | |||
_voiceWebSocket.OnDebugMessage += (s, e) => RaiseOnVoiceDebugMessage(e.Message); | |||
if (_config.EnableDebug) | |||
_voiceWebSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Type, e.Message); | |||
} | |||
#endif | |||
#pragma warning disable CS1998 //Disable unused async keyword warning | |||
#if !DNXCORE50 | |||
_webSocket.GotEvent += async (s, e) => | |||
#else | |||
_webSocket.GotEvent += (s, e) => | |||
#endif | |||
{ | |||
switch (e.Type) | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.WebSocketEvent, $"{e.Type}: {e.Event}"); | |||
switch (e.Type) | |||
{ | |||
//Global | |||
case "READY": //Resync | |||
@@ -610,12 +716,11 @@ namespace Discord | |||
//Others | |||
default: | |||
RaiseOnDebugMessage("Unknown WebSocket message type: " + e.Type); | |||
RaiseOnDebugMessage(DebugMessageType.WebSocketUnknownEvent, "Unknown WebSocket message type: " + e.Type); | |||
break; | |||
} | |||
}; | |||
} | |||
#pragma warning restore CS1998 //Restore unused async keyword warning | |||
private async Task SendAsync() | |||
{ | |||
@@ -658,7 +763,7 @@ namespace Discord | |||
{ | |||
await Task.Delay(-1, cancelToken); | |||
} | |||
catch { } | |||
catch (OperationCanceledException) { } | |||
} | |||
/// <summary> Returns the user with the specified id, or null if none was found. </summary> | |||
@@ -867,12 +972,19 @@ namespace Discord | |||
try | |||
{ | |||
await _webSocket.ConnectAsync(url); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); | |||
Http.Token = token; | |||
await _webSocket.Login(); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got token."); | |||
success = true; | |||
} | |||
catch (InvalidOperationException) //Bad Token | |||
{ | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket had a bad token."); | |||
if (password == null) //If we don't have an alternate login, throw this error | |||
throw; | |||
} | |||
@@ -881,35 +993,51 @@ namespace Discord | |||
{ | |||
//Open websocket while we wait for login response | |||
Task socketTask = _webSocket.ConnectAsync(url); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); | |||
var response = await DiscordAPI.Login(emailOrUsername, password); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got token."); | |||
await socketTask; | |||
//Wait for websocket to finish connecting, then send token | |||
token = response.Token; | |||
Http.Token = token; | |||
await _webSocket.Login(); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket logged in."); | |||
success = true; | |||
} | |||
if (!success && password == null) //Anonymous login | |||
{ | |||
//Open websocket while we wait for login response | |||
Task socketTask = _webSocket.ConnectAsync(url); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); | |||
var response = await DiscordAPI.LoginAnonymous(emailOrUsername); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket generated anonymous token."); | |||
await socketTask; | |||
//Wait for websocket to finish connecting, then send token | |||
token = response.Token; | |||
Http.Token = token; | |||
await _webSocket.Login(); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket logged in."); | |||
success = true; | |||
} | |||
if (success) | |||
{ | |||
var cancelToken = _disconnectToken.Token; | |||
if (_config.UseMessageQueue) | |||
_tasks = Task.WhenAll(await Task.Factory.StartNew(SendAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)); | |||
_tasks = SendAsync(); | |||
else | |||
_tasks = Task.WhenAll(await Task.Factory.StartNew(EmptyAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)); | |||
_tasks = EmptyAsync(); | |||
_tasks = _tasks.ContinueWith(async x => | |||
{ | |||
await _webSocket.DisconnectAsync(); | |||
@@ -1347,10 +1475,13 @@ namespace Discord | |||
/// <param name="count">Number of bytes in this frame. </param> | |||
public void SendVoicePCM(byte[] data, int count) | |||
{ | |||
if (!_config.EnableVoice) | |||
if (!_config.EnableVoice) | |||
throw new InvalidOperationException("Voice is not enabled for this client."); | |||
if (count == 0) return; | |||
_voiceWebSocket.SendPCMFrame(data, count); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.VoiceOutput, $"Queued {count} bytes for voice output."); | |||
_voiceWebSocket.SendPCMFrame(data, count); | |||
} | |||
/// <summary> Clears the PCM buffer. </summary> | |||
@@ -1359,6 +1490,8 @@ namespace Discord | |||
if (!_config.EnableVoice) | |||
throw new InvalidOperationException("Voice is not enabled for this client."); | |||
if (_config.EnableDebug) | |||
RaiseOnDebugMessage(DebugMessageType.VoiceOutput, $"Cleared the voice buffer."); | |||
_voiceWebSocket.ClearPCMFrames(); | |||
} | |||
#endif | |||
@@ -7,6 +7,9 @@ | |||
/// <remarks> This option requires the opus .dll or .so be in the local lib/ folder. </remarks> | |||
public bool EnableVoice { get; set; } = false; | |||
#endif | |||
/// <summary> Enables the verbose DebugMessage event handler. May hinder performance but should help debug any issues. </summary> | |||
public bool EnableDebug { get; set; } = false; | |||
/// <summary> Max time in milliseconds to wait for the web socket to connect. </summary> | |||
public int ConnectionTimeout { get; set; } = 5000; | |||
/// <summary> Max time in milliseconds to wait for the voice web socket to connect. </summary> | |||
@@ -3,7 +3,7 @@ using System; | |||
namespace Discord | |||
{ | |||
internal partial class DiscordTextWebSocket | |||
internal partial class DiscordDataSocket | |||
{ | |||
public event EventHandler<MessageEventArgs> GotEvent; | |||
public sealed class MessageEventArgs : EventArgs |
@@ -9,11 +9,11 @@ using WebSocketMessage = Discord.API.Models.TextWebSocketCommands.WebSocketMessa | |||
namespace Discord | |||
{ | |||
internal sealed partial class DiscordTextWebSocket : DiscordWebSocket | |||
internal sealed partial class DiscordDataSocket : DiscordWebSocket | |||
{ | |||
private ManualResetEventSlim _connectWaitOnLogin, _connectWaitOnLogin2; | |||
public DiscordTextWebSocket(DiscordClient client, int timeout, int interval) | |||
public DiscordDataSocket(DiscordClient client, int timeout, int interval) | |||
: base(client, timeout, interval) | |||
{ | |||
_connectWaitOnLogin = new ManualResetEventSlim(false); | |||
@@ -72,7 +72,7 @@ namespace Discord | |||
} | |||
break; | |||
default: | |||
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); | |||
RaiseOnDebugMessage(DebugMessageType.WebSocketUnknownInput, "Unknown DataSocket op: " + msg.Operation); | |||
break; | |||
} | |||
#if DNXCORE |
@@ -287,7 +287,7 @@ namespace Discord | |||
break; | |||
#endif | |||
default: | |||
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); | |||
RaiseOnDebugMessage(DebugMessageType.WebSocketUnknownInput, "Unknown VoiceSocket op: " + msg.Operation); | |||
break; | |||
} | |||
#if DNXCORE50 | |||
@@ -4,25 +4,26 @@ namespace Discord | |||
{ | |||
internal abstract partial class DiscordWebSocket | |||
{ | |||
//Debug | |||
public event EventHandler<LogMessageEventArgs> OnDebugMessage; | |||
protected void RaiseOnDebugMessage(DebugMessageType type, string message) | |||
{ | |||
if (OnDebugMessage != null) | |||
OnDebugMessage(this, new LogMessageEventArgs(type, message)); | |||
} | |||
//Connection | |||
public event EventHandler Connected; | |||
private void RaiseConnected() | |||
{ | |||
if (Connected != null) | |||
Connected(this, EventArgs.Empty); | |||
} | |||
public event EventHandler Disconnected; | |||
private void RaiseDisconnected() | |||
{ | |||
if (Disconnected != null) | |||
Disconnected(this, EventArgs.Empty); | |||
} | |||
public event EventHandler<DiscordClient.LogMessageEventArgs> OnDebugMessage; | |||
protected void RaiseOnDebugMessage(string message) | |||
{ | |||
if (OnDebugMessage != null) | |||
OnDebugMessage(this, new DiscordClient.LogMessageEventArgs(message)); | |||
} | |||
} | |||
} |
@@ -67,7 +67,12 @@ namespace Discord | |||
_client = client; | |||
_bans = new ConcurrentDictionary<string, bool>(); | |||
_members = new AsyncCache<Membership, API.Models.MemberInfo>( | |||
(key, parentKey) => new Membership(parentKey, key, _client), | |||
(key, parentKey) => | |||
{ | |||
if (_client.Config.EnableDebug) | |||
_client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Created user {key} in server {parentKey}."); | |||
return new Membership(parentKey, key, _client); | |||
}, | |||
(member, model) => | |||
{ | |||
if (model is API.Models.PresenceMemberInfo) | |||
@@ -103,7 +108,14 @@ namespace Discord | |||
member.IsDeafened = extendedModel.IsDeafened; | |||
member.IsMuted = extendedModel.IsMuted; | |||
} | |||
} | |||
if (_client.Config.EnableDebug) | |||
_client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated user {member.User?.Name} ({member.UserId}) in server {member.Server?.Name} ({member.ServerId})."); | |||
}, | |||
(member) => | |||
{ | |||
if (_client.Config.EnableDebug) | |||
_client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed user {member.User?.Name} ({member.UserId}) in server {member.Server?.Name} ({member.ServerId})."); | |||
} | |||
); | |||
} | |||