using Discord.Commands; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace Discord.Modules { public class ModuleManager { public event EventHandler ServerEnabled; public event EventHandler ServerDisabled; public event EventHandler ChannelEnabled; public event EventHandler ChannelDisabled; public event EventHandler LeftServer; public event EventHandler ServerUpdated; public event EventHandler ServerUnavailable; public event EventHandler ServerAvailable; public event EventHandler UserBanned; public event EventHandler UserUnbanned; public event EventHandler ChannelCreated; public event EventHandler ChannelDestroyed; public event EventHandler ChannelUpdated; public event EventHandler RoleCreated; public event EventHandler RoleUpdated; public event EventHandler RoleDeleted; public event EventHandler UserJoined; public event EventHandler UserLeft; public event EventHandler UserUpdated; public event EventHandler UserPresenceUpdated; public event EventHandler UserVoiceStateUpdated; public event EventHandler UserIsTypingUpdated; public event EventHandler UserIsSpeakingUpdated; public event EventHandler MessageReceived; public event EventHandler MessageSent; public event EventHandler MessageDeleted; public event EventHandler MessageUpdated; public event EventHandler MessageReadRemotely; private readonly DiscordClient _client; private readonly string _name, _id; private readonly FilterType _filterType; private readonly bool _useServerWhitelist, _useChannelWhitelist, _allowAll, _allowPrivate; private readonly ConcurrentDictionary _enabledServers; private readonly ConcurrentDictionary _enabledChannels; private readonly ConcurrentDictionary _indirectServers; public DiscordClient Client => _client; public string Name => _name; public string Id => _id; public FilterType FilterType => _filterType; public IEnumerable EnabledServers => _enabledServers.Select(x => x.Value); public IEnumerable EnabledChannels => _enabledChannels.Select(x => x.Value); internal ModuleManager(DiscordClient client, string name, FilterType filterType) { _client = client; _name = name; _id = name.ToLowerInvariant(); _filterType = filterType; _allowAll = filterType == FilterType.Unrestricted; _useServerWhitelist = filterType.HasFlag(FilterType.ServerWhitelist); _useChannelWhitelist = filterType.HasFlag(FilterType.ChannelWhitelist); _allowPrivate = filterType.HasFlag(FilterType.AllowPrivate); _enabledServers = new ConcurrentDictionary(); _enabledChannels = new ConcurrentDictionary(); _indirectServers = new ConcurrentDictionary(); if (_allowAll || _useServerWhitelist) //Server-only events { client.ChannelCreated += (s, e) => { if (ChannelCreated != null && HasServer(e.Server)) ChannelCreated(s, e); }; client.UserVoiceStateUpdated += (s, e) => { if (UserVoiceStateUpdated != null && HasServer(e.Server)) UserVoiceStateUpdated(s, e); }; client.UserIsSpeakingUpdated += (s, e) => { if (UserIsSpeakingUpdated != null && HasServer(e.Server)) UserIsSpeakingUpdated(s, e); }; } client.ChannelDestroyed += (s, e) => { if (ChannelDestroyed != null && HasChannel(e.Channel)) ChannelDestroyed(s, e); }; client.ChannelUpdated += (s, e) => { if (ChannelUpdated != null && HasChannel(e.Channel)) ChannelUpdated(s, e); }; client.MessageReceived += (s, e) => { if (MessageReceived != null && HasChannel(e.Channel)) MessageReceived(s, e); }; client.MessageSent += (s, e) => { if (MessageSent != null && HasChannel(e.Channel)) MessageSent(s, e); }; client.MessageDeleted += (s, e) => { if (MessageDeleted != null && HasChannel(e.Channel)) MessageDeleted(s, e); }; client.MessageUpdated += (s, e) => { if (MessageUpdated != null && HasChannel(e.Channel)) MessageUpdated(s, e); }; client.MessageReadRemotely += (s, e) => { if (MessageReadRemotely != null && HasChannel(e.Channel)) MessageReadRemotely(s, e); }; client.RoleCreated += (s, e) => { if (RoleCreated != null && HasIndirectServer(e.Server)) RoleCreated(s, e); }; client.RoleUpdated += (s, e) => { if (RoleUpdated != null && HasIndirectServer(e.Server)) RoleUpdated(s, e); }; client.RoleDeleted += (s, e) => { if (RoleDeleted != null && HasIndirectServer(e.Server)) RoleDeleted(s, e); }; client.LeftServer += (s, e) => { if (LeftServer != null && HasIndirectServer(e.Server)) { DisableServer(e.Server); LeftServer(s, e); } }; client.ServerUpdated += (s, e) => { if (ServerUpdated != null && HasIndirectServer(e.Server)) ServerUpdated(s, e); }; client.ServerUnavailable += (s, e) => { if (ServerUnavailable != null && HasIndirectServer(e.Server)) ServerUnavailable(s, e); }; client.ServerAvailable += (s, e) => { if (ServerAvailable != null && HasIndirectServer(e.Server)) ServerAvailable(s, e); }; client.UserJoined += (s, e) => { if (UserJoined != null && HasIndirectServer(e.Server)) UserJoined(s, e); }; client.UserLeft += (s, e) => { if (UserLeft != null && HasIndirectServer(e.Server)) UserLeft(s, e); }; client.UserUpdated += (s, e) => { if (UserUpdated != null && HasIndirectServer(e.Server)) UserUpdated(s, e); }; client.UserIsTypingUpdated += (s, e) => { if (UserIsSpeakingUpdated != null && HasChannel(e.Channel)) UserIsTypingUpdated(s, e); }; //TODO: We aren't getting events from UserPresence if AllowPrivate is enabled, but the server we know that user through isn't on the whitelist client.UserPresenceUpdated += (s, e) => { if (UserPresenceUpdated != null && HasIndirectServer(e.Server)) UserPresenceUpdated(s, e); }; client.UserBanned += (s, e) => { if (UserBanned != null && HasIndirectServer(e.Server)) UserBanned(s, e); }; client.UserUnbanned += (s, e) => { if (UserUnbanned != null && HasIndirectServer(e.Server)) UserUnbanned(s, e); }; } public void CreateCommands(string prefix, Action config) { var commandService = _client.Commands(true); commandService.CreateGroup(prefix, x => { x.Category(_name); x.AddCheck(new ModuleChecker(this)); config(x); }); } public bool EnableServer(Server server) { if (server == null) throw new ArgumentNullException(nameof(server)); if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist."); lock (this) return EnableServerInternal(server); } public void EnableServers(IEnumerable servers) { if (servers == null) throw new ArgumentNullException(nameof(servers)); if (servers.Contains(null)) throw new ArgumentException("Collection cannot contain null.", nameof(servers)); if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist."); lock (this) { foreach (var server in servers) EnableServerInternal(server); } } private bool EnableServerInternal(Server server) { if (_enabledServers.TryAdd(server.Id, server)) { if (ServerEnabled != null) ServerEnabled(this, new ServerEventArgs(server)); return true; } return false; } public bool DisableServer(Server server) { if (server == null) throw new ArgumentNullException(nameof(server)); if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist."); lock (this) { if (_enabledServers.TryRemove(server.Id, out server)) { if (ServerDisabled != null) ServerDisabled(this, new ServerEventArgs(server)); return true; } return false; } } public void DisableAllServers() { if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist."); lock (this) { if (ServerDisabled != null) { foreach (var server in _enabledServers) ServerDisabled(this, new ServerEventArgs(server.Value)); } _enabledServers.Clear(); } } public bool EnableChannel(Channel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (!_useChannelWhitelist) throw new InvalidOperationException("This module is not configured to use a channel whitelist."); lock (this) return EnableChannelInternal(channel); } public void EnableChannels(IEnumerable channels) { if (channels == null) throw new ArgumentNullException(nameof(channels)); if (channels.Contains(null)) throw new ArgumentException("Collection cannot contain null.", nameof(channels)); if (!_useChannelWhitelist) throw new InvalidOperationException("This module is not configured to use a channel whitelist."); lock (this) { foreach (var channel in channels) EnableChannelInternal(channel); } } private bool EnableChannelInternal(Channel channel) { if (_enabledChannels.TryAdd(channel.Id, channel)) { var server = channel.Server; if (server != null) { int value = 0; _indirectServers.TryGetValue(server.Id, out value); value++; _indirectServers[server.Id] = value; } if (ChannelEnabled != null) ChannelEnabled(this, new ChannelEventArgs(channel)); return true; } return false; } public bool DisableChannel(Channel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (!_useChannelWhitelist) throw new InvalidOperationException("This module is not configured to use a channel whitelist."); lock (this) { Channel ignored; if (_enabledChannels.TryRemove(channel.Id, out ignored)) { var server = channel.Server; if (server != null) { int value = 0; _indirectServers.TryGetValue(server.Id, out value); value--; if (value <= 0) _indirectServers.TryRemove(server.Id, out value); else _indirectServers[server.Id] = value; } if (ChannelDisabled != null) ChannelDisabled(this, new ChannelEventArgs(channel)); return true; } return false; } } public void DisableAllChannels() { if (!_useChannelWhitelist) throw new InvalidOperationException("This module is not configured to use a channel whitelist."); lock (this) { if (ChannelDisabled != null) { foreach (var channel in _enabledChannels) ChannelDisabled(this, new ChannelEventArgs(channel.Value)); } _enabledChannels.Clear(); _indirectServers.Clear(); } } public void DisableAll() { if (_useServerWhitelist) DisableAllServers(); if (_useChannelWhitelist) DisableAllChannels(); } internal bool HasServer(Server server) => _allowAll || _useServerWhitelist && _enabledServers.ContainsKey(server.Id); internal bool HasIndirectServer(Server server) => _allowAll || (_useServerWhitelist && _enabledServers.ContainsKey(server.Id)) || (_useChannelWhitelist && _indirectServers.ContainsKey(server.Id)); internal bool HasChannel(Channel channel) { if (_allowAll) return true; if (channel.IsPrivate) return _allowPrivate; if (_useChannelWhitelist && _enabledChannels.ContainsKey(channel.Id)) return true; if (_useServerWhitelist) { var server = channel.Server; if (server == null) return false; if (_enabledServers.ContainsKey(server.Id)) return true; } return false; } } }