Esempio n. 1
0
		//Constructor
		/// <summary> Initializes a new instance of the DiscordClient class. </summary>
		public DiscordClient(DiscordClientConfig config = null)
		{
			_blockEvent = new ManualResetEventSlim(true);
			_config = config ?? new DiscordClientConfig();
			_isDebugMode = config.EnableDebug;
			_rand = new Random();
			
			_serializer = new JsonSerializer();
#if TEST_RESPONSES
			_serializer.CheckAdditionalContent = true;
			_serializer.MissingMemberHandling = MissingMemberHandling.Error;
#endif

			_userRegex = new Regex(@"<@\d+?>", RegexOptions.Compiled);
			_channelRegex = new Regex(@"<#\d+?>", RegexOptions.Compiled);
			_userRegexEvaluator = new MatchEvaluator(e =>
			{
				string id = e.Value.Substring(2, e.Value.Length - 3);
				var user = _users[id];
				if (user != null)
					return '@' + user.Name;
				else //User not found
					return e.Value;
			});
			_channelRegexEvaluator = new MatchEvaluator(e =>
			{
				string id = e.Value.Substring(2, e.Value.Length - 3);
				var channel = _channels[id];
				if (channel != null)
					return channel.Name;
				else //Channel not found
					return e.Value;
			});

			if (_config.UseMessageQueue)
				_pendingMessages = new ConcurrentQueue<Message>();

			_http = new JsonHttpClient(config.EnableDebug);
			_api = new DiscordAPI(_http);
			if (_isDebugMode)
				_http.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Type, e.Message);

			CreateCaches();

			_webSocket = new DiscordDataSocket(this, config.ConnectionTimeout, config.WebSocketInterval, config.EnableDebug);
			_webSocket.Connected += (s, e) => RaiseConnected();
			_webSocket.Disconnected += async (s, e) =>
			{
				RaiseDisconnected();

				//Reconnect if we didn't cause the disconnect
				if (e.WasUnexpected)
				{
					while (!_disconnectToken.IsCancellationRequested)
					{
						try
						{
							await Task.Delay(_config.ReconnectDelay);
							await _webSocket.ReconnectAsync();
							if (_http.Token != null)
								await _webSocket.Login(_http.Token);
							break;
						}
						catch (Exception ex)
						{
							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);
						}
					}
				}
			};
			if (_isDebugMode)
				_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Type, $"DataSocket: {e.Message}");

#if !DNXCORE50
			if (_config.EnableVoice)
			{
				_voiceWebSocket = new DiscordVoiceSocket(this, _config.VoiceConnectionTimeout, _config.WebSocketInterval, config.EnableDebug);
				_voiceWebSocket.Connected += (s, e) => RaiseVoiceConnected();
				_voiceWebSocket.Disconnected += async (s, e) =>
				{
					RaiseVoiceDisconnected();

					//Reconnect if we didn't cause the disconnect
					if (e.WasUnexpected)
					{
						while (!_disconnectToken.IsCancellationRequested)
						{
							try
							{
								await Task.Delay(_config.ReconnectDelay);
								await _voiceWebSocket.ReconnectAsync();
								await _voiceWebSocket.Login(_currentVoiceServerId, _myId, _sessionId, _currentVoiceToken);
								break;
							}
							catch (Exception ex)
							{
								if (_isDebugMode)
									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);
							}
						}
					}
				};
				if (_isDebugMode)
					_voiceWebSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Type, $"VoiceSocket: {e.Message}");
			}
#endif

#if !DNXCORE50
			_webSocket.GotEvent += async (s, e) =>
#else
			_webSocket.GotEvent += (s, e) =>
#endif
			{
                switch (e.Type)
				{
					//Global
					case "READY": //Resync
						{
							var data = e.Event.ToObject<TextWebSocketEvents.Ready>(_serializer);

							_servers.Clear();
							_channels.Clear();
							_users.Clear();

							_myId = data.User.Id;
#if !DNXCORE50
							_sessionId = data.SessionId;
#endif
							_user = _users.Update(data.User.Id, data.User);
							foreach (var server in data.Guilds)
								_servers.Update(server.Id, server);
							foreach (var channel in data.PrivateChannels)
								_channels.Update(channel.Id, null, channel);
						}
						break;

					//Servers
					case "GUILD_CREATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildCreate>(_serializer);
							var server = _servers.Update(data.Id, data);
							try { RaiseServerCreated(server); } catch { }
						}
						break;
					case "GUILD_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildUpdate>(_serializer);
							var server = _servers.Update(data.Id, data);
							try { RaiseServerUpdated(server); } catch { }
						}
						break;
					case "GUILD_DELETE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildDelete>(_serializer);
							var server = _servers.Remove(data.Id);
							if (server != null)
								try { RaiseServerDestroyed(server); } catch { }
						}
						break;

					//Channels
					case "CHANNEL_CREATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.ChannelCreate>(_serializer);
							var channel = _channels.Update(data.Id, data.GuildId, data);
							try { RaiseChannelCreated(channel); } catch { }
						}
						break;
					case "CHANNEL_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.ChannelUpdate>(_serializer);
							var channel = _channels.Update(data.Id, data.GuildId, data);
							try { RaiseChannelUpdated(channel); } catch { }
						}
						break;
					case "CHANNEL_DELETE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.ChannelDelete>(_serializer);
							var channel = _channels.Remove(data.Id);
							if (channel != null)
								try { RaiseChannelDestroyed(channel); } catch { }
						}
						break;

					//Members
					case "GUILD_MEMBER_ADD":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildMemberAdd>(_serializer);
							var user = _users.Update(data.User.Id, data.User);
							var server = _servers[data.ServerId];
							if (server != null)
							{
								var member = server.UpdateMember(data);
								try { RaiseMemberAdded(member); } catch { }
							}
						}
						break;
					case "GUILD_MEMBER_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildMemberUpdate>(_serializer);
							var user = _users.Update(data.User.Id, data.User);
							var server = _servers[data.ServerId];
							if (server != null)
							{
								var member = server.UpdateMember(data);
								try { RaiseMemberUpdated(member); } catch { }
							}
						}
						break;
					case "GUILD_MEMBER_REMOVE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildMemberRemove>(_serializer);
							var server = _servers[data.ServerId];
							if (server != null)
							{
								var member = server.RemoveMember(data.User.Id);
								if (member != null)
									try { RaiseMemberRemoved(member); } catch { }
							}
						}
						break;

					//Roles
					case "GUILD_ROLE_CREATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildRoleCreateUpdate>(_serializer);
							var role = _roles.Update(data.Role.Id, data.ServerId, data.Role);
							try { RaiseRoleCreated(role); } catch { }
						}
						break;
					case "GUILD_ROLE_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildRoleCreateUpdate>(_serializer);
							var role = _roles.Update(data.Role.Id, data.ServerId, data.Role);
							try { RaiseRoleUpdated(role); } catch { }
						}
						break;
					case "GUILD_ROLE_DELETE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildRoleDelete>(_serializer);
							var role = _roles.Remove(data.RoleId);
							if (role != null)
								try { RaiseRoleDeleted(role); } catch { }
						}
						break;

					//Bans
					case "GUILD_BAN_ADD":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildBanAddRemove>(_serializer);
							var user = _users.Update(data.User.Id, data.User);
							var server = _servers[data.ServerId];
							try { RaiseBanAdded(user, server); } catch { }
						}
						break;
					case "GUILD_BAN_REMOVE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.GuildBanAddRemove>(_serializer);
							var user = _users.Update(data.User.Id, data.User);
							var server = _servers[data.ServerId];
							if (server != null && server.RemoveBan(user.Id))
							{
								try { RaiseBanRemoved(user, server); } catch { }
							}
						}
						break;

					//Messages
					case "MESSAGE_CREATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.MessageCreate>(_serializer);
							Message msg = null;
							bool wasLocal = _config.UseMessageQueue && data.Author.Id == _myId && data.Nonce != null;
                            if (wasLocal)
							{
								msg = _messages.Remap("nonce" + data.Nonce, data.Id);
								if (msg != null)
								{
									msg.IsQueued = false;
									msg.Id = data.Id;
								}
							}
							msg = _messages.Update(data.Id, data.ChannelId, data);
							msg.User.UpdateActivity(data.Timestamp);
							if (wasLocal)
							{
								try { RaiseMessageSent(msg); } catch { }
							}
							try { RaiseMessageCreated(msg); } catch { }
						}
						break;
					case "MESSAGE_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.MessageUpdate>(_serializer);
							var msg = _messages.Update(data.Id, data.ChannelId, data);
							try { RaiseMessageUpdated(msg); } catch { }
						}
						break;
					case "MESSAGE_DELETE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.MessageDelete>(_serializer);
							var msg = GetMessage(data.MessageId);
							if (msg != null)
							{
								_messages.Remove(msg.Id);
								try { RaiseMessageDeleted(msg); } catch { }
							}
						}
						break;
					case "MESSAGE_ACK":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.MessageAck>(_serializer);
							var msg = GetMessage(data.MessageId);
							if (msg != null)
								try { RaiseMessageRead(msg); } catch { }
						}
						break;

					//Statuses
					case "PRESENCE_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.PresenceUpdate>(_serializer);
							var user = _users.Update(data.User.Id, data.User);
							var server = _servers[data.ServerId];
							if (server != null)
							{
								var member = server.UpdateMember(data);
								try { RaisePresenceUpdated(member); } catch { }
							}
						}
						break;
					case "VOICE_STATE_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.VoiceStateUpdate>(_serializer);
							var server = _servers[data.ServerId];
							if (server != null)
							{
								var member = server.UpdateMember(data);
								if (member != null)
									try { RaiseVoiceStateUpdated(member); } catch { }
							}
						}
						break;
					case "TYPING_START":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.TypingStart>(_serializer);
							var channel = _channels[data.ChannelId];
							var user = _users[data.UserId];
							if (user != null)
							{
								user.UpdateActivity(DateTime.UtcNow);
								if (channel != null)
									try { RaiseUserTyping(user, channel); } catch { }
							}
						}
						break;

					//Voice
					case "VOICE_SERVER_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.VoiceServerUpdate>(_serializer);
							var server = _servers[data.ServerId];
							server.VoiceServer = data.Endpoint;
                            try { RaiseVoiceServerUpdated(server, data.Endpoint); } catch { }

#if !DNXCORE50
							if (_config.EnableVoice && data.ServerId == _currentVoiceServerId)
							{
								_currentVoiceToken = data.Token;
								await _voiceWebSocket.ConnectAsync("wss://" + data.Endpoint.Split(':')[0]);
								await _voiceWebSocket.Login(_currentVoiceServerId, _myId, _myId, data.Token);
							}
#endif
						}
						break;

					//Settings
					case "USER_UPDATE":
						{
							var data = e.Event.ToObject<TextWebSocketEvents.UserUpdate>(_serializer);
							var user = _users.Update(data.Id, data);
							try { RaiseUserUpdated(user); } catch { }
						}
						break;
					case "USER_SETTINGS_UPDATE":
						{
							//TODO: Process this
						}
						break;

					//Others
					default:
						RaiseOnDebugMessage(DebugMessageType.WebSocketUnknownEvent, "Unknown WebSocket message type: " + e.Type);
						break;
				}
			};
		}