/// <summary> Initializes a new instance of the DiscordClient class. </summary> public DiscordSimpleClient(DiscordClientConfig config = null) { _config = config ?? new DiscordClientConfig(); _config.Lock(); _enableVoice = config.VoiceMode != DiscordVoiceMode.Disabled && !config.EnableVoiceMultiserver; _state = (int)DiscordClientState.Disconnected; _cancelToken = new CancellationToken(true); _disconnectedEvent = new ManualResetEvent(true); _connectedEvent = new ManualResetEventSlim(false); _dataSocket = CreateDataSocket(); if (_enableVoice) _voiceSocket = CreateVoiceSocket(); }
internal DiscordSimpleClient(DiscordClientConfig config = null, string serverId = null) : this(config) { _voiceServerId = serverId; }
/// <summary> Initializes a new instance of the DiscordClient class. </summary> public DiscordClient(DiscordClientConfig config = null) : base(config ?? new DiscordClientConfig()) { _rand = new Random(); _api = new DiscordAPIClient(_config); if (Config.UseMessageQueue) { _pendingMessages = new ConcurrentQueue <Message>(); } if (Config.EnableVoiceMultiserver) { _voiceClients = new ConcurrentDictionary <string, DiscordWSClient>(); } object cacheLock = new object(); _channels = new Channels(this, cacheLock); _users = new Users(this, cacheLock); _messages = new Messages(this, cacheLock, Config.MessageCacheLength > 0); _roles = new Roles(this, cacheLock); _servers = new Servers(this, cacheLock); _globalUsers = new GlobalUsers(this, cacheLock); _status = UserStatus.Online; this.Connected += async(s, e) => { _api.CancelToken = _cancelToken; await SendStatus().ConfigureAwait(false); }; VoiceDisconnected += (s, e) => { var server = _servers[e.ServerId]; if (server != null) { foreach (var member in server.Members) { if (member.IsSpeaking) { member.IsSpeaking = false; RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false); } } } }; if (_config.LogLevel >= LogMessageSeverity.Info) { ServerCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Created: {e.Server?.Name ?? "[Private]"}"); ServerDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Destroyed: {e.Server?.Name ?? "[Private]"}"); ServerUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Updated: {e.Server?.Name ?? "[Private]"}"); ServerAvailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Available: {e.Server?.Name ?? "[Private]"}"); ServerUnavailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Unavailable: {e.Server?.Name ?? "[Private]"}"); ChannelCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Channel Created: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}"); ChannelDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Channel Destroyed: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}"); ChannelUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Channel Updated: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}"); MessageReceived += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Message Received: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); MessageDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Message Deleted: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); MessageUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Message Update: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); RoleCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Role Created: {e.Server?.Name ?? "[Private]"}/{e.Role?.Name}"); RoleUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Role Updated: {e.Server?.Name ?? "[Private]"}/{e.Role?.Name}"); RoleDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Role Deleted: {e.Server?.Name ?? "[Private]"}/{e.Role?.Name}"); UserBanned += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Banned User: {e.Server?.Name ?? "[Private]" }/{e.UserId}"); UserUnbanned += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Unbanned User: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Joined: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Left: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); UserUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Updated: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Updated (Voice State): {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, "Profile Updated"); } if (_config.LogLevel >= LogMessageSeverity.Verbose) { UserIsTypingUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"User Updated (Is Typing): {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.User?.Name}"); MessageReadRemotely += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Read Message (Remotely): {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); MessageSent += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Sent Message: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); UserPresenceUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"User Updated (Presence): {e.Server?.Name ?? "[Private]"}/{e.User?.Name}"); _api.RestClient.OnRequest += (s, e) => { if (e.Payload != null) { RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms ({e.Payload})"); } else { RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms"); } }; } if (_config.LogLevel >= LogMessageSeverity.Debug) { _channels.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Channel {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _channels.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Channel {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _channels.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Channels"); _users.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created User {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _users.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed User {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _users.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Users"); _messages.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); _messages.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); _messages.ItemRemapped += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Remapped Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/[{e.OldId} -> {e.NewId}]"); _messages.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Messages"); _roles.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Role {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _roles.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Role {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _roles.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Roles"); _servers.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Server {e.Item.Id}"); _servers.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Server {e.Item.Id}"); _servers.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Servers"); _globalUsers.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created User {e.Item.Id}"); _globalUsers.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed User {e.Item.Id}"); _globalUsers.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Users"); } if (Config.UseMessageQueue) { _pendingMessages = new ConcurrentQueue <Message>(); } _serializer = new JsonSerializer(); _serializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc; #if TEST_RESPONSES _serializer.CheckAdditionalContent = true; _serializer.MissingMemberHandling = MissingMemberHandling.Error; #endif }
/// <summary> Initializes a new instance of the DiscordClient class. </summary> public DiscordClient(DiscordClientConfig config = null) : base(config ?? new DiscordClientConfig()) { _rand = new Random(); _api = new DiscordAPIClient(_config.LogLevel, _config.UserAgent, _config.APITimeout); if (Config.UseMessageQueue) _pendingMessages = new ConcurrentQueue<Message>(); if (Config.EnableVoiceMultiserver) _voiceClients = new ConcurrentDictionary<string, DiscordSimpleClient>(); object cacheLock = new object(); _channels = new Channels(this, cacheLock); _members = new Members(this, cacheLock); _messages = new Messages(this, cacheLock); _roles = new Roles(this, cacheLock); _servers = new Servers(this, cacheLock); _users = new Users(this, cacheLock); _status = UserStatus.Online; this.Connected += async (s, e) => { _api.CancelToken = CancelToken; await SendStatus(); }; VoiceDisconnected += (s, e) => { foreach (var member in _members) { if (member.ServerId == e.ServerId && member.IsSpeaking) { member.IsSpeaking = false; RaiseUserIsSpeaking(member, false); } } }; bool showIDs = _config.LogLevel > LogMessageSeverity.Debug; //Hide this for now if (_config.LogLevel >= LogMessageSeverity.Info) { ServerCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Created Server: {e.Server?.Name}" + (showIDs ? $" ({e.ServerId})" : "")); ServerDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Destroyed Server: {e.Server?.Name}" + (showIDs ? $" ({e.ServerId})" : "")); ServerUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Updated Server: {e.Server?.Name}" + (showIDs ? $" ({e.ServerId})" : "")); ServerAvailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Unavailable: {e.Server?.Name}" + (showIDs ? $" ({e.ServerId})" : "")); ServerUnavailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Unavailable: {e.Server?.Name}" + (showIDs ? $" ({e.ServerId})" : "")); ChannelCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Created Channel: {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId})" : "")); ChannelDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Destroyed Channel: {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId})" : "")); ChannelUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Updated Channel: {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId})" : "")); MessageCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Created Message: {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}/{e.MessageId}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId}/{e.MessageId})" : "")); MessageDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Deleted Message: {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}/{e.MessageId}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId}/{e.MessageId})" : "")); MessageUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Updated Message: {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}/{e.MessageId}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId}/{e.MessageId})" : "")); RoleCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Created Role: {e.Server?.Name ?? "[Private]"}/{e.Role.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.RoleId})." : "")); RoleUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Updated Role: {e.Server?.Name ?? "[Private]"}/{e.Role.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.RoleId})." : "")); RoleDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Deleted Role: {e.Server?.Name ?? "[Private]"}/{e.Role.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.RoleId})." : "")); BanAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Added Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? "Unknown"}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? "Unknown"}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Added Member: {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Removed Member: {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); MemberUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Updated Member: {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Updated Member (Voice State): {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + (showIDs ? $" ({e.ServerId ?? "0"}/{e.UserId})" : "")); UserUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Updated User: {e.User.Name}" + (showIDs ? $" ({e.UserId})." : "")); } if (_config.LogLevel >= LogMessageSeverity.Verbose) { UserIsTyping += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Updated User (Is Typing): {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}/{e.User.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId}/{e.UserId})" : "")); MessageReadRemotely += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Read Message (Remotely): {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}/{e.MessageId}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId}/{e.MessageId})" : "")); MessageSent += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Sent Message: {e.Server?.Name ?? "[Private]"}/{e.Channel.Name}/{e.MessageId}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.ChannelId}/{e.MessageId})" : "")); UserPresenceUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Updated Member (Presence): {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})" : "")); _api.RestClient.OnRequest += (s, e) => { if (e.Payload != null) RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms ({e.Payload})"); else RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms"); }; } if (_config.LogLevel >= LogMessageSeverity.Debug) { _channels.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Channel {e.Item?.ServerId ?? "[Private]"}/{e.Item.Id}"); _channels.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Channel {e.Item.ServerId ?? "[Private]"}/{e.Item.Id}"); _channels.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Channels"); _members.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Member {e.Item.ServerId ?? "[Private]"}/{e.Item.UserId}"); _members.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Member {e.Item.ServerId ?? "[Private]"}/{e.Item.UserId}"); _members.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Members"); _messages.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Message {e.Item.ServerId ?? "[Private]"}/{e.Item.ChannelId}/{e.Item.Id}"); _messages.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Message {e.Item.ServerId ?? "[Private]"}/{e.Item.ChannelId}/{e.Item.Id}"); _messages.ItemRemapped += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Remapped Message {e.Item.ServerId ?? "[Private]"}/{e.Item.ChannelId}/[{e.OldId} -> {e.NewId}]"); _messages.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Messages"); _roles.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Role {e.Item.ServerId}/{e.Item.Id}"); _roles.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Role {e.Item.ServerId}/{e.Item.Id}"); _roles.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Roles"); _servers.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Server {e.Item.Id}"); _servers.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Server {e.Item.Id}"); _servers.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Servers"); _users.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created User {e.Item.Id}"); _users.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed User {e.Item.Id}"); _users.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Users"); } if (Config.UseMessageQueue) _pendingMessages = new ConcurrentQueue<Message>(); _serializer = new JsonSerializer(); #if TEST_RESPONSES _serializer.CheckAdditionalContent = true; _serializer.MissingMemberHandling = MissingMemberHandling.Error; #endif }
static void Main(string[] args) { if (!restart) { rpg = new KokoroBotRPG(); loadFiles(); } else { restart = false; } { DiscordClientConfig config = new DiscordClientConfig(); config.VoiceMode = DiscordVoiceMode.Outgoing; config.VoiceBufferLength = 40; var client = new DiscordClient(config); //Display all log messages in the console client.LogMessage += (s, e) => Console.WriteLine($"[{e.Severity}] {e.Source}: {e.Message}"); client.MessageReceived += async (s, e) => { Console.WriteLine(e.Message.User.Name + ": " + e.Message.Text); if (!e.Message.IsAuthor) { var currentChannel = e.Channel; if (e.User.Id == 95543627391959040) { if (e.Message.Text == "-mute") { mute = !mute; await client.SendMessage(currentChannel, "KokoroBot is now mute: " + mute.ToString()); } else if (e.Message.Text == "-clear") { if (voiceclient != null) voiceclient.ClearVoicePCM(); } else if (e.Message.Text == "-save") { saveFiles(); await client.SendMessage(currentChannel, "I have saved everything :3"); } else if (e.Message.Text == "-dc") { quit = true; running = false; } else if (e.Message.Text == "-restart") { await client.SendMessage(currentChannel, "Cya on the other side :3"); restart = true; running = false; await client.Disconnect(); } else if (e.Message.Text.StartsWith("-join")) { var channels = e.Server.Channels.Where((Channel chan) => { return e.Message.Text.Substring(5).TrimStart(' ') == chan.Name && chan.Type == ChannelType.Voice; }); if (channels.Any()) { var channel = channels.First(); Console.WriteLine("KokoroBot tries to join Channel: " + channel.Name); voiceclient = await client.JoinVoiceServer(channel); voiceserver = e.Message.Server; } } else if (e.Message.Text == "-leave") { if (voiceclient != null) { voiceclient.ClearVoicePCM(); await client.LeaveVoiceServer(voiceserver); voiceclient = null; voiceserver = null; } } } else if (e.User.Name == "part") { await client.SendMessage(currentChannel, "I don't like you. B-b-baka. >.<"); return; } if (!mute) { if (e.Message.Text.Length > 0) { string[] splitmessage = e.Message.Text.Split(' '); if (splitmessage[0] == "-kardfacts") { if (splitmessage.Length > 2) { if (splitmessage[1] == "add") { try { string finalstr = ""; for (int i = 2; i < splitmessage.Length; i++) { if (i != 2) finalstr += ' ' + splitmessage[i]; else finalstr = splitmessage[i]; } if (finalstr.Length > 5) { kardFactsStrings.Add(finalstr); await client.SendMessage(currentChannel, "A new fact about Kard has been added. (Yay ^-^):"); currentChannel = e.Channel; await client.SendMessage(currentChannel, finalstr); } else { throw new IOException("Hue."); } } catch (Exception) { await client.SendMessage(currentChannel, "That hurt <.< Don't do this again, ok? :3"); } } } else { await client.SendMessage(currentChannel, kardFacts()); } } else if (e.Message.Text.StartsWith("-play")) { Task.Run(() => { PlaySoundWav(e); }); } else if(e.Message.Text.StartsWith("-getclientid")) { if (e.Message.Text.Length > "-getclientid ".Length) { try { await client.SendMessage(currentChannel, e.Server.Members.Where((User u) => { return u.Name.StartsWith(e.Message.Text.Substring("-getclientid ".Length)); }).First().Id.ToString()); } catch(Exception) { await client.SendMessage(currentChannel, "User does not exist. B-baka."); } } else { await client.SendMessage(currentChannel, e.Message.User.Id.ToString()); } } else if(e.Message.Text.StartsWith(":")) { await rpg.HandleCommands(e, client, currentChannel); } else if (await handleSimpleCommands(e, client, currentChannel) == false) { await handleTiroCommands(e, client, currentChannel); } } } } }; //Convert our sync method to an async one and block the Main function until the bot disconnects client.Run(async () => { //Connect to the Discord server using our email and password await client.Connect(Sensitive.email, Sensitive.passwd); while (running) { var inputTask = Task.Run<string>((Func<string>)Console.ReadLine); await inputTask; string dbgCommand = inputTask.Result; if( dbgCommand == "exit" || restart || quit) { running = false; await client.Disconnect(); } else if ( dbgCommand == "listservers") { foreach(Server s in client.AllServers) { Console.WriteLine("#######################################"); Console.WriteLine("Servername: " + s.Name); Console.WriteLine("Voicechannels: "); foreach(Channel c in s.VoiceChannels) { Console.WriteLine(" "+c.Name); } Console.WriteLine("Channels: "); foreach (Channel c in s.Channels) { Console.WriteLine(" "+c.Name); } } } } //If we are not a member of any server, use our invite code (made beforehand in the official Discord Client) }); } if (!restart) { saveFiles(); } else { Main(new string[] { }); } }
/// <summary> Initializes a new instance of the DiscordClient class. </summary> public DiscordClient(DiscordClientConfig config = null) : base(config ?? new DiscordClientConfig()) { _rand = new Random(); _api = new DiscordAPIClient(_config); if (Config.UseMessageQueue) _pendingMessages = new ConcurrentQueue<Message>(); if (Config.EnableVoiceMultiserver) _voiceClients = new ConcurrentDictionary<string, DiscordWSClient>(); object cacheLock = new object(); _channels = new Channels(this, cacheLock); _users = new Users(this, cacheLock); _messages = new Messages(this, cacheLock, Config.MessageCacheLength > 0); _roles = new Roles(this, cacheLock); _servers = new Servers(this, cacheLock); _globalUsers = new GlobalUsers(this, cacheLock); _status = UserStatus.Online; this.Connected += async (s, e) => { _api.CancelToken = _cancelToken; await SendStatus().ConfigureAwait(false); }; VoiceDisconnected += (s, e) => { var server = _servers[e.ServerId]; if (server != null) { foreach (var member in server.Members) { if (member.IsSpeaking) { member.IsSpeaking = false; RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false); } } } }; if (_config.LogLevel >= LogMessageSeverity.Info) { ServerCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Created: {e.Server?.Name ?? "[Private]"}"); ServerDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Destroyed: {e.Server?.Name ?? "[Private]"}"); ServerUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Updated: {e.Server?.Name ?? "[Private]"}"); ServerAvailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Available: {e.Server?.Name ?? "[Private]"}"); ServerUnavailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Server Unavailable: {e.Server?.Name ?? "[Private]"}"); ChannelCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Channel Created: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}"); ChannelDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Channel Destroyed: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}"); ChannelUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Channel Updated: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}"); MessageReceived += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Message Received: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); MessageDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Message Deleted: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); MessageUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Message Update: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); RoleCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Role Created: {e.Server?.Name ?? "[Private]"}/{e.Role?.Name}"); RoleUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Role Updated: {e.Server?.Name ?? "[Private]"}/{e.Role?.Name}"); RoleDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Role Deleted: {e.Server?.Name ?? "[Private]"}/{e.Role?.Name}"); UserBanned += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Banned User: {e.Server?.Name ?? "[Private]" }/{e.UserId}"); UserUnbanned += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Unbanned User: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Joined: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Left: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); UserUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Updated: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"User Updated (Voice State): {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, "Profile Updated"); } if (_config.LogLevel >= LogMessageSeverity.Verbose) { UserIsTypingUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"User Updated (Is Typing): {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.User?.Name}"); MessageReadRemotely += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Read Message (Remotely): {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); MessageSent += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Sent Message: {e.Server?.Name ?? "[Private]"}/{e.Channel?.Name}/{e.Message?.Id}"); UserPresenceUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"User Updated (Presence): {e.Server?.Name ?? "[Private]"}/{e.User?.Name}"); _api.RestClient.OnRequest += (s, e) => { if (e.Payload != null) RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms ({e.Payload})"); else RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms"); }; } if (_config.LogLevel >= LogMessageSeverity.Debug) { _channels.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Channel {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _channels.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Channel {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _channels.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Channels"); _users.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created User {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _users.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed User {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _users.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Users"); _messages.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); _messages.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); _messages.ItemRemapped += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Remapped Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/[{e.OldId} -> {e.NewId}]"); _messages.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Messages"); _roles.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Role {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _roles.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Role {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); _roles.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Roles"); _servers.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Server {e.Item.Id}"); _servers.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Server {e.Item.Id}"); _servers.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Servers"); _globalUsers.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created User {e.Item.Id}"); _globalUsers.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed User {e.Item.Id}"); _globalUsers.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Users"); } if (Config.UseMessageQueue) _pendingMessages = new ConcurrentQueue<Message>(); _serializer = new JsonSerializer(); _serializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc; #if TEST_RESPONSES _serializer.CheckAdditionalContent = true; _serializer.MissingMemberHandling = MissingMemberHandling.Error; #endif }
public DiscordBotClient(DiscordClientConfig config = null, Func<User, Server, int> getPermissions = null) : base(config) { _commands = new List<Command>(); CommandChar = '~'; UseCommandChar = true; RequireCommandCharInPublic = true; RequireCommandCharInPrivate = true; MessageCreated += async (s, e) => { //If commands aren't being used, don't bother processing them if (_commands.Count == 0) return; //Ignore messages from ourselves if (e.Message.UserId == CurrentUserId) return; //Check for the command character string msg = e.Message.Text; if (UseCommandChar) { if (msg.Length == 0) return; bool isPrivate = e.Message.Channel.IsPrivate; bool hasCommandChar = msg[0] == CommandChar; if (hasCommandChar) msg = msg.Substring(1); if (!isPrivate && RequireCommandCharInPublic && !hasCommandChar) return; if (isPrivate && RequireCommandCharInPrivate && !hasCommandChar) return; } string[] args; if (!CommandParser.ParseArgs(msg, out args)) return; for (int i = 0; i < _commands.Count; i++) { Command cmd = _commands[i]; //Check Command Parts if (args.Length < cmd.Parts.Length) continue; bool isValid = true; for (int j = 0; j < cmd.Parts.Length; j++) { if (!string.Equals(args[j], cmd.Parts[j], StringComparison.OrdinalIgnoreCase)) { isValid = false; break; } } if (!isValid) continue; //Check Arg Count int argCount = args.Length - cmd.Parts.Length; if (argCount < cmd.MinArgs || argCount > cmd.MaxArgs) continue; //Clean Args string[] newArgs = new string[argCount]; for (int j = 0; j < newArgs.Length; j++) newArgs[j] = args[j + cmd.Parts.Length]; //Check Permissions int permissions = getPermissions != null ? getPermissions(e.Message.User, e.Message.Channel?.Server) : 0; var eventArgs = new CommandEventArgs(e.Message, cmd, msg, permissions, newArgs); if (permissions < cmd.MinPerms) { RaiseCommandError(eventArgs, new PermissionException()); return; } //Run Command RaiseRanCommand(eventArgs); try { var task = cmd.Handler(eventArgs); if (task != null) await task; } catch (Exception ex) { RaiseCommandError(eventArgs, ex); } break; } }; }
public DiscordBotClient(DiscordClientConfig config = null, Func <User, Server, int> getPermissions = null) : base(config) { _commands = new List <Command>(); CommandChar = '~'; RequireCommandCharInPublic = true; RequireCommandCharInPrivate = true; MessageCreated += async(s, e) => { //Ignore messages from ourselves if (e.Message.UserId == _myId) { return; } //Check for the command character string msg = e.Message.Text; if (UseCommandChar) { if (msg.Length == 0) { return; } bool isPrivate = e.Message.Channel.IsPrivate; bool hasCommandChar = msg[0] == CommandChar; if (hasCommandChar) { msg = msg.Substring(1); } if (!isPrivate && RequireCommandCharInPublic && !hasCommandChar) { return; } if (isPrivate && RequireCommandCharInPrivate && !hasCommandChar) { return; } } string[] args; if (!CommandParser.ParseArgs(msg, out args)) { return; } for (int i = 0; i < _commands.Count; i++) { Command cmd = _commands[i]; //Check Command Parts if (args.Length < cmd.Parts.Length) { continue; } bool isValid = true; for (int j = 0; j < cmd.Parts.Length; j++) { if (!string.Equals(args[j], cmd.Parts[j], StringComparison.OrdinalIgnoreCase)) { isValid = false; break; } } if (!isValid) { continue; } //Check Arg Count int argCount = args.Length - cmd.Parts.Length; if (argCount < cmd.MinArgs || argCount > cmd.MaxArgs) { continue; } //Clean Args string[] newArgs = new string[argCount]; for (int j = 0; j < newArgs.Length; j++) { newArgs[j] = args[j + cmd.Parts.Length]; } //Check Permissions int permissions = getPermissions != null?getPermissions(e.Message.User, e.Message.Channel?.Server) : 0; var eventArgs = new CommandEventArgs(e.Message, cmd, msg, permissions, newArgs); if (permissions < cmd.MinPerms) { RaiseCommandError(eventArgs, new PermissionException()); return; } //Run Command RaiseRanCommand(eventArgs); try { var task = cmd.Handler(eventArgs); if (task != null) { await task; } } catch (Exception ex) { RaiseCommandError(eventArgs, ex); } break; } }; }
//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; } }; }
//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; } }; }