| @@ -84,9 +84,18 @@ | |||||
| <Compile Include="..\Discord.Net.Audio\Sodium\SecretBox.cs"> | <Compile Include="..\Discord.Net.Audio\Sodium\SecretBox.cs"> | ||||
| <Link>Sodium\SecretBox.cs</Link> | <Link>Sodium\SecretBox.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Audio\UserIsTalkingEventArgs.cs"> | |||||
| <Link>UserIsTalkingEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Audio\VoiceBuffer.cs"> | <Compile Include="..\Discord.Net.Audio\VoiceBuffer.cs"> | ||||
| <Link>VoiceBuffer.cs</Link> | <Link>VoiceBuffer.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Audio\VoiceDisconnectedEventArgs.cs"> | |||||
| <Link>VoiceDisconnectedEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Audio\VoicePacketEventArgs.cs"> | |||||
| <Link>VoicePacketEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="Properties\AssemblyInfo.cs" /> | <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| @@ -1,49 +1,10 @@ | |||||
| using Discord.Net.WebSockets; | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Audio | namespace Discord.Audio | ||||
| { | { | ||||
| public class VoiceDisconnectedEventArgs : DisconnectedEventArgs | |||||
| { | |||||
| public readonly ulong ServerId; | |||||
| public VoiceDisconnectedEventArgs(ulong serverId, DisconnectedEventArgs e) | |||||
| : base(e.WasUnexpected, e.Exception) | |||||
| { | |||||
| ServerId = serverId; | |||||
| } | |||||
| } | |||||
| public class UserIsSpeakingEventArgs : UserEventArgs | |||||
| { | |||||
| public readonly bool IsSpeaking; | |||||
| public UserIsSpeakingEventArgs(User user, bool isSpeaking) | |||||
| : base(user) | |||||
| { | |||||
| IsSpeaking = isSpeaking; | |||||
| } | |||||
| } | |||||
| public class VoicePacketEventArgs : EventArgs | |||||
| { | |||||
| public readonly ulong UserId; | |||||
| public readonly ulong ChannelId; | |||||
| public readonly byte[] Buffer; | |||||
| public readonly int Offset; | |||||
| public readonly int Count; | |||||
| public VoicePacketEventArgs(ulong userId, ulong channelId, byte[] buffer, int offset, int count) | |||||
| { | |||||
| UserId = userId; | |||||
| ChannelId = channelId; | |||||
| Buffer = buffer; | |||||
| Offset = offset; | |||||
| Count = count; | |||||
| } | |||||
| } | |||||
| public class AudioService : IService | public class AudioService : IService | ||||
| { | { | ||||
| private AudioClient _defaultClient; | private AudioClient _defaultClient; | ||||
| @@ -51,46 +12,33 @@ namespace Discord.Audio | |||||
| private ConcurrentDictionary<User, bool> _talkingUsers; | private ConcurrentDictionary<User, bool> _talkingUsers; | ||||
| //private int _nextClientId; | //private int _nextClientId; | ||||
| internal DiscordClient Client => _client; | |||||
| private DiscordClient _client; | |||||
| internal DiscordClient Client { get; private set; } | |||||
| public AudioServiceConfig Config { get; } | |||||
| public AudioServiceConfig Config => _config; | |||||
| private readonly AudioServiceConfig _config; | |||||
| public event EventHandler Connected = delegate { }; | |||||
| public event EventHandler<VoiceDisconnectedEventArgs> Disconnected = delegate { }; | |||||
| public event EventHandler<VoicePacketEventArgs> PacketReceived = delegate { }; | |||||
| public event EventHandler<UserIsSpeakingEventArgs> UserIsSpeakingUpdated = delegate { }; | |||||
| public event EventHandler Connected; | |||||
| private void RaiseConnected() | |||||
| { | |||||
| if (Connected != null) | |||||
| Connected(this, EventArgs.Empty); | |||||
| } | |||||
| public event EventHandler<VoiceDisconnectedEventArgs> Disconnected; | |||||
| private void RaiseDisconnected(ulong serverId, DisconnectedEventArgs e) | |||||
| { | |||||
| if (Disconnected != null) | |||||
| Disconnected(this, new VoiceDisconnectedEventArgs(serverId, e)); | |||||
| } | |||||
| public event EventHandler<VoicePacketEventArgs> OnPacket; | |||||
| internal void RaiseOnPacket(VoicePacketEventArgs e) | |||||
| { | |||||
| if (OnPacket != null) | |||||
| OnPacket(this, e); | |||||
| } | |||||
| public event EventHandler<UserIsSpeakingEventArgs> UserIsSpeakingUpdated; | |||||
| private void RaiseUserIsSpeakingUpdated(User user, bool isSpeaking) | |||||
| { | |||||
| if (UserIsSpeakingUpdated != null) | |||||
| UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking)); | |||||
| } | |||||
| private void OnConnected() | |||||
| => Connected(this, EventArgs.Empty); | |||||
| private void OnDisconnected(ulong serverId, bool wasUnexpected, Exception ex) | |||||
| => Disconnected(this, new VoiceDisconnectedEventArgs(serverId, wasUnexpected, ex)); | |||||
| internal void OnPacketReceived(VoicePacketEventArgs e) | |||||
| => PacketReceived(this, e); | |||||
| private void OnUserIsSpeakingUpdated(User user, bool isSpeaking) | |||||
| => UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking)); | |||||
| public AudioService(AudioServiceConfig config) | public AudioService(AudioServiceConfig config) | ||||
| { | { | ||||
| _config = config; | |||||
| _config.Lock(); | |||||
| Config = config; | |||||
| } | } | ||||
| public void Install(DiscordClient client) | public void Install(DiscordClient client) | ||||
| { | { | ||||
| _client = client; | |||||
| if (Config.EnableMultiserver) | |||||
| Client = client; | |||||
| Config.Lock(); | |||||
| if (Config.EnableMultiserver) | |||||
| _voiceClients = new ConcurrentDictionary<ulong, IAudioClient>(); | _voiceClients = new ConcurrentDictionary<ulong, IAudioClient>(); | ||||
| else | else | ||||
| { | { | ||||
| @@ -113,7 +61,7 @@ namespace Discord.Audio | |||||
| { | { | ||||
| bool ignored; | bool ignored; | ||||
| if (_talkingUsers.TryRemove(member.Key, out ignored)) | if (_talkingUsers.TryRemove(member.Key, out ignored)) | ||||
| RaiseUserIsSpeakingUpdated(member.Key, false); | |||||
| OnUserIsSpeakingUpdated(member.Key, false); | |||||
| } | } | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -0,0 +1,13 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public class UserIsSpeakingEventArgs : UserEventArgs | |||||
| { | |||||
| public bool IsSpeaking { get; } | |||||
| public UserIsSpeakingEventArgs(User user, bool isSpeaking) | |||||
| : base(user) | |||||
| { | |||||
| IsSpeaking = isSpeaking; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| using System; | |||||
| namespace Discord | |||||
| { | |||||
| public class VoiceDisconnectedEventArgs : DisconnectedEventArgs | |||||
| { | |||||
| public ulong ServerId { get; } | |||||
| public VoiceDisconnectedEventArgs(ulong serverId, bool wasUnexpected, Exception ex) | |||||
| : base(wasUnexpected, ex) | |||||
| { | |||||
| ServerId = serverId; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,22 @@ | |||||
| using System; | |||||
| namespace Discord | |||||
| { | |||||
| public class VoicePacketEventArgs : EventArgs | |||||
| { | |||||
| public ulong UserId { get; } | |||||
| public ulong ChannelId { get; } | |||||
| public byte[] Buffer { get; } | |||||
| public int Offset { get; } | |||||
| public int Count { get; } | |||||
| public VoicePacketEventArgs(ulong userId, ulong channelId, byte[] buffer, int offset, int count) | |||||
| { | |||||
| UserId = userId; | |||||
| ChannelId = channelId; | |||||
| Buffer = buffer; | |||||
| Offset = offset; | |||||
| Count = count; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -45,21 +45,27 @@ | |||||
| <Compile Include="..\Discord.Net.Commands\CommandBuilder.cs"> | <Compile Include="..\Discord.Net.Commands\CommandBuilder.cs"> | ||||
| <Link>CommandBuilder.cs</Link> | <Link>CommandBuilder.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Commands\CommandErrorEventArgs.cs"> | |||||
| <Link>CommandErrorEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Commands\CommandEventArgs.cs"> | |||||
| <Link>CommandEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Commands\CommandExtensions.cs"> | <Compile Include="..\Discord.Net.Commands\CommandExtensions.cs"> | ||||
| <Link>CommandExtensions.cs</Link> | <Link>CommandExtensions.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Commands\CommandMap.cs"> | <Compile Include="..\Discord.Net.Commands\CommandMap.cs"> | ||||
| <Link>CommandMap.cs</Link> | <Link>CommandMap.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Commands\CommandParameter.cs"> | |||||
| <Link>CommandParameter.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Commands\CommandParser.cs"> | <Compile Include="..\Discord.Net.Commands\CommandParser.cs"> | ||||
| <Link>CommandParser.cs</Link> | <Link>CommandParser.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Commands\CommandService.cs"> | <Compile Include="..\Discord.Net.Commands\CommandService.cs"> | ||||
| <Link>CommandService.cs</Link> | <Link>CommandService.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Commands\CommandService.Events.cs"> | |||||
| <Link>CommandService.Events.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Commands\CommandServiceConfig.cs"> | <Compile Include="..\Discord.Net.Commands\CommandServiceConfig.cs"> | ||||
| <Link>CommandServiceConfig.cs</Link> | <Link>CommandServiceConfig.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -5,48 +5,24 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| public enum ParameterType | |||||
| { | |||||
| /// <summary> Catches a single required parameter. </summary> | |||||
| Required, | |||||
| /// <summary> Catches a single optional parameter. </summary> | |||||
| Optional, | |||||
| /// <summary> Catches a zero or more optional parameters. </summary> | |||||
| Multiple, | |||||
| /// <summary> Catches all remaining text as a single optional parameter. </summary> | |||||
| Unparsed | |||||
| } | |||||
| public sealed class CommandParameter | |||||
| { | |||||
| public string Name { get; } | |||||
| public int Id { get; internal set; } | |||||
| public ParameterType Type { get; } | |||||
| public CommandParameter(string name, ParameterType type) | |||||
| { | |||||
| Name = name; | |||||
| Type = type; | |||||
| } | |||||
| } | |||||
| public sealed class Command | public sealed class Command | ||||
| { | |||||
| public string Text { get; } | |||||
| { | |||||
| private string[] _aliases; | |||||
| internal CommandParameter[] _parameters; | |||||
| private IPermissionChecker[] _checks; | |||||
| private Func<CommandEventArgs, Task> _runFunc; | |||||
| internal readonly Dictionary<string, CommandParameter> _parametersByName; | |||||
| public string Text { get; } | |||||
| public string Category { get; internal set; } | public string Category { get; internal set; } | ||||
| public bool IsHidden { get; internal set; } | public bool IsHidden { get; internal set; } | ||||
| public string Description { get; internal set; } | public string Description { get; internal set; } | ||||
| public IEnumerable<string> Aliases => _aliases; | public IEnumerable<string> Aliases => _aliases; | ||||
| private string[] _aliases; | |||||
| public IEnumerable<CommandParameter> Parameters => _parameters; | public IEnumerable<CommandParameter> Parameters => _parameters; | ||||
| internal CommandParameter[] _parameters; | |||||
| private IPermissionChecker[] _checks; | |||||
| private Func<CommandEventArgs, Task> _runFunc; | |||||
| internal readonly Dictionary<string, CommandParameter> _parametersByName; | |||||
| public CommandParameter this[string name] => _parametersByName[name]; | |||||
| internal Command(string text) | |||||
| internal Command(string text) | |||||
| { | { | ||||
| Text = text; | Text = text; | ||||
| IsHidden = false; | IsHidden = false; | ||||
| @@ -55,7 +31,6 @@ namespace Discord.Commands | |||||
| _parametersByName = new Dictionary<string, CommandParameter>(); | _parametersByName = new Dictionary<string, CommandParameter>(); | ||||
| } | } | ||||
| public CommandParameter this[string name] => _parametersByName[name]; | |||||
| internal void SetAliases(string[] aliases) | internal void SetAliases(string[] aliases) | ||||
| { | { | ||||
| @@ -0,0 +1,18 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| public enum CommandErrorType { Exception, UnknownCommand, BadPermissions, BadArgCount, InvalidInput } | |||||
| public class CommandErrorEventArgs : CommandEventArgs | |||||
| { | |||||
| public CommandErrorType ErrorType { get; } | |||||
| public Exception Exception { get; } | |||||
| public CommandErrorEventArgs(CommandErrorType errorType, CommandEventArgs baseArgs, Exception ex) | |||||
| : base(baseArgs.Message, baseArgs.Command, baseArgs.Args) | |||||
| { | |||||
| Exception = ex; | |||||
| ErrorType = errorType; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,27 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| public class CommandEventArgs : EventArgs | |||||
| { | |||||
| private readonly string[] _args; | |||||
| public Message Message { get; } | |||||
| public Command Command { get; } | |||||
| public User User => Message.User; | |||||
| public Channel Channel => Message.Channel; | |||||
| public Server Server => Message.Channel.Server; | |||||
| public CommandEventArgs(Message message, Command command, string[] args) | |||||
| { | |||||
| Message = message; | |||||
| Command = command; | |||||
| _args = args; | |||||
| } | |||||
| public string[] Args => _args; | |||||
| public string GetArg(int index) => _args[index]; | |||||
| public string GetArg(string name) => _args[Command[name].Id]; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| namespace Discord.Commands | |||||
| { | |||||
| public enum ParameterType | |||||
| { | |||||
| /// <summary> Catches a single required parameter. </summary> | |||||
| Required, | |||||
| /// <summary> Catches a single optional parameter. </summary> | |||||
| Optional, | |||||
| /// <summary> Catches a zero or more optional parameters. </summary> | |||||
| Multiple, | |||||
| /// <summary> Catches all remaining text as a single optional parameter. </summary> | |||||
| Unparsed | |||||
| } | |||||
| public sealed class CommandParameter | |||||
| { | |||||
| public string Name { get; } | |||||
| public int Id { get; internal set; } | |||||
| public ParameterType Type { get; } | |||||
| public CommandParameter(string name, ParameterType type) | |||||
| { | |||||
| Name = name; | |||||
| Type = type; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,57 +0,0 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| public class CommandEventArgs : EventArgs | |||||
| { | |||||
| private readonly string[] _args; | |||||
| public Message Message { get; } | |||||
| public Command Command { get; } | |||||
| public User User => Message.User; | |||||
| public Channel Channel => Message.Channel; | |||||
| public Server Server => Message.Channel.Server; | |||||
| public CommandEventArgs(Message message, Command command, string[] args) | |||||
| { | |||||
| Message = message; | |||||
| Command = command; | |||||
| _args = args; | |||||
| } | |||||
| public string[] Args => _args; | |||||
| public string GetArg(int index) => _args[index]; | |||||
| public string GetArg(string name) => _args[Command[name].Id]; | |||||
| } | |||||
| public enum CommandErrorType { Exception, UnknownCommand, BadPermissions, BadArgCount, InvalidInput } | |||||
| public class CommandErrorEventArgs : CommandEventArgs | |||||
| { | |||||
| public CommandErrorType ErrorType { get; } | |||||
| public Exception Exception { get; } | |||||
| public CommandErrorEventArgs(CommandErrorType errorType, CommandEventArgs baseArgs, Exception ex) | |||||
| : base(baseArgs.Message, baseArgs.Command, baseArgs.Args) | |||||
| { | |||||
| Exception = ex; | |||||
| ErrorType = errorType; | |||||
| } | |||||
| } | |||||
| public partial class CommandService | |||||
| { | |||||
| public event EventHandler<CommandEventArgs> RanCommand; | |||||
| private void RaiseRanCommand(CommandEventArgs args) | |||||
| { | |||||
| if (RanCommand != null) | |||||
| RanCommand(this, args); | |||||
| } | |||||
| public event EventHandler<CommandErrorEventArgs> CommandError; | |||||
| private void RaiseCommandError(CommandErrorType errorType, CommandEventArgs args, Exception ex = null) | |||||
| { | |||||
| if (CommandError != null) | |||||
| CommandError(this, new CommandErrorEventArgs(errorType, args, ex)); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -6,42 +6,45 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| /// <summary> A Discord.Net client with extensions for handling common bot operations like text commands. </summary> | |||||
| public sealed partial class CommandService : IService | |||||
| public partial class CommandService : IService | |||||
| { | { | ||||
| private readonly CommandServiceConfig _config; | |||||
| private readonly CommandGroupBuilder _root; | |||||
| private DiscordClient _client; | |||||
| public DiscordClient Client => _client; | |||||
| public CommandGroupBuilder Root => _root; | |||||
| private readonly List<Command> _allCommands; | |||||
| private readonly Dictionary<string, CommandMap> _categories; | |||||
| private readonly CommandMap _map; //Command map stores all commands by their input text, used for fast resolving and parsing | |||||
| public CommandServiceConfig Config { get; } | |||||
| public CommandGroupBuilder Root { get; } | |||||
| public DiscordClient Client { get; private set; } | |||||
| //AllCommands store a flattened collection of all commands | //AllCommands store a flattened collection of all commands | ||||
| public IEnumerable<Command> AllCommands => _allCommands; | |||||
| private readonly List<Command> _allCommands; | |||||
| //Command map stores all commands by their input text, used for fast resolving and parsing | |||||
| private readonly CommandMap _map; | |||||
| public IEnumerable<Command> AllCommands => _allCommands; | |||||
| //Groups store all commands by their module, used for more informative help | //Groups store all commands by their module, used for more informative help | ||||
| internal IEnumerable<CommandMap> Categories => _categories.Values; | internal IEnumerable<CommandMap> Categories => _categories.Values; | ||||
| private readonly Dictionary<string, CommandMap> _categories; | |||||
| public CommandService(CommandServiceConfig config) | |||||
| public event EventHandler<CommandEventArgs> Command = delegate { }; | |||||
| public event EventHandler<CommandErrorEventArgs> CommandError = delegate { }; | |||||
| private void OnCommand(CommandEventArgs args) | |||||
| => Command(this, args); | |||||
| private void OnCommandError(CommandErrorType errorType, CommandEventArgs args, Exception ex = null) | |||||
| => CommandError(this, new CommandErrorEventArgs(errorType, args, ex)); | |||||
| public CommandService(CommandServiceConfig config) | |||||
| { | { | ||||
| _config = config; | |||||
| Config = config; | |||||
| _allCommands = new List<Command>(); | _allCommands = new List<Command>(); | ||||
| _map = new CommandMap(null, "", ""); | _map = new CommandMap(null, "", ""); | ||||
| _categories = new Dictionary<string, CommandMap>(); | _categories = new Dictionary<string, CommandMap>(); | ||||
| _root = new CommandGroupBuilder(this, "", null); | |||||
| Root = new CommandGroupBuilder(this, "", null); | |||||
| } | } | ||||
| void IService.Install(DiscordClient client) | void IService.Install(DiscordClient client) | ||||
| { | { | ||||
| _client = client; | |||||
| _config.Lock(); | |||||
| Client = client; | |||||
| Config.Lock(); | |||||
| if (_config.HelpMode != HelpMode.Disable) | |||||
| if (Config.HelpMode != HelpMode.Disable) | |||||
| { | { | ||||
| CreateCommand("help") | CreateCommand("help") | ||||
| .Parameter("command", ParameterType.Multiple) | .Parameter("command", ParameterType.Multiple) | ||||
| @@ -49,7 +52,7 @@ namespace Discord.Commands | |||||
| .Description("Returns information about commands.") | .Description("Returns information about commands.") | ||||
| .Do(async e => | .Do(async e => | ||||
| { | { | ||||
| Channel replyChannel = _config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreatePMChannel().ConfigureAwait(false); | |||||
| Channel replyChannel = Config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreatePMChannel().ConfigureAwait(false); | |||||
| if (e.Args.Length > 0) //Show command help | if (e.Args.Length > 0) //Show command help | ||||
| { | { | ||||
| var map = _map.GetItem(string.Join(" ", e.Args)); | var map = _map.GetItem(string.Join(" ", e.Args)); | ||||
| @@ -66,13 +69,13 @@ namespace Discord.Commands | |||||
| client.MessageReceived += async (s, e) => | client.MessageReceived += async (s, e) => | ||||
| { | { | ||||
| if (_allCommands.Count == 0) return; | if (_allCommands.Count == 0) return; | ||||
| if (e.Message.User == null || e.Message.User.Id == _client.CurrentUser.Id) return; | |||||
| if (e.Message.User == null || e.Message.User.Id == Client.CurrentUser.Id) return; | |||||
| string msg = e.Message.RawText; | string msg = e.Message.RawText; | ||||
| if (msg.Length == 0) return; | if (msg.Length == 0) return; | ||||
| //Check for command char if one is provided | //Check for command char if one is provided | ||||
| var chars = _config.CommandChars; | |||||
| var chars = Config.CommandChars; | |||||
| if (chars.Length > 0) | if (chars.Length > 0) | ||||
| { | { | ||||
| if (!chars.Contains(msg[0])) | if (!chars.Contains(msg[0])) | ||||
| @@ -87,7 +90,7 @@ namespace Discord.Commands | |||||
| if (commands == null) | if (commands == null) | ||||
| { | { | ||||
| CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null); | CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null); | ||||
| RaiseCommandError(CommandErrorType.UnknownCommand, errorArgs); | |||||
| OnCommandError(CommandErrorType.UnknownCommand, errorArgs); | |||||
| return; | return; | ||||
| } | } | ||||
| else | else | ||||
| @@ -104,7 +107,7 @@ namespace Discord.Commands | |||||
| else | else | ||||
| { | { | ||||
| var errorArgs = new CommandEventArgs(e.Message, command, null); | var errorArgs = new CommandEventArgs(e.Message, command, null); | ||||
| RaiseCommandError(error.Value, errorArgs); | |||||
| OnCommandError(error.Value, errorArgs); | |||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| @@ -115,24 +118,24 @@ namespace Discord.Commands | |||||
| string errorText; | string errorText; | ||||
| if (!command.CanRun(eventArgs.User, eventArgs.Channel, out errorText)) | if (!command.CanRun(eventArgs.User, eventArgs.Channel, out errorText)) | ||||
| { | { | ||||
| RaiseCommandError(CommandErrorType.BadPermissions, eventArgs, errorText != null ? new Exception(errorText) : null); | |||||
| OnCommandError(CommandErrorType.BadPermissions, eventArgs, errorText != null ? new Exception(errorText) : null); | |||||
| return; | return; | ||||
| } | } | ||||
| // Run the command | // Run the command | ||||
| try | try | ||||
| { | { | ||||
| RaiseRanCommand(eventArgs); | |||||
| OnCommand(eventArgs); | |||||
| await command.Run(eventArgs).ConfigureAwait(false); | await command.Run(eventArgs).ConfigureAwait(false); | ||||
| } | } | ||||
| catch (Exception ex) | catch (Exception ex) | ||||
| { | { | ||||
| RaiseCommandError(CommandErrorType.Exception, eventArgs, ex); | |||||
| OnCommandError(CommandErrorType.Exception, eventArgs, ex); | |||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| var errorArgs2 = new CommandEventArgs(e.Message, null, null); | var errorArgs2 = new CommandEventArgs(e.Message, null, null); | ||||
| RaiseCommandError(CommandErrorType.BadArgCount, errorArgs2); | |||||
| OnCommandError(CommandErrorType.BadArgCount, errorArgs2); | |||||
| } | } | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -184,7 +187,7 @@ namespace Discord.Commands | |||||
| { | { | ||||
| output.Append("\n\n"); | output.Append("\n\n"); | ||||
| var chars = _config.CommandChars; | |||||
| var chars = Config.CommandChars; | |||||
| if (chars.Length > 0) | if (chars.Length > 0) | ||||
| { | { | ||||
| if (chars.Length == 1) | if (chars.Length == 1) | ||||
| @@ -294,8 +297,8 @@ namespace Discord.Commands | |||||
| output.AppendLine($"Aliases: `" + string.Join("`, `", command.Aliases) + '`'); | output.AppendLine($"Aliases: `" + string.Join("`, `", command.Aliases) + '`'); | ||||
| } | } | ||||
| public void CreateGroup(string cmd, Action<CommandGroupBuilder> config = null) => _root.CreateGroup(cmd, config); | |||||
| public CommandBuilder CreateCommand(string cmd) => _root.CreateCommand(cmd); | |||||
| public void CreateGroup(string cmd, Action<CommandGroupBuilder> config = null) => Root.CreateGroup(cmd, config); | |||||
| public CommandBuilder CreateCommand(string cmd) => Root.CreateCommand(cmd); | |||||
| internal void AddCommand(Command command) | internal void AddCommand(Command command) | ||||
| { | { | ||||
| @@ -6,7 +6,7 @@ using System.Linq; | |||||
| namespace Discord.Modules | namespace Discord.Modules | ||||
| { | { | ||||
| public class ModuleManager | |||||
| public sealed class ModuleManager | |||||
| { | { | ||||
| public event EventHandler<ServerEventArgs> ServerEnabled = delegate { }; | public event EventHandler<ServerEventArgs> ServerEnabled = delegate { }; | ||||
| public event EventHandler<ServerEventArgs> ServerDisabled = delegate { }; | public event EventHandler<ServerEventArgs> ServerDisabled = delegate { }; | ||||
| @@ -5,21 +5,19 @@ namespace Discord.Modules | |||||
| { | { | ||||
| public class ModuleService : IService | public class ModuleService : IService | ||||
| { | { | ||||
| private DiscordClient _client; | |||||
| //ModuleServiceConfig Config { get; } | |||||
| public DiscordClient Client { get; private set; } | |||||
| public IEnumerable<ModuleManager> Modules => _modules.Values; | public IEnumerable<ModuleManager> Modules => _modules.Values; | ||||
| private readonly Dictionary<IModule, ModuleManager> _modules; | private readonly Dictionary<IModule, ModuleManager> _modules; | ||||
| public ModuleService(/*ModuleServiceConfig config*/) | |||||
| public ModuleService() | |||||
| { | { | ||||
| //Config = config; | |||||
| _modules = new Dictionary<IModule, ModuleManager>(); | _modules = new Dictionary<IModule, ModuleManager>(); | ||||
| } | } | ||||
| void IService.Install(DiscordClient client) | void IService.Install(DiscordClient client) | ||||
| { | { | ||||
| _client = client; | |||||
| Client = client; | |||||
| } | } | ||||
| public void Install<T>(T module, string name, FilterType type) | public void Install<T>(T module, string name, FilterType type) | ||||
| @@ -27,10 +25,12 @@ namespace Discord.Modules | |||||
| { | { | ||||
| if (module == null) throw new ArgumentNullException(nameof(module)); | if (module == null) throw new ArgumentNullException(nameof(module)); | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| if (_client == null) throw new InvalidOperationException("Service needs to be added to a DiscordClient before modules can be installed."); | |||||
| if (_modules.ContainsKey(module)) throw new InvalidOperationException("This module has already been added."); | |||||
| if (Client == null) | |||||
| throw new InvalidOperationException("Service needs to be added to a DiscordClient before modules can be installed."); | |||||
| if (_modules.ContainsKey(module)) | |||||
| throw new InvalidOperationException("This module has already been added."); | |||||
| var manager = new ModuleManager(_client, name, type); | |||||
| var manager = new ModuleManager(Client, name, type); | |||||
| _modules.Add(module, manager); | _modules.Add(module, manager); | ||||
| module.Install(manager); | module.Install(manager); | ||||
| } | } | ||||
| @@ -391,15 +391,24 @@ | |||||
| <Compile Include="..\Discord.Net\API\Status\Rest\Upcoming.cs"> | <Compile Include="..\Discord.Net\API\Status\Rest\Upcoming.cs"> | ||||
| <Link>API\Status\Rest\Upcoming.cs</Link> | <Link>API\Status\Rest\Upcoming.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\ChannelEventArgs.cs"> | |||||
| <Link>ChannelEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\ChannelUserEventArgs.cs"> | |||||
| <Link>ChannelUserEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Config.cs"> | |||||
| <Link>Config.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\DisconnectedEventArgs.cs"> | |||||
| <Link>DisconnectedEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\DiscordClient.cs"> | <Compile Include="..\Discord.Net\DiscordClient.cs"> | ||||
| <Link>DiscordClient.cs</Link> | <Link>DiscordClient.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\DiscordClient.Events.cs"> | <Compile Include="..\Discord.Net\DiscordClient.Events.cs"> | ||||
| <Link>DiscordClient.Events.cs</Link> | <Link>DiscordClient.Events.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\DiscordClient.Obsolete.cs"> | |||||
| <Link>DiscordClient.Obsolete.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\DiscordConfig.cs"> | <Compile Include="..\Discord.Net\DiscordConfig.cs"> | ||||
| <Link>DiscordConfig.cs</Link> | <Link>DiscordConfig.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -421,33 +430,6 @@ | |||||
| <Compile Include="..\Discord.Net\Enums\UserStatus.cs"> | <Compile Include="..\Discord.Net\Enums\UserStatus.cs"> | ||||
| <Link>Enums\UserStatus.cs</Link> | <Link>Enums\UserStatus.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Events\ChannelEventArgs.cs"> | |||||
| <Link>Events\ChannelEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\ChannelUserEventArgs.cs"> | |||||
| <Link>Events\ChannelUserEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\DisconnectedEventArgs.cs"> | |||||
| <Link>Events\DisconnectedEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\LogMessageEventArgs.cs"> | |||||
| <Link>Events\LogMessageEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\MessageEventArgs.cs"> | |||||
| <Link>Events\MessageEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\ProfileEventArgs.cs"> | |||||
| <Link>Events\ProfileEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\RoleEventArgs.cs"> | |||||
| <Link>Events\RoleEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\ServerEventArgs.cs"> | |||||
| <Link>Events\ServerEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Events\UserEventArgs.cs"> | |||||
| <Link>Events\UserEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Extensions.cs"> | <Compile Include="..\Discord.Net\Extensions.cs"> | ||||
| <Link>Extensions.cs</Link> | <Link>Extensions.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -457,12 +439,21 @@ | |||||
| <Compile Include="..\Discord.Net\IService.cs"> | <Compile Include="..\Discord.Net\IService.cs"> | ||||
| <Link>IService.cs</Link> | <Link>IService.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Legacy.cs"> | |||||
| <Link>Legacy.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Logging\Logger.cs"> | <Compile Include="..\Discord.Net\Logging\Logger.cs"> | ||||
| <Link>Logging\Logger.cs</Link> | <Link>Logging\Logger.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Logging\LogManager.cs"> | <Compile Include="..\Discord.Net\Logging\LogManager.cs"> | ||||
| <Link>Logging\LogManager.cs</Link> | <Link>Logging\LogManager.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\LogMessageEventArgs.cs"> | |||||
| <Link>LogMessageEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\MessageEventArgs.cs"> | |||||
| <Link>MessageEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\MessageQueue.cs"> | <Compile Include="..\Discord.Net\MessageQueue.cs"> | ||||
| <Link>MessageQueue.cs</Link> | <Link>MessageQueue.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -502,6 +493,9 @@ | |||||
| <Compile Include="..\Discord.Net\Net\Rest\IRestEngine.cs"> | <Compile Include="..\Discord.Net\Net\Rest\IRestEngine.cs"> | ||||
| <Link>Net\Rest\IRestEngine.cs</Link> | <Link>Net\Rest\IRestEngine.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Net\Rest\RequestEventArgs.cs"> | |||||
| <Link>Net\Rest\RequestEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Net\Rest\RestClient.cs"> | <Compile Include="..\Discord.Net\Net\Rest\RestClient.cs"> | ||||
| <Link>Net\Rest\RestClient.cs</Link> | <Link>Net\Rest\RestClient.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -514,6 +508,9 @@ | |||||
| <Compile Include="..\Discord.Net\Net\WebSocketException.cs"> | <Compile Include="..\Discord.Net\Net\WebSocketException.cs"> | ||||
| <Link>Net\WebSockets\WebSocketException.cs</Link> | <Link>Net\WebSockets\WebSocketException.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Net\WebSockets\BinaryMessageEventArgs.cs"> | |||||
| <Link>Net\WebSockets\BinaryMessageEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Net\WebSockets\BuiltInEngine.cs"> | <Compile Include="..\Discord.Net\Net\WebSockets\BuiltInEngine.cs"> | ||||
| <Link>Net\WebSockets\BuiltInEngine.cs</Link> | <Link>Net\WebSockets\BuiltInEngine.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -523,30 +520,39 @@ | |||||
| <Compile Include="..\Discord.Net\Net\WebSockets\IWebSocketEngine.cs"> | <Compile Include="..\Discord.Net\Net\WebSockets\IWebSocketEngine.cs"> | ||||
| <Link>Net\WebSockets\IWebSocketEngine.cs</Link> | <Link>Net\WebSockets\IWebSocketEngine.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocket.BuiltIn.cs"> | |||||
| <Link>Net\WebSockets\WebSocket.BuiltIn.cs</Link> | |||||
| <Compile Include="..\Discord.Net\Net\WebSockets\TextMessageEventArgs.cs"> | |||||
| <Link>Net\WebSockets\TextMessageEventArgs.cs</Link> | |||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocket.cs"> | <Compile Include="..\Discord.Net\Net\WebSockets\WebSocket.cs"> | ||||
| <Link>Net\WebSockets\WebSocket.cs</Link> | <Link>Net\WebSockets\WebSocket.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocketSharpEngine.cs"> | |||||
| <Link>Net\WebSockets\WebSocketSharpEngine.cs</Link> | |||||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocketEventEventArgs.cs"> | |||||
| <Link>Net\WebSockets\WebSocketEventEventArgs.cs</Link> | |||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Net\WebSockets\WS4NetEngine.cs"> | <Compile Include="..\Discord.Net\Net\WebSockets\WS4NetEngine.cs"> | ||||
| <Link>Net\WebSockets\WS4NetEngine.cs</Link> | <Link>Net\WebSockets\WS4NetEngine.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Reference.cs"> | |||||
| <Link>Reference.cs</Link> | |||||
| <Compile Include="..\Discord.Net\ProfileEventArgs.cs"> | |||||
| <Link>ProfileEventArgs.cs</Link> | |||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\RelativeDirection.cs"> | <Compile Include="..\Discord.Net\RelativeDirection.cs"> | ||||
| <Link>RelativeDirection.cs</Link> | <Link>RelativeDirection.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\RoleEventArgs.cs"> | |||||
| <Link>RoleEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\ServerEventArgs.cs"> | |||||
| <Link>ServerEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\ServiceManager.cs"> | <Compile Include="..\Discord.Net\ServiceManager.cs"> | ||||
| <Link>ServiceManager.cs</Link> | <Link>ServiceManager.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\TaskManager.cs"> | <Compile Include="..\Discord.Net\TaskManager.cs"> | ||||
| <Link>TaskManager.cs</Link> | <Link>TaskManager.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\UserEventArgs.cs"> | |||||
| <Link>UserEventArgs.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="Properties\AssemblyInfo.cs" /> | <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| @@ -4,7 +4,7 @@ using System.Collections.Generic; | |||||
| namespace Discord.API.Client.GatewaySocket | namespace Discord.API.Client.GatewaySocket | ||||
| { | { | ||||
| [JsonObject(MemberSerialization.OptIn)] | [JsonObject(MemberSerialization.OptIn)] | ||||
| internal sealed class IdentifyCommand : IWebSocketMessage | |||||
| public sealed class IdentifyCommand : IWebSocketMessage | |||||
| { | { | ||||
| int IWebSocketMessage.OpCode => (int)OpCodes.Identify; | int IWebSocketMessage.OpCode => (int)OpCodes.Identify; | ||||
| object IWebSocketMessage.Payload => this; | object IWebSocketMessage.Payload => this; | ||||
| @@ -4,7 +4,7 @@ using Newtonsoft.Json; | |||||
| namespace Discord.API.Client.GatewaySocket | namespace Discord.API.Client.GatewaySocket | ||||
| { | { | ||||
| [JsonObject(MemberSerialization.OptIn)] | [JsonObject(MemberSerialization.OptIn)] | ||||
| internal sealed class RequestMembersCommand : IWebSocketMessage | |||||
| public sealed class RequestMembersCommand : IWebSocketMessage | |||||
| { | { | ||||
| int IWebSocketMessage.OpCode => (int)OpCodes.RequestGuildMembers; | int IWebSocketMessage.OpCode => (int)OpCodes.RequestGuildMembers; | ||||
| object IWebSocketMessage.Payload => this; | object IWebSocketMessage.Payload => this; | ||||
| @@ -1,9 +1,4 @@ | |||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Client.GatewaySocket | |||||
| namespace Discord.API.Client.GatewaySocket | |||||
| { | { | ||||
| public sealed class GuildBanAddEvent : MemberReference | |||||
| { | |||||
| } | |||||
| public sealed class GuildBanAddEvent : MemberReference { } | |||||
| } | } | ||||
| @@ -1,9 +1,4 @@ | |||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Client.GatewaySocket | |||||
| namespace Discord.API.Client.GatewaySocket | |||||
| { | { | ||||
| public sealed class GuildBanRemoveEvent : MemberReference | |||||
| { | |||||
| } | |||||
| public sealed class GuildBanRemoveEvent : MemberReference { } | |||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| namespace Discord.API.Client.GatewaySocket.Events | namespace Discord.API.Client.GatewaySocket.Events | ||||
| { | { | ||||
| //public sealed class GuildEmojisUpdate { } | |||||
| //public sealed class GuildEmojisUpdateEvent { } | |||||
| } | } | ||||
| @@ -8,6 +8,6 @@ namespace Discord.API.Client.GatewaySocket | |||||
| [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] | [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] | ||||
| public ulong GuildId { get; set; } | public ulong GuildId { get; set; } | ||||
| [JsonProperty("members")] | [JsonProperty("members")] | ||||
| public Member[] Members; | |||||
| public Member[] Members { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,7 +8,7 @@ namespace Discord.API.Client | |||||
| object Payload { get; } | object Payload { get; } | ||||
| bool IsPrivate { get; } | bool IsPrivate { get; } | ||||
| } | } | ||||
| public class WebSocketMessage | |||||
| public sealed class WebSocketMessage | |||||
| { | { | ||||
| [JsonProperty("op")] | [JsonProperty("op")] | ||||
| public int? Operation { get; set; } | public int? Operation { get; set; } | ||||
| @@ -4,7 +4,7 @@ using System.Collections.Generic; | |||||
| namespace Discord.API.Converters | namespace Discord.API.Converters | ||||
| { | { | ||||
| public class LongStringConverter : JsonConverter | |||||
| public sealed class LongStringConverter : JsonConverter | |||||
| { | { | ||||
| public override bool CanConvert(Type objectType) | public override bool CanConvert(Type objectType) | ||||
| => objectType == typeof(ulong); | => objectType == typeof(ulong); | ||||
| @@ -14,7 +14,7 @@ namespace Discord.API.Converters | |||||
| => writer.WriteValue(((ulong)value).ToIdString()); | => writer.WriteValue(((ulong)value).ToIdString()); | ||||
| } | } | ||||
| public class NullableLongStringConverter : JsonConverter | |||||
| public sealed class NullableLongStringConverter : JsonConverter | |||||
| { | { | ||||
| public override bool CanConvert(Type objectType) | public override bool CanConvert(Type objectType) | ||||
| => objectType == typeof(ulong?); | => objectType == typeof(ulong?); | ||||
| @@ -24,7 +24,7 @@ namespace Discord.API.Converters | |||||
| => writer.WriteValue(((ulong?)value).ToIdString()); | => writer.WriteValue(((ulong?)value).ToIdString()); | ||||
| } | } | ||||
| /*public class LongStringEnumerableConverter : JsonConverter | |||||
| /*public sealed class LongStringEnumerableConverter : JsonConverter | |||||
| { | { | ||||
| public override bool CanConvert(Type objectType) => objectType == typeof(IEnumerable<ulong>); | public override bool CanConvert(Type objectType) => objectType == typeof(IEnumerable<ulong>); | ||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | ||||
| @@ -55,7 +55,7 @@ namespace Discord.API.Converters | |||||
| } | } | ||||
| }*/ | }*/ | ||||
| internal class LongStringArrayConverter : JsonConverter | |||||
| internal sealed class LongStringArrayConverter : JsonConverter | |||||
| { | { | ||||
| public override bool CanConvert(Type objectType) => objectType == typeof(IEnumerable<ulong[]>); | public override bool CanConvert(Type objectType) => objectType == typeof(IEnumerable<ulong[]>); | ||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | ||||
| @@ -5,6 +5,7 @@ namespace Discord | |||||
| public class ChannelEventArgs : EventArgs | public class ChannelEventArgs : EventArgs | ||||
| { | { | ||||
| public Channel Channel { get; } | public Channel Channel { get; } | ||||
| public Server Server => Channel.Server; | public Server Server => Channel.Server; | ||||
| public ChannelEventArgs(Channel channel) { Channel = channel; } | public ChannelEventArgs(Channel channel) { Channel = channel; } | ||||
| @@ -0,0 +1,24 @@ | |||||
| using System; | |||||
| namespace Discord | |||||
| { | |||||
| public abstract class Config<T> | |||||
| where T : Config<T> | |||||
| { | |||||
| protected bool _isLocked; | |||||
| protected internal void Lock() { _isLocked = true; } | |||||
| protected void SetValue<U>(ref U storage, U value) | |||||
| { | |||||
| if (_isLocked) | |||||
| throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created."); | |||||
| storage = value; | |||||
| } | |||||
| public T Clone() | |||||
| { | |||||
| var config = MemberwiseClone() as T; | |||||
| config._isLocked = false; | |||||
| return config; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -13,26 +13,6 @@ namespace Discord | |||||
| Verbose = 4, | Verbose = 4, | ||||
| Debug = 5 | Debug = 5 | ||||
| } | } | ||||
| public abstract class Config<T> | |||||
| where T : Config<T> | |||||
| { | |||||
| protected bool _isLocked; | |||||
| protected internal void Lock() { _isLocked = true; } | |||||
| protected void SetValue<U>(ref U storage, U value) | |||||
| { | |||||
| if (_isLocked) | |||||
| throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created."); | |||||
| storage = value; | |||||
| } | |||||
| public T Clone() | |||||
| { | |||||
| var config = MemberwiseClone() as T; | |||||
| config._isLocked = false; | |||||
| return config; | |||||
| } | |||||
| } | |||||
| public class DiscordConfig : Config<DiscordConfig> | public class DiscordConfig : Config<DiscordConfig> | ||||
| { | { | ||||
| @@ -1,6 +1,6 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class ChannelType : StringEnum | |||||
| public sealed class ChannelType : StringEnum | |||||
| { | { | ||||
| /// <summary> A text-only channel. </summary> | /// <summary> A text-only channel. </summary> | ||||
| public static ChannelType Text { get; } = new ChannelType("text"); | public static ChannelType Text { get; } = new ChannelType("text"); | ||||
| @@ -1,6 +1,6 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class PermissionTarget : StringEnum | |||||
| public sealed class PermissionTarget : StringEnum | |||||
| { | { | ||||
| /// <summary> A text-only channel. </summary> | /// <summary> A text-only channel. </summary> | ||||
| public static PermissionTarget Role { get; } = new PermissionTarget("role"); | public static PermissionTarget Role { get; } = new PermissionTarget("role"); | ||||
| @@ -1,6 +1,6 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class UserStatus : StringEnum | |||||
| public sealed class UserStatus : StringEnum | |||||
| { | { | ||||
| /// <summary> User is currently online and active. </summary> | /// <summary> User is currently online and active. </summary> | ||||
| public static UserStatus Online { get; } = new UserStatus("online"); | public static UserStatus Online { get; } = new UserStatus("online"); | ||||
| @@ -3,7 +3,7 @@ using System.Collections.Generic; | |||||
| using System.IO; | using System.IO; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Legacy | |||||
| namespace Discord | |||||
| { | { | ||||
| public static class Mention | public static class Mention | ||||
| { | { | ||||
| @@ -31,19 +31,19 @@ namespace Discord.Legacy | |||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.FindChannels(name, type, exactMatch); | return server.FindChannels(name, type, exactMatch); | ||||
| } | } | ||||
| [Obsolete("Use Server.CreateChannel")] | [Obsolete("Use Server.CreateChannel")] | ||||
| public static Task<Channel> CreateChannel(this DiscordClient client, Server server, string name, ChannelType type) | public static Task<Channel> CreateChannel(this DiscordClient client, Server server, string name, ChannelType type) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.CreateChannel(name, type); | return server.CreateChannel(name, type); | ||||
| } | |||||
| } | |||||
| [Obsolete("Use User.CreateChannel")] | [Obsolete("Use User.CreateChannel")] | ||||
| public static Task<Channel> CreatePMChannel(this DiscordClient client, User user) | public static Task<Channel> CreatePMChannel(this DiscordClient client, User user) | ||||
| { | { | ||||
| if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
| return user.CreatePMChannel(); | return user.CreatePMChannel(); | ||||
| } | |||||
| } | |||||
| [Obsolete("Use Channel.Edit")] | [Obsolete("Use Channel.Edit")] | ||||
| public static Task EditChannel(this DiscordClient client, Channel channel, string name = null, string topic = null, int? position = null) | public static Task EditChannel(this DiscordClient client, Channel channel, string name = null, string topic = null, int? position = null) | ||||
| { | { | ||||
| @@ -62,15 +62,15 @@ namespace Discord.Legacy | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.ReorderChannels(channels, after); | return server.ReorderChannels(channels, after); | ||||
| } | |||||
| } | |||||
| [Obsolete("Use Server.GetInvites")] | [Obsolete("Use Server.GetInvites")] | ||||
| public static Task<IEnumerable<Invite>> GetInvites(this DiscordClient client, Server server) | public static Task<IEnumerable<Invite>> GetInvites(this DiscordClient client, Server server) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.GetInvites(); | return server.GetInvites(); | ||||
| } | } | ||||
| [Obsolete("Use Server.CreateInvite")] | [Obsolete("Use Server.CreateInvite")] | ||||
| public static Task<Invite> CreateInvite(this DiscordClient client, Server server, int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) | public static Task<Invite> CreateInvite(this DiscordClient client, Server server, int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) | ||||
| { | { | ||||
| @@ -83,20 +83,20 @@ namespace Discord.Legacy | |||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| return channel.CreateInvite(maxAge, maxUses, tempMembership, withXkcd); | return channel.CreateInvite(maxAge, maxUses, tempMembership, withXkcd); | ||||
| } | } | ||||
| [Obsolete("Use Invite.Delete")] | [Obsolete("Use Invite.Delete")] | ||||
| public static Task DeleteInvite(this DiscordClient client, Invite invite) | public static Task DeleteInvite(this DiscordClient client, Invite invite) | ||||
| { | { | ||||
| if (invite == null) throw new ArgumentNullException(nameof(invite)); | if (invite == null) throw new ArgumentNullException(nameof(invite)); | ||||
| return invite.Delete(); | return invite.Delete(); | ||||
| } | |||||
| } | |||||
| [Obsolete("Use Invite.Accept")] | [Obsolete("Use Invite.Accept")] | ||||
| public static Task AcceptInvite(this DiscordClient client, Invite invite) | public static Task AcceptInvite(this DiscordClient client, Invite invite) | ||||
| { | { | ||||
| if (invite == null) throw new ArgumentNullException(nameof(invite)); | if (invite == null) throw new ArgumentNullException(nameof(invite)); | ||||
| return invite.Accept(); | return invite.Accept(); | ||||
| } | } | ||||
| [Obsolete("Use Channel.SendMessage")] | [Obsolete("Use Channel.SendMessage")] | ||||
| public static Task<Message> SendMessage(this DiscordClient client, Channel channel, string text) | public static Task<Message> SendMessage(this DiscordClient client, Channel channel, string text) | ||||
| { | { | ||||
| @@ -139,14 +139,14 @@ namespace Discord.Legacy | |||||
| if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
| return user.SendFile(filename, stream); | return user.SendFile(filename, stream); | ||||
| } | } | ||||
| [Obsolete("Use Message.Edit")] | [Obsolete("Use Message.Edit")] | ||||
| public static Task EditMessage(this DiscordClient client, Message message, string text) | public static Task EditMessage(this DiscordClient client, Message message, string text) | ||||
| { | { | ||||
| if (message == null) throw new ArgumentNullException(nameof(message)); | if (message == null) throw new ArgumentNullException(nameof(message)); | ||||
| return message.Edit(text); | return message.Edit(text); | ||||
| } | } | ||||
| [Obsolete("Use Message.Delete")] | [Obsolete("Use Message.Delete")] | ||||
| public static Task DeleteMessage(this DiscordClient client, Message message) | public static Task DeleteMessage(this DiscordClient client, Message message) | ||||
| { | { | ||||
| @@ -161,14 +161,14 @@ namespace Discord.Legacy | |||||
| foreach (var message in messages) | foreach (var message in messages) | ||||
| await message.Delete().ConfigureAwait(false); | await message.Delete().ConfigureAwait(false); | ||||
| } | } | ||||
| [Obsolete("Use Channel.DownloadMessages")] | [Obsolete("Use Channel.DownloadMessages")] | ||||
| public static Task<Message[]> DownloadMessages(this DiscordClient client, Channel channel, int limit = 100, ulong? relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) | public static Task<Message[]> DownloadMessages(this DiscordClient client, Channel channel, int limit = 100, ulong? relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) | ||||
| { | { | ||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| return channel.DownloadMessages(limit, relativeMessageId, relativeDir, useCache); | return channel.DownloadMessages(limit, relativeMessageId, relativeDir, useCache); | ||||
| } | } | ||||
| [Obsolete("Use Message.Acknowledge")] | [Obsolete("Use Message.Acknowledge")] | ||||
| public static Task AckMessage(this DiscordClient client, Message message) | public static Task AckMessage(this DiscordClient client, Message message) | ||||
| { | { | ||||
| @@ -212,7 +212,7 @@ namespace Discord.Legacy | |||||
| return JsonConvert.SerializeObject(channel.Messages); | return JsonConvert.SerializeObject(channel.Messages); | ||||
| }*/ | }*/ | ||||
| [Obsolete("Use Server.GetUser")] | [Obsolete("Use Server.GetUser")] | ||||
| public static User GetUser(this DiscordClient client, Server server, ulong userId) | public static User GetUser(this DiscordClient client, Server server, ulong userId) | ||||
| { | { | ||||
| @@ -225,7 +225,7 @@ namespace Discord.Legacy | |||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.GetUser(username, discriminator); | return server.GetUser(username, discriminator); | ||||
| } | } | ||||
| [Obsolete("Use Server.FindUsers")] | [Obsolete("Use Server.FindUsers")] | ||||
| public static IEnumerable<User> FindUsers(this DiscordClient client, Server server, string name, bool exactMatch = false) | public static IEnumerable<User> FindUsers(this DiscordClient client, Server server, string name, bool exactMatch = false) | ||||
| { | { | ||||
| @@ -287,20 +287,20 @@ namespace Discord.Legacy | |||||
| string username = null, string email = null, string password = null, | string username = null, string email = null, string password = null, | ||||
| Stream avatar = null, ImageType avatarType = ImageType.Png) | Stream avatar = null, ImageType avatarType = ImageType.Png) | ||||
| => client.CurrentUser.Edit(currentPassword, username, email, password, avatar, avatarType); | => client.CurrentUser.Edit(currentPassword, username, email, password, avatar, avatarType); | ||||
| [Obsolete("Use Server.GetRole")] | [Obsolete("Use Server.GetRole")] | ||||
| public static Role GetRole(this DiscordClient client, Server server, ulong id) | public static Role GetRole(this DiscordClient client, Server server, ulong id) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.GetRole(id); | return server.GetRole(id); | ||||
| } | |||||
| } | |||||
| [Obsolete("Use Server.FindRoles")] | [Obsolete("Use Server.FindRoles")] | ||||
| public static IEnumerable<Role> FindRoles(this DiscordClient client, Server server, string name) | public static IEnumerable<Role> FindRoles(this DiscordClient client, Server server, string name) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.FindRoles(name); | return server.FindRoles(name); | ||||
| } | } | ||||
| [Obsolete("Use Server.CreateRole")] | [Obsolete("Use Server.CreateRole")] | ||||
| public static Task<Role> CreateRole(this DiscordClient client, Server server, string name, ServerPermissions permissions = null, Color color = null, bool isHoisted = false) | public static Task<Role> CreateRole(this DiscordClient client, Server server, string name, ServerPermissions permissions = null, Color color = null, bool isHoisted = false) | ||||
| { | { | ||||
| @@ -320,21 +320,21 @@ namespace Discord.Legacy | |||||
| if (role == null) throw new ArgumentNullException(nameof(role)); | if (role == null) throw new ArgumentNullException(nameof(role)); | ||||
| return role.Delete(); | return role.Delete(); | ||||
| } | } | ||||
| [Obsolete("Use Server.ReorderRoles")] | [Obsolete("Use Server.ReorderRoles")] | ||||
| public static Task ReorderRoles(this DiscordClient client, Server server, IEnumerable<Role> roles, Role after = null) | public static Task ReorderRoles(this DiscordClient client, Server server, IEnumerable<Role> roles, Role after = null) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.ReorderRoles(roles, after); | return server.ReorderRoles(roles, after); | ||||
| } | } | ||||
| [Obsolete("Use Server.Edit")] | [Obsolete("Use Server.Edit")] | ||||
| public static Task EditServer(this DiscordClient client, Server server, string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png) | public static Task EditServer(this DiscordClient client, Server server, string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| return server.Edit(name, region, icon, iconType); | return server.Edit(name, region, icon, iconType); | ||||
| } | } | ||||
| [Obsolete("Use Server.Leave")] | [Obsolete("Use Server.Leave")] | ||||
| public static Task LeaveServer(this DiscordClient client, Server server) | public static Task LeaveServer(this DiscordClient client, Server server) | ||||
| { | { | ||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class LogMessageEventArgs : EventArgs | |||||
| public class LogMessageEventArgs : EventArgs | |||||
| { | { | ||||
| public LogSeverity Severity { get; } | public LogSeverity Severity { get; } | ||||
| public string Source { get; } | public string Source { get; } | ||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord.Logging | namespace Discord.Logging | ||||
| { | { | ||||
| public class LogManager | |||||
| public sealed class LogManager | |||||
| { | { | ||||
| private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord.Logging | namespace Discord.Logging | ||||
| { | { | ||||
| public class Logger | |||||
| public sealed class Logger | |||||
| { | { | ||||
| private readonly LogManager _manager; | private readonly LogManager _manager; | ||||
| @@ -5,6 +5,7 @@ namespace Discord | |||||
| public class MessageEventArgs : EventArgs | public class MessageEventArgs : EventArgs | ||||
| { | { | ||||
| public Message Message { get; } | public Message Message { get; } | ||||
| public User User => Message.User; | public User User => Message.User; | ||||
| public Channel Channel => Message.Channel; | public Channel Channel => Message.Channel; | ||||
| public Server Server => Message.Server; | public Server Server => Message.Server; | ||||
| @@ -9,9 +9,9 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Net | namespace Discord.Net | ||||
| { | { | ||||
| /// <summary> Manages an outgoing message queue for DiscordClient. </summary> | /// <summary> Manages an outgoing message queue for DiscordClient. </summary> | ||||
| public class MessageQueue | |||||
| public sealed class MessageQueue | |||||
| { | { | ||||
| private class MessageQueueItem | |||||
| private struct MessageQueueItem | |||||
| { | { | ||||
| public readonly ulong Id, ChannelId; | public readonly ulong Id, ChannelId; | ||||
| public readonly string Text; | public readonly string Text; | ||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class Color | |||||
| public sealed class Color | |||||
| { | { | ||||
| public static readonly Color Default = PresetColor(0); | public static readonly Color Default = PresetColor(0); | ||||
| @@ -65,8 +65,8 @@ namespace Discord | |||||
| //Bypasses isLocked for API changes. | //Bypasses isLocked for API changes. | ||||
| _rawValue = rawValue; | _rawValue = rawValue; | ||||
| } | } | ||||
| protected byte GetByte(int pos) => (byte)((_rawValue >> (8 * (pos - 1))) & 0xFF); | |||||
| protected void SetByte(int pos, byte value) | |||||
| private byte GetByte(int pos) => (byte)((_rawValue >> (8 * (pos - 1))) & 0xFF); | |||||
| private void SetByte(int pos, byte value) | |||||
| { | { | ||||
| if (_isLocked) | if (_isLocked) | ||||
| throw new InvalidOperationException("Unable to edit cached colors directly, use Copy() to make an editable copy."); | throw new InvalidOperationException("Unable to edit cached colors directly, use Copy() to make an editable copy."); | ||||
| @@ -9,7 +9,7 @@ using APIMember = Discord.API.Client.Member; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class User | |||||
| public sealed class User | |||||
| { | { | ||||
| internal static string GetAvatarUrl(ulong userId, string avatarId) | internal static string GetAvatarUrl(ulong userId, string avatarId) | ||||
| => avatarId != null ? $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.jpg" : null; | => avatarId != null ? $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.jpg" : null; | ||||
| @@ -7,7 +7,7 @@ namespace Discord.Net | |||||
| #if NET46 | #if NET46 | ||||
| [Serializable] | [Serializable] | ||||
| #endif | #endif | ||||
| public class HttpException : Exception | |||||
| public sealed class HttpException : Exception | |||||
| { | { | ||||
| public HttpStatusCode StatusCode { get; } | public HttpStatusCode StatusCode { get; } | ||||
| @@ -0,0 +1,20 @@ | |||||
| using System; | |||||
| namespace Discord.Net.Rest | |||||
| { | |||||
| public class RequestEventArgs : EventArgs | |||||
| { | |||||
| public string Method { get; } | |||||
| public string Path { get; } | |||||
| public string Payload { get; } | |||||
| public double ElapsedMilliseconds { get; } | |||||
| public RequestEventArgs(string method, string path, string payload, double milliseconds) | |||||
| { | |||||
| Method = method; | |||||
| Path = path; | |||||
| Payload = payload; | |||||
| ElapsedMilliseconds = milliseconds; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -8,21 +8,6 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Net.Rest | namespace Discord.Net.Rest | ||||
| { | { | ||||
| public class RequestEventArgs : EventArgs | |||||
| { | |||||
| public string Method { get; } | |||||
| public string Path { get; } | |||||
| public string Payload { get; } | |||||
| public double ElapsedMilliseconds { get; } | |||||
| public RequestEventArgs(string method, string path, string payload, double milliseconds) | |||||
| { | |||||
| Method = method; | |||||
| Path = path; | |||||
| Payload = payload; | |||||
| ElapsedMilliseconds = milliseconds; | |||||
| } | |||||
| } | |||||
| public sealed partial class RestClient | public sealed partial class RestClient | ||||
| { | { | ||||
| private readonly DiscordConfig _config; | private readonly DiscordConfig _config; | ||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord.Net | namespace Discord.Net | ||||
| { | { | ||||
| public class WebSocketException : Exception | |||||
| public sealed class WebSocketException : Exception | |||||
| { | { | ||||
| public int Code { get; } | public int Code { get; } | ||||
| public string Reason { get; } | public string Reason { get; } | ||||
| @@ -0,0 +1,11 @@ | |||||
| using System; | |||||
| namespace Discord.Net.WebSockets | |||||
| { | |||||
| public class BinaryMessageEventArgs : EventArgs | |||||
| { | |||||
| public byte[] Data { get; } | |||||
| public BinaryMessageEventArgs(byte[] data) { Data = data; } | |||||
| } | |||||
| } | |||||
| @@ -12,7 +12,7 @@ using WebSocketClient = System.Net.WebSockets.ClientWebSocket; | |||||
| namespace Discord.Net.WebSockets | namespace Discord.Net.WebSockets | ||||
| { | { | ||||
| internal class BuiltInEngine : IWebSocketEngine | |||||
| internal sealed class BuiltInEngine : IWebSocketEngine | |||||
| { | { | ||||
| private const int ReceiveChunkSize = 12 * 1024; //12KB | private const int ReceiveChunkSize = 12 * 1024; //12KB | ||||
| private const int SendChunkSize = 4 * 1024; //4KB | private const int SendChunkSize = 4 * 1024; //4KB | ||||
| @@ -23,12 +23,12 @@ namespace Discord.Net.WebSockets | |||||
| private WebSocketClient _webSocket; | private WebSocketClient _webSocket; | ||||
| private Task _tempTask; | private Task _tempTask; | ||||
| public event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage = delegate { }; | |||||
| public event EventHandler<WebSocketTextMessageEventArgs> TextMessage = delegate { }; | |||||
| public event EventHandler<BinaryMessageEventArgs> BinaryMessage = delegate { }; | |||||
| public event EventHandler<TextMessageEventArgs> TextMessage = delegate { }; | |||||
| private void OnBinaryMessage(byte[] data) | private void OnBinaryMessage(byte[] data) | ||||
| => BinaryMessage(this, new WebSocketBinaryMessageEventArgs(data)); | |||||
| => BinaryMessage(this, new BinaryMessageEventArgs(data)); | |||||
| private void OnTextMessage(string msg) | private void OnTextMessage(string msg) | ||||
| => TextMessage(this, new WebSocketTextMessageEventArgs(msg)); | |||||
| => TextMessage(this, new TextMessageEventArgs(msg)); | |||||
| internal BuiltInEngine(DiscordConfig config) | internal BuiltInEngine(DiscordConfig config) | ||||
| { | { | ||||
| @@ -10,18 +10,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Net.WebSockets | namespace Discord.Net.WebSockets | ||||
| { | { | ||||
| public sealed class WebSocketEventEventArgs : EventArgs | |||||
| { | |||||
| public readonly string Type; | |||||
| public readonly JToken Payload; | |||||
| internal WebSocketEventEventArgs(string type, JToken data) | |||||
| { | |||||
| Type = type; | |||||
| Payload = data; | |||||
| } | |||||
| } | |||||
| public partial class GatewaySocket : WebSocket | |||||
| public sealed class GatewaySocket : WebSocket | |||||
| { | { | ||||
| private uint _lastSequence; | private uint _lastSequence; | ||||
| private string _sessionId; | private string _sessionId; | ||||
| @@ -5,21 +5,10 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Net.WebSockets | namespace Discord.Net.WebSockets | ||||
| { | { | ||||
| public class WebSocketBinaryMessageEventArgs : EventArgs | |||||
| { | |||||
| public readonly byte[] Data; | |||||
| public WebSocketBinaryMessageEventArgs(byte[] data) { Data = data; } | |||||
| } | |||||
| public class WebSocketTextMessageEventArgs : EventArgs | |||||
| { | |||||
| public readonly string Message; | |||||
| public WebSocketTextMessageEventArgs(string msg) { Message = msg; } | |||||
| } | |||||
| public interface IWebSocketEngine | public interface IWebSocketEngine | ||||
| { | { | ||||
| event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage; | |||||
| event EventHandler<WebSocketTextMessageEventArgs> TextMessage; | |||||
| event EventHandler<BinaryMessageEventArgs> BinaryMessage; | |||||
| event EventHandler<TextMessageEventArgs> TextMessage; | |||||
| Task Connect(string host, CancellationToken cancelToken); | Task Connect(string host, CancellationToken cancelToken); | ||||
| Task Disconnect(); | Task Disconnect(); | ||||
| @@ -0,0 +1,11 @@ | |||||
| using System; | |||||
| namespace Discord.Net.WebSockets | |||||
| { | |||||
| public class TextMessageEventArgs : EventArgs | |||||
| { | |||||
| public string Message { get; } | |||||
| public TextMessageEventArgs(string msg) { Message = msg; } | |||||
| } | |||||
| } | |||||
| @@ -10,7 +10,7 @@ using WebSocketClient = WebSocket4Net.WebSocket; | |||||
| namespace Discord.Net.WebSockets | namespace Discord.Net.WebSockets | ||||
| { | { | ||||
| internal class WS4NetEngine : IWebSocketEngine | |||||
| internal sealed class WS4NetEngine : IWebSocketEngine | |||||
| { | { | ||||
| private readonly DiscordConfig _config; | private readonly DiscordConfig _config; | ||||
| private readonly ConcurrentQueue<string> _sendQueue; | private readonly ConcurrentQueue<string> _sendQueue; | ||||
| @@ -18,12 +18,12 @@ namespace Discord.Net.WebSockets | |||||
| private WebSocketClient _webSocket; | private WebSocketClient _webSocket; | ||||
| private ManualResetEventSlim _waitUntilConnect, _waitUntilDisconnect; | private ManualResetEventSlim _waitUntilConnect, _waitUntilDisconnect; | ||||
| public event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage = delegate { }; | |||||
| public event EventHandler<WebSocketTextMessageEventArgs> TextMessage = delegate { }; | |||||
| public event EventHandler<BinaryMessageEventArgs> BinaryMessage = delegate { }; | |||||
| public event EventHandler<TextMessageEventArgs> TextMessage = delegate { }; | |||||
| private void OnBinaryMessage(byte[] data) | private void OnBinaryMessage(byte[] data) | ||||
| => BinaryMessage(this, new WebSocketBinaryMessageEventArgs(data)); | |||||
| => BinaryMessage(this, new BinaryMessageEventArgs(data)); | |||||
| private void OnTextMessage(string msg) | private void OnTextMessage(string msg) | ||||
| => TextMessage(this, new WebSocketTextMessageEventArgs(msg)); | |||||
| => TextMessage(this, new TextMessageEventArgs(msg)); | |||||
| internal WS4NetEngine(DiscordConfig config, TaskManager taskManager) | internal WS4NetEngine(DiscordConfig config, TaskManager taskManager) | ||||
| { | { | ||||
| @@ -1,152 +0,0 @@ | |||||
| #if DOTNET5_4 | |||||
| /*using System; | |||||
| using System.Collections.Concurrent; | |||||
| using System.Collections.Generic; | |||||
| using System.ComponentModel; | |||||
| using System.Net.WebSockets; | |||||
| using System.Text; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | |||||
| using State = System.Net.WebSockets.WebSocketState; | |||||
| namespace Discord.Net.WebSockets | |||||
| { | |||||
| internal class BuiltInWebSocketEngine : IWebSocketEngine | |||||
| { | |||||
| private const int ReceiveChunkSize = 4096; | |||||
| private const int SendChunkSize = 4096; | |||||
| private const int HR_TIMEOUT = -2147012894; | |||||
| private readonly ConcurrentQueue<string> _sendQueue; | |||||
| private readonly int _sendInterval; | |||||
| private ClientWebSocket _webSocket; | |||||
| public event EventHandler<WebSocketMessageEventArgs> ProcessMessage; | |||||
| private void RaiseProcessMessage(string msg) | |||||
| { | |||||
| if (ProcessMessage != null) | |||||
| ProcessMessage(this, new WebSocketMessageEventArgs(msg)); | |||||
| } | |||||
| public BuiltInWebSocketEngine(int sendInterval) | |||||
| { | |||||
| _sendInterval = sendInterval; | |||||
| _sendQueue = new ConcurrentQueue<string>(); | |||||
| } | |||||
| public Task Connect(string host, CancellationToken cancelToken) | |||||
| { | |||||
| _webSocket = new ClientWebSocket(); | |||||
| return _webSocket.ConnectAsync(new Uri(host), cancelToken); | |||||
| } | |||||
| public Task Disconnect() | |||||
| { | |||||
| string ignored; | |||||
| while (_sendQueue.TryDequeue(out ignored)) { } | |||||
| _webSocket.Dispose(); | |||||
| _webSocket = new ClientWebSocket(); | |||||
| return TaskHelper.CompletedTask; | |||||
| } | |||||
| public IEnumerable<Task> GetTasks(CancellationToken cancelToken) | |||||
| { | |||||
| return new Task[] | |||||
| { | |||||
| ReceiveAsync(cancelToken), | |||||
| SendAsync(cancelToken) | |||||
| }; | |||||
| } | |||||
| private Task ReceiveAsync(CancellationToken cancelToken) | |||||
| { | |||||
| return Task.Run(async () => | |||||
| { | |||||
| var buffer = new ArraySegment<byte>(new byte[ReceiveChunkSize]); | |||||
| var builder = new StringBuilder(); | |||||
| try | |||||
| { | |||||
| while (_webSocket.State == State.Open && !cancelToken.IsCancellationRequested) | |||||
| { | |||||
| WebSocketReceiveResult result = null; | |||||
| do | |||||
| { | |||||
| if (_webSocket.State != State.Open || cancelToken.IsCancellationRequested) | |||||
| return; | |||||
| try | |||||
| { | |||||
| result = await _webSocket.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false); | |||||
| } | |||||
| catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) | |||||
| { | |||||
| throw new Exception($"Connection timed out."); | |||||
| } | |||||
| if (result.MessageType == WebSocketMessageType.Close) | |||||
| throw new Exception($"Got Close Message ({result.CloseStatus?.ToString() ?? "Unexpected"}): " + | |||||
| result.CloseStatusDescription != "" ? result.CloseStatusDescription : "No Reason"); | |||||
| else | |||||
| builder.Append(Encoding.UTF8.GetString(buffer.Array, buffer.Offset, result.Count)); | |||||
| } | |||||
| while (result == null || !result.EndOfMessage); | |||||
| RaiseProcessMessage(builder.ToString()); | |||||
| builder.Clear(); | |||||
| } | |||||
| } | |||||
| catch (OperationCanceledException) { } | |||||
| }); | |||||
| } | |||||
| private Task SendAsync(CancellationToken cancelToken) | |||||
| { | |||||
| return Task.Run(async () => | |||||
| { | |||||
| try | |||||
| { | |||||
| while (_webSocket.State == State.Open && !cancelToken.IsCancellationRequested) | |||||
| { | |||||
| string json; | |||||
| while (_sendQueue.TryDequeue(out json)) | |||||
| { | |||||
| byte[] bytes = Encoding.UTF8.GetBytes(json); | |||||
| int frameCount = (int)Math.Ceiling((double)bytes.Length / SendChunkSize); | |||||
| int offset = 0; | |||||
| for (var i = 0; i < frameCount; i++, offset += SendChunkSize) | |||||
| { | |||||
| bool isLast = i == (frameCount - 1); | |||||
| int count; | |||||
| if (isLast) | |||||
| count = bytes.Length - (i * SendChunkSize); | |||||
| else | |||||
| count = SendChunkSize; | |||||
| try | |||||
| { | |||||
| await _webSocket.SendAsync(new ArraySegment<byte>(bytes, offset, count), WebSocketMessageType.Text, isLast, cancelToken).ConfigureAwait(false); | |||||
| } | |||||
| catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) | |||||
| { | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| await Task.Delay(_sendInterval, cancelToken).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| catch (OperationCanceledException) { } | |||||
| }); | |||||
| } | |||||
| public void QueueMessage(string message) | |||||
| { | |||||
| _sendQueue.Enqueue(message); | |||||
| } | |||||
| } | |||||
| }*/ | |||||
| #endif | |||||
| @@ -0,0 +1,17 @@ | |||||
| using Newtonsoft.Json.Linq; | |||||
| using System; | |||||
| namespace Discord.Net.WebSockets | |||||
| { | |||||
| public class WebSocketEventEventArgs : EventArgs | |||||
| { | |||||
| public string Type { get; } | |||||
| public JToken Payload { get; } | |||||
| internal WebSocketEventEventArgs(string type, JToken data) | |||||
| { | |||||
| Type = type; | |||||
| Payload = data; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,118 +0,0 @@ | |||||
| /*#if !DOTNET5_4 | |||||
| using System; | |||||
| using System.Collections.Concurrent; | |||||
| using System.Collections.Generic; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | |||||
| using WSSharpWebSocket = WebSocketSharp.WebSocket; | |||||
| namespace Discord.Net.WebSockets | |||||
| { | |||||
| internal class WebSocketSharpEngine : IWebSocketEngine | |||||
| { | |||||
| private readonly DiscordConfig _config; | |||||
| private readonly Logger _logger; | |||||
| private readonly ConcurrentQueue<string> _sendQueue; | |||||
| private readonly WebSocket _parent; | |||||
| private WSSharpWebSocket _webSocket; | |||||
| public event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage; | |||||
| public event EventHandler<WebSocketTextMessageEventArgs> TextMessage; | |||||
| private void RaiseBinaryMessage(byte[] data) | |||||
| { | |||||
| if (BinaryMessage != null) | |||||
| BinaryMessage(this, new WebSocketBinaryMessageEventArgs(data)); | |||||
| } | |||||
| private void RaiseTextMessage(string msg) | |||||
| { | |||||
| if (TextMessage != null) | |||||
| TextMessage(this, new WebSocketTextMessageEventArgs(msg)); | |||||
| } | |||||
| internal WebSocketSharpEngine(WebSocket parent, DiscordConfig config, Logger logger) | |||||
| { | |||||
| _parent = parent; | |||||
| _config = config; | |||||
| _logger = logger; | |||||
| _sendQueue = new ConcurrentQueue<string>(); | |||||
| } | |||||
| public Task Connect(string host, CancellationToken cancelToken) | |||||
| { | |||||
| _webSocket = new WSSharpWebSocket(host); | |||||
| _webSocket.EmitOnPing = false; | |||||
| _webSocket.EnableRedirection = true; | |||||
| //_webSocket.Compression = WebSocketSharp.CompressionMethod.Deflate; | |||||
| _webSocket.SetProxy(null, null, null); //Disable | |||||
| //_webSocket.SetProxy(_config.ProxyUrl, _config.ProxyCredentials?.UserName, _config.ProxyCredentials?.Password); | |||||
| _webSocket.OnMessage += (s, e) => | |||||
| { | |||||
| if (e.IsBinary) | |||||
| RaiseBinaryMessage(e.RawData); | |||||
| else if (e.IsText) | |||||
| RaiseTextMessage(e.Data); | |||||
| }; | |||||
| _webSocket.OnError += async (s, e) => | |||||
| { | |||||
| _logger.Log(LogSeverity.Error, "WebSocket Error", e.Exception); | |||||
| await _parent.SignalDisconnect(e.Exception, isUnexpected: true).ConfigureAwait(false); | |||||
| }; | |||||
| _webSocket.OnClose += async (s, e) => | |||||
| { | |||||
| string code = e.WasClean ? e.Code.ToString() : "Unexpected"; | |||||
| string reason = e.Reason != "" ? e.Reason : "No Reason"; | |||||
| var ex = new Exception($"Got Close Message ({code}): {reason}"); | |||||
| await _parent.SignalDisconnect(ex, isUnexpected: true).ConfigureAwait(false); | |||||
| }; | |||||
| _webSocket.Log.Output = (e, m) => { }; //Dont let websocket-sharp print to console directly | |||||
| _webSocket.Connect(); | |||||
| return TaskHelper.CompletedTask; | |||||
| } | |||||
| public Task Disconnect() | |||||
| { | |||||
| string ignored; | |||||
| while (_sendQueue.TryDequeue(out ignored)) { } | |||||
| var socket = _webSocket; | |||||
| _webSocket = null; | |||||
| if (socket != null) | |||||
| socket.Close(); | |||||
| return TaskHelper.CompletedTask; | |||||
| } | |||||
| public IEnumerable<Task> GetTasks(CancellationToken cancelToken) | |||||
| { | |||||
| return new Task[] | |||||
| { | |||||
| SendAsync(cancelToken) | |||||
| }; | |||||
| } | |||||
| private Task SendAsync(CancellationToken cancelToken) | |||||
| { | |||||
| var sendInterval = _config.WebSocketInterval; | |||||
| return Task.Run(async () => | |||||
| { | |||||
| try | |||||
| { | |||||
| while (!cancelToken.IsCancellationRequested) | |||||
| { | |||||
| string json; | |||||
| while (_sendQueue.TryDequeue(out json)) | |||||
| _webSocket.Send(json); | |||||
| await Task.Delay(sendInterval, cancelToken).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| catch (OperationCanceledException) { } | |||||
| }); | |||||
| } | |||||
| public void QueueMessage(string message) | |||||
| { | |||||
| _sendQueue.Enqueue(message); | |||||
| } | |||||
| } | |||||
| } | |||||
| #endif*/ | |||||
| @@ -1,71 +0,0 @@ | |||||
| using System; | |||||
| namespace Discord | |||||
| { | |||||
| /*internal class Reference<T> | |||||
| where T : CachedObject<ulong> | |||||
| { | |||||
| private Action<T> _onCache, _onUncache; | |||||
| private Func<ulong, T> _getItem; | |||||
| private ulong? _id; | |||||
| public ulong? Id | |||||
| { | |||||
| get { return _id; } | |||||
| set | |||||
| { | |||||
| _id = value; | |||||
| _value = null; | |||||
| } | |||||
| } | |||||
| private T _value; | |||||
| public T Value | |||||
| { | |||||
| get | |||||
| { | |||||
| } | |||||
| } | |||||
| public T Load() | |||||
| { | |||||
| var v = _value; //A little trickery to make this threadsafe | |||||
| var id = _id; | |||||
| if (v != null && !_value.IsCached) | |||||
| { | |||||
| v = null; | |||||
| _value = null; | |||||
| } | |||||
| if (v == null && id != null) | |||||
| { | |||||
| v = _getItem(id.Value); | |||||
| if (v != null && _onCache != null) | |||||
| _onCache(v); | |||||
| _value = v; | |||||
| } | |||||
| return v; | |||||
| return Value != null; //Used for precaching | |||||
| } | |||||
| public void Unload() | |||||
| { | |||||
| if (_onUncache != null) | |||||
| { | |||||
| var v = _value; | |||||
| if (v != null && _onUncache != null) | |||||
| _onUncache(v); | |||||
| } | |||||
| } | |||||
| public Reference(Func<ulong, T> onUpdate, Action<T> onCache = null, Action<T> onUncache = null) | |||||
| : this(null, onUpdate, onCache, onUncache) | |||||
| { } | |||||
| public Reference(ulong? id, Func<ulong, T> getItem, Action<T> onCache = null, Action<T> onUncache = null) | |||||
| { | |||||
| _id = id; | |||||
| _getItem = getItem; | |||||
| _onCache = onCache; | |||||
| _onUncache = onUncache; | |||||
| _value = null; | |||||
| } | |||||
| }*/ | |||||
| } | |||||
| @@ -5,6 +5,7 @@ namespace Discord | |||||
| public class RoleEventArgs : EventArgs | public class RoleEventArgs : EventArgs | ||||
| { | { | ||||
| public Role Role { get; } | public Role Role { get; } | ||||
| public Server Server => Role.Server; | public Server Server => Role.Server; | ||||
| public RoleEventArgs(Role role) { Role = role; } | public RoleEventArgs(Role role) { Role = role; } | ||||
| @@ -3,7 +3,7 @@ using System.Collections.Generic; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class ServiceManager | |||||
| public sealed class ServiceManager | |||||
| { | { | ||||
| private readonly Dictionary<Type, IService> _services; | private readonly Dictionary<Type, IService> _services; | ||||
| @@ -8,7 +8,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> Helper class used to manage several tasks and keep them in sync. If any single task errors or stops, all other tasks will also be stopped. </summary> | /// <summary> Helper class used to manage several tasks and keep them in sync. If any single task errors or stops, all other tasks will also be stopped. </summary> | ||||
| public class TaskManager | |||||
| public sealed class TaskManager | |||||
| { | { | ||||
| private readonly object _lock; | private readonly object _lock; | ||||
| private readonly Func<Task> _stopAction; | private readonly Func<Task> _stopAction; | ||||
| @@ -22,7 +22,7 @@ namespace Discord | |||||
| public Exception Exception => _stopReason?.SourceException; | public Exception Exception => _stopReason?.SourceException; | ||||
| private ExceptionDispatchInfo _stopReason; | private ExceptionDispatchInfo _stopReason; | ||||
| public TaskManager() | |||||
| internal TaskManager() | |||||
| { | { | ||||
| _lock = new object(); | _lock = new object(); | ||||
| } | } | ||||
| @@ -4,6 +4,7 @@ namespace Discord | |||||
| public class UserEventArgs : EventArgs | public class UserEventArgs : EventArgs | ||||
| { | { | ||||
| public User User { get; } | public User User { get; } | ||||
| public Server Server => User.Server; | public Server Server => User.Server; | ||||
| public UserEventArgs(User user) { User = user; } | public UserEventArgs(User user) { User = user; } | ||||
| @@ -1,5 +1,4 @@ | |||||
| using Discord.Legacy; | |||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||