diff --git a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj b/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
index 99cfdcc0b..f7326b4a9 100644
--- a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
+++ b/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
@@ -47,6 +47,9 @@
AudioExtensions.cs
+
+ AudioMode.cs
+
AudioService.cs
diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net.Audio/AudioClient.cs
index c7d9d182d..46474f6a4 100644
--- a/src/Discord.Net.Audio/AudioClient.cs
+++ b/src/Discord.Net.Audio/AudioClient.cs
@@ -61,8 +61,8 @@ namespace Discord.Audio
public Stream OutputStream { get; }
public CancellationToken CancelToken { get; private set; }
- public string SessionId { get; private set; }
-
+ public string SessionId => GatewaySocket.SessionId;
+
public ConnectionState State => VoiceSocket.State;
public Server Server => VoiceSocket.Server;
public Channel Channel => VoiceSocket.Channel;
@@ -71,7 +71,7 @@ namespace Discord.Audio
{
Id = id;
_config = client.Config;
- Service = client.Audio();
+ Service = client.Services.Get();
Config = Service.Config;
Serializer = client.Serializer;
_gatewayState = (int)ConnectionState.Disconnected;
diff --git a/src/Discord.Net.Audio/AudioExtensions.cs b/src/Discord.Net.Audio/AudioExtensions.cs
index eca362f80..50f508ff5 100644
--- a/src/Discord.Net.Audio/AudioExtensions.cs
+++ b/src/Discord.Net.Audio/AudioExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
namespace Discord.Audio
{
@@ -9,14 +10,17 @@ namespace Discord.Audio
client.Services.Add(new AudioService(config));
return client;
}
- public static DiscordClient UsingAudio(this DiscordClient client, Action configFunc = null)
+ public static DiscordClient UsingAudio(this DiscordClient client, Action configFunc = null)
{
- var config = new AudioServiceConfig();
- configFunc(config);
- client.Services.Add(new AudioService(config));
+ var builder = new AudioServiceConfigBuilder();
+ configFunc(builder);
+ client.Services.Add(new AudioService(builder));
return client;
}
- public static AudioService Audio(this DiscordClient client, bool required = true)
- => client.Services.Get(required);
+
+ public static Task JoinAudio(this Channel channel) => channel.Client.Services.Get().Join(channel);
+ public static Task LeaveAudio(this Channel channel) => channel.Client.Services.Get().Leave(channel);
+ public static Task LeaveAudio(this Server server) => server.Client.Services.Get().Leave(server);
+ public static IAudioClient GetAudioClient(Server server) => server.Client.Services.Get().GetClient(server);
}
}
diff --git a/src/Discord.Net.Audio/AudioMode.cs b/src/Discord.Net.Audio/AudioMode.cs
new file mode 100644
index 000000000..b9acdbf89
--- /dev/null
+++ b/src/Discord.Net.Audio/AudioMode.cs
@@ -0,0 +1,9 @@
+namespace Discord.Audio
+{
+ public enum AudioMode : byte
+ {
+ Outgoing = 1,
+ Incoming = 2,
+ Both = Outgoing | Incoming
+ }
+}
diff --git a/src/Discord.Net.Audio/AudioService.cs b/src/Discord.Net.Audio/AudioService.cs
index a8844bc46..3de3531ae 100644
--- a/src/Discord.Net.Audio/AudioService.cs
+++ b/src/Discord.Net.Audio/AudioService.cs
@@ -29,16 +29,23 @@ namespace Discord.Audio
private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
=> UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking));
- public AudioService(AudioServiceConfig config)
+ public AudioService()
+ : this(new AudioServiceConfigBuilder())
+ {
+ }
+ public AudioService(AudioServiceConfigBuilder builder)
+ : this(builder.Build())
+ {
+ }
+ public AudioService(AudioServiceConfig config)
{
- Config = config;
+ Config = config;
_asyncLock = new AsyncLock();
}
void IService.Install(DiscordClient client)
{
Client = client;
- Config.Lock();
if (Config.EnableMultiserver)
_voiceClients = new ConcurrentDictionary();
diff --git a/src/Discord.Net.Audio/AudioServiceConfig.cs b/src/Discord.Net.Audio/AudioServiceConfig.cs
index a9b4a4b33..89d05d85b 100644
--- a/src/Discord.Net.Audio/AudioServiceConfig.cs
+++ b/src/Discord.Net.Audio/AudioServiceConfig.cs
@@ -1,62 +1,51 @@
-
-using System;
-
-namespace Discord.Audio
+namespace Discord.Audio
{
- public enum AudioMode : byte
+ public class AudioServiceConfigBuilder
{
- Outgoing = 1,
- Incoming = 2,
- Both = Outgoing | Incoming
- }
+ /// Enables the voice websocket and UDP client and specifies how it will be used.
+ public AudioMode Mode { get; set; } = AudioMode.Outgoing;
- public class AudioServiceConfig
- {
+ /// Enables the voice websocket and UDP client. This option requires the libsodium .dll or .so be in the local or system folder.
+ public bool EnableEncryption { get; set; } = true;
+ ///
+ /// Enables the client to be simultaneously connected to multiple channels at once (Discord still limits you to one channel per server).
+ /// This option uses a lot of CPU power and network bandwidth, as a new gateway connection needs to be spun up per server. Use sparingly.
+ ///
+ public bool EnableMultiserver { get; set; } = false;
+
+ /// Gets or sets the buffer length (in milliseconds) for outgoing voice packets.
+ public int BufferLength { get; set; } = 1000;
+ /// Gets or sets the bitrate used (in kbit/s, between 1 and MaxBitrate inclusively) for outgoing voice packets. A null value will use default Opus settings.
+ public int? Bitrate { get; set; } = null;
+ /// Gets or sets the number of channels (1 or 2) used in both input provided to IAudioClient and output send to Discord. Defaults to 2 (stereo).
+ public int Channels { get; set; } = 2;
+
+ public AudioServiceConfig Build() => new AudioServiceConfig(this);
+ }
+
+ public class AudioServiceConfig
+ {
public const int MaxBitrate = 128;
- /// Max time in milliseconds to wait for DiscordAudioClient to connect and initialize.
- public int ConnectionTimeout { get { return _connectionTimeout; } set { SetValue(ref _connectionTimeout, value); } }
- private int _connectionTimeout = 30000;
+ public AudioMode Mode { get; }
- //Experimental Features
- /// (Experimental) Enables the voice websocket and UDP client and specifies how it will be used.
- public AudioMode Mode { get { return _mode; } set { SetValue(ref _mode, value); } }
- private AudioMode _mode = AudioMode.Outgoing;
+ public bool EnableEncryption { get; }
+ public bool EnableMultiserver { get; }
- /// (Experimental) Enables the voice websocket and UDP client. This option requires the libsodium .dll or .so be in the local or system folder.
- public bool EnableEncryption { get { return _enableEncryption; } set { SetValue(ref _enableEncryption, value); } }
- private bool _enableEncryption = true;
+ public int BufferLength { get; }
+ public int? Bitrate { get; }
+ public int Channels { get; }
- /// (Experimental) Enables the client to be simultaneously connected to multiple channels at once (Discord still limits you to one channel per server).
- public bool EnableMultiserver { get { return _enableMultiserver; } set { SetValue(ref _enableMultiserver, value); } }
- private bool _enableMultiserver = false;
+ internal AudioServiceConfig(AudioServiceConfigBuilder builder)
+ {
+ Mode = builder.Mode;
- /// Gets or sets the buffer length (in milliseconds) for outgoing voice packets.
- public int BufferLength { get { return _bufferLength; } set { SetValue(ref _bufferLength, value); } }
- private int _bufferLength = 1000;
+ EnableEncryption = builder.EnableEncryption;
+ EnableMultiserver = builder.EnableMultiserver;
- /// Gets or sets the bitrate used (in kbit/s, between 1 and MaxBitrate inclusively) for outgoing voice packets. A null value will use default Opus settings.
- public int? Bitrate { get { return _bitrate; } set { SetValue(ref _bitrate, value); } }
- private int? _bitrate = null;
- /// Gets or sets the number of channels (1 or 2) used in both input provided to IAudioClient and output send to Discord. Defaults to 2 (stereo).
- public int Channels { get { return _channels; } set { SetValue(ref _channels, value); } }
- private int _channels = 2;
-
- //Lock
- protected bool _isLocked;
- internal void Lock() { _isLocked = true; }
- protected void SetValue(ref T storage, T value)
- {
- if (_isLocked)
- throw new InvalidOperationException("Unable to modify a service's configuration after it has been created.");
- storage = value;
- }
-
- public AudioServiceConfig Clone()
- {
- var config = MemberwiseClone() as AudioServiceConfig;
- config._isLocked = false;
- return config;
- }
- }
+ BufferLength = builder.BufferLength;
+ Bitrate = builder.Bitrate;
+ Channels = builder.Channels;
+ }
+ }
}
diff --git a/src/Discord.Net.Commands/CommandExtensions.cs b/src/Discord.Net.Commands/CommandExtensions.cs
index 1ac1e030b..557f5ac5a 100644
--- a/src/Discord.Net.Commands/CommandExtensions.cs
+++ b/src/Discord.Net.Commands/CommandExtensions.cs
@@ -9,14 +9,12 @@ namespace Discord.Commands
client.Services.Add(new CommandService(config));
return client;
}
- public static DiscordClient UsingCommands(this DiscordClient client, Action configFunc = null)
+ public static DiscordClient UsingCommands(this DiscordClient client, Action configFunc = null)
{
- var config = new CommandServiceConfig();
- configFunc(config);
- client.Services.Add(new CommandService(config));
+ var builder = new CommandServiceConfigBuilder();
+ configFunc(builder);
+ client.Services.Add(new CommandService(builder));
return client;
}
- public static CommandService Commands(this DiscordClient client, bool required = true)
- => client.Services.Get(required);
}
}
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index 27ebe0927..647bd01f1 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -29,6 +29,18 @@ namespace Discord.Commands
private void OnCommandError(CommandErrorType errorType, CommandEventArgs args, Exception ex = null)
=> CommandErrored(this, new CommandErrorEventArgs(errorType, args, ex));
+ public CommandService()
+ : this(new CommandServiceConfigBuilder())
+ {
+ }
+ public CommandService(CommandServiceConfigBuilder builder)
+ : this(builder.Build())
+ {
+ if (builder.ExecuteHandler != null)
+ CommandExecuted += builder.ExecuteHandler;
+ if (builder.ErrorHandler != null)
+ CommandErrored += builder.ErrorHandler;
+ }
public CommandService(CommandServiceConfig config)
{
Config = config;
@@ -42,9 +54,8 @@ namespace Discord.Commands
void IService.Install(DiscordClient client)
{
Client = client;
- Config.Lock();
- if (Config.HelpMode != HelpMode.Disable)
+ if (Config.HelpMode != HelpMode.Disabled)
{
CreateCommand("help")
.Parameter("command", ParameterType.Multiple)
diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs
index 982ce2cd9..f43c838fb 100644
--- a/src/Discord.Net.Commands/CommandServiceConfig.cs
+++ b/src/Discord.Net.Commands/CommandServiceConfig.cs
@@ -2,36 +2,45 @@
namespace Discord.Commands
{
- public class CommandServiceConfig
+ public class CommandServiceConfigBuilder
{
/// Gets or sets the prefix character used to trigger commands, if ActivationMode has the Char flag set.
- public char? PrefixChar { get { return _prefixChar; } set { SetValue(ref _prefixChar, value); } }
- private char? _prefixChar = null;
-
+ public char? PrefixChar { get; set; } = null;
/// Gets or sets whether a message beginning with a mention to the logged-in user should be treated as a command.
- public bool AllowMentionPrefix { get { return _allowMentionPrefix; } set { SetValue(ref _allowMentionPrefix, value); } }
- private bool _allowMentionPrefix = true;
-
+ public bool AllowMentionPrefix { get; set; } = true;
///
/// Gets or sets a custom function used to detect messages that should be treated as commands.
/// This function should a positive one indicating the index of where the in the message's RawText the command begins,
/// and a negative value if the message should be ignored.
///
- public Func CustomPrefixHandler { get { return _customPrefixHandler; } set { SetValue(ref _customPrefixHandler, value); } }
- private Func _customPrefixHandler = null;
+ public Func CustomPrefixHandler { get; set; } = null;
+
+ /// Gets or sets whether a help function should be automatically generated.
+ public HelpMode HelpMode { get; set; } = HelpMode.Disabled;
+
+
+ /// Gets or sets a handler that is called on any successful command execution.
+ public EventHandler ExecuteHandler { get; set; }
+ /// Gets or sets a handler that is called on any error during command parsing or execution.
+ public EventHandler ErrorHandler { get; set; }
+
+ public CommandServiceConfig Build() => new CommandServiceConfig(this);
+ }
+ public class CommandServiceConfig
+ {
+ public char? PrefixChar { get; }
+ public bool AllowMentionPrefix { get; }
+ public Func CustomPrefixHandler { get; }
/// Gets or sets whether a help function should be automatically generated.
- public HelpMode HelpMode { get { return _helpMode; } set { SetValue(ref _helpMode, value); } }
- private HelpMode _helpMode = HelpMode.Disable;
+ public HelpMode HelpMode { get; set; } = HelpMode.Disabled;
- //Lock
- protected bool _isLocked;
- internal void Lock() { _isLocked = true; }
- protected void SetValue(ref T storage, T value)
- {
- if (_isLocked)
- throw new InvalidOperationException("Unable to modify a service's configuration after it has been created.");
- storage = value;
- }
+ internal CommandServiceConfig(CommandServiceConfigBuilder builder)
+ {
+ PrefixChar = builder.PrefixChar;
+ AllowMentionPrefix = builder.AllowMentionPrefix;
+ CustomPrefixHandler = builder.CustomPrefixHandler;
+ HelpMode = builder.HelpMode;
+ }
}
}
diff --git a/src/Discord.Net.Commands/HelpMode.cs b/src/Discord.Net.Commands/HelpMode.cs
index 27f5c444f..272403f42 100644
--- a/src/Discord.Net.Commands/HelpMode.cs
+++ b/src/Discord.Net.Commands/HelpMode.cs
@@ -3,7 +3,7 @@
public enum HelpMode
{
/// Disable the automatic help command.
- Disable,
+ Disabled,
/// Use the automatic help command and respond in the channel the command is used.
Public,
/// Use the automatic help command and respond in a private message.
diff --git a/src/Discord.Net.Modules/ModuleManager.cs b/src/Discord.Net.Modules/ModuleManager.cs
index 17b2b166b..0427219d3 100644
--- a/src/Discord.Net.Modules/ModuleManager.cs
+++ b/src/Discord.Net.Modules/ModuleManager.cs
@@ -113,7 +113,7 @@ namespace Discord.Modules
public void CreateCommands(string prefix, Action config)
{
- var commandService = Client.Commands(true);
+ var commandService = Client.Services.Get();
commandService.CreateGroup(prefix, x =>
{
x.Category(Name);
diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj
index 7bc06ff71..42b76726f 100644
--- a/src/Discord.Net.Net45/Discord.Net.csproj
+++ b/src/Discord.Net.Net45/Discord.Net.csproj
@@ -409,6 +409,9 @@
Enums\ImageType.cs
+
+ Enums\LogSeverity.cs
+
Enums\PermissionTarget.cs
diff --git a/src/Discord.Net.Shared/TaskHelper.cs b/src/Discord.Net.Shared/TaskHelper.cs
index 82c70f500..83408b8e9 100644
--- a/src/Discord.Net.Shared/TaskHelper.cs
+++ b/src/Discord.Net.Shared/TaskHelper.cs
@@ -5,15 +5,11 @@ namespace Discord
{
internal static class TaskHelper
{
- public static Task CompletedTask { get; }
- static TaskHelper()
- {
#if DOTNET54
- CompletedTask = Task.CompletedTask;
+ public static Task CompletedTask => Task.CompletedTask;
#else
- CompletedTask = Task.Delay(0);
+ public static Task CompletedTask => Task.Delay(0);
#endif
- }
public static Func ToAsync(Action action)
{
diff --git a/src/Discord.Net/Config.cs b/src/Discord.Net/Config.cs
deleted file mode 100644
index af7da9529..000000000
--- a/src/Discord.Net/Config.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Discord
-{
- public abstract class Config
- where T : Config
- {
- protected bool _isLocked;
- protected internal void Lock() { _isLocked = true; }
- protected void SetValue(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;
- }
- }
-}
diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs
index 5e6ce6650..bdcc8c74a 100644
--- a/src/Discord.Net/DiscordClient.cs
+++ b/src/Discord.Net/DiscordClient.cs
@@ -76,28 +76,33 @@ namespace Discord
public IEnumerable Regions => _regions.Select(x => x.Value);
/// Initializes a new instance of the DiscordClient class.
- public DiscordClient(Action configFunc)
+ public DiscordClient(Action configFunc)
: this(ProcessConfig(configFunc))
{
}
- private static DiscordConfig ProcessConfig(Action func)
+ private static DiscordConfigBuilder ProcessConfig(Action func)
{
- var config = new DiscordConfig();
+ var config = new DiscordConfigBuilder();
func(config);
return config;
}
/// Initializes a new instance of the DiscordClient class.
public DiscordClient()
- : this((DiscordConfig)null)
+ : this(new DiscordConfigBuilder())
{
}
-
+ /// Initializes a new instance of the DiscordClient class.
+ public DiscordClient(DiscordConfigBuilder builder)
+ : this(builder.Build())
+ {
+ if (builder.LogHandler != null)
+ Log.Message += builder.LogHandler;
+ }
/// Initializes a new instance of the DiscordClient class.
public DiscordClient(DiscordConfig config)
{
- Config = config ?? new DiscordConfig();
- Config.Lock();
+ Config = config;
State = (int)ConnectionState.Disconnected;
Status = UserStatus.Online;
@@ -145,9 +150,8 @@ namespace Discord
};
//GatewaySocket.Disconnected += (s, e) => OnDisconnected(e.WasUnexpected, e.Exception);
GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e);
-
- if (Config.UseMessageQueue)
- MessageQueue = new MessageQueue(ClientAPI, Log.CreateLogger("MessageQueue"));
+
+ MessageQueue = new MessageQueue(ClientAPI, Log.CreateLogger("MessageQueue"));
//Extensibility
Services = new ServiceManager(this);
@@ -194,10 +198,11 @@ namespace Discord
await Login(email, password, token).ConfigureAwait(false);
await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false);
- List tasks = new List();
- tasks.Add(CancelToken.Wait());
- if (Config.UseMessageQueue)
- tasks.Add(MessageQueue.Run(CancelToken, Config.MessageQueueInterval));
+ Task[] tasks = new[]
+ {
+ CancelToken.Wait(),
+ MessageQueue.Run(CancelToken)
+ };
await _taskManager.Start(tasks, cancelSource).ConfigureAwait(false);
GatewaySocket.WaitForConnection(CancelToken);
@@ -222,7 +227,7 @@ namespace Discord
byte[] cacheKey = null;
//Get Token
- if (email != null && Config.CacheToken)
+ if (email != null && Config.CacheDir != null)
{
tokenPath = GetTokenCachePath(email);
if (token == null && password != null)
@@ -240,7 +245,7 @@ namespace Discord
var request = new LoginRequest() { Email = email, Password = password };
var response = await ClientAPI.Send(request).ConfigureAwait(false);
token = response.Token;
- if (Config.CacheToken && token != oldToken && tokenPath != null)
+ if (Config.CacheDir != null && token != oldToken && tokenPath != null)
SaveToken(tokenPath, cacheKey, token);
ClientAPI.Token = token;
@@ -270,9 +275,8 @@ namespace Discord
try { await ClientAPI.Send(new LogoutRequest()).ConfigureAwait(false); }
catch (OperationCanceledException) { }
}
-
- if (Config.UseMessageQueue)
- MessageQueue.Clear();
+
+ MessageQueue.Clear();
await GatewaySocket.Disconnect().ConfigureAwait(false);
ClientAPI.Token = null;
@@ -1053,7 +1057,7 @@ namespace Discord
StringBuilder filenameBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
filenameBuilder.Append(data[i].ToString("x2"));
- return Path.Combine(Path.GetTempPath(), Config.AppName ?? "Discord.Net", filenameBuilder.ToString());
+ return Path.Combine(Config.CacheDir, filenameBuilder.ToString());
}
}
private string LoadToken(string path, byte[] key)
diff --git a/src/Discord.Net/DiscordConfig.cs b/src/Discord.Net/DiscordConfig.cs
index 62512aae9..c22b60163 100644
--- a/src/Discord.Net/DiscordConfig.cs
+++ b/src/Discord.Net/DiscordConfig.cs
@@ -1,127 +1,133 @@
-using Newtonsoft.Json;
-using System;
+using System;
+using System.IO;
using System.Reflection;
using System.Text;
namespace Discord
-{
- public enum LogSeverity : byte
- {
- Error = 1,
- Warning = 2,
- Info = 3,
- Verbose = 4,
- Debug = 5
- }
-
- public class DiscordConfig : Config
+{
+ public class DiscordConfigBuilder
{
- public const int MaxMessageSize = 2000;
-
- public const string LibName = "Discord.Net";
- public static string LibVersion => typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3);
- public const string LibUrl = "https://github.com/RogueException/Discord.Net";
-
- public const string ClientAPIUrl = "https://discordapp.com/api/";
- public const string StatusAPIUrl = "https://srhpyqt94yxb.statuspage.io/api/v2/"; //"https://status.discordapp.com/api/v2/";
- //public const string CDNUrl = "https://cdn.discordapp.com/";
- public const string InviteUrl = "https://discord.gg/";
-
//Global
/// Gets or sets name of your application, used both for the token cache directory and user agent.
- public string AppName { get { return _appName; } set { SetValue(ref _appName, value); UpdateUserAgent(); } }
- private string _appName = null;
+ public string AppName { get; set; } = null;
/// Gets or sets url to your application, used in the user agent.
- public string AppUrl { get { return _appUrl; } set { SetValue(ref _appUrl, value); UpdateUserAgent(); } }
- private string _appUrl = null;
+ public string AppUrl { get; set; } = null;
/// Gets or sets the version of your application, used in the user agent.
- public string AppVersion { get { return _appVersion; } set { SetValue(ref _appVersion, value); UpdateUserAgent(); } }
- private string _appVersion = null;
+ public string AppVersion { get; set; } = null;
/// Gets or sets the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues.
- public LogSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } }
- private LogSeverity _logLevel = LogSeverity.Info;
+ public LogSeverity LogLevel { get; set; } = LogSeverity.Info;
/// Enables or disables the default event logger.
- public bool LogEvents { get { return _logEvents; } set { SetValue(ref _logEvents, value); } }
- private bool _logEvents = true;
-
- /// Gets the user agent used when connecting to Discord.
- public string UserAgent { get; private set; }
-
- //Rest
-
- /// Gets or sets the max time (in milliseconds) to wait for an API request to complete.
- public int RestTimeout { get { return _restTimeout; } set { SetValue(ref _restTimeout, value); } }
- private int _restTimeout = 10000;
-
- /// Enables or disables the internal message queue. This will allow SendMessage/EditMessage to return immediately and handle messages internally.
- public bool UseMessageQueue { get { return _useMessageQueue; } set { SetValue(ref _useMessageQueue, value); } }
- private bool _useMessageQueue = true;
- /// Gets or sets the time (in milliseconds) to wait when the message queue is empty before checking again.
- public int MessageQueueInterval { get { return _messageQueueInterval; } set { SetValue(ref _messageQueueInterval, value); } }
- private int _messageQueueInterval = 100;
+ public bool LogEvents { get; set; } = true;
//WebSocket
/// Gets or sets the time (in milliseconds) to wait for the websocket to connect and initialize.
- public int ConnectionTimeout { get { return _connectionTimeout; } set { SetValue(ref _connectionTimeout, value); } }
- private int _connectionTimeout = 30000;
+ public int ConnectionTimeout { get; set; } = 30000;
/// Gets or sets the time (in milliseconds) to wait after an unexpected disconnect before reconnecting.
- public int ReconnectDelay { get { return _reconnectDelay; } set { SetValue(ref _reconnectDelay, value); } }
- private int _reconnectDelay = 1000;
+ public int ReconnectDelay { get; set; } = 1000;
/// Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying.
- public int FailedReconnectDelay { get { return _failedReconnectDelay; } set { SetValue(ref _failedReconnectDelay, value); } }
- private int _failedReconnectDelay = 15000;
-
- /// Gets or sets the time (in milliseconds) to wait when the websocket's message queue is empty before checking again.
- public int WebSocketInterval { get { return _webSocketInterval; } set { SetValue(ref _webSocketInterval, value); } }
- private int _webSocketInterval = 100;
+ public int FailedReconnectDelay { get; set; } = 15000;
//Performance
- /// Cache an encrypted login token to temp dir after success login.
- public bool CacheToken { get { return _cacheToken; } set { SetValue(ref _cacheToken, value); } }
- private bool _cacheToken = true;
+ /// Gets or sets whether an encrypted login token should be saved to temp dir after successful login.
+ public bool CacheToken { get; set; } = true;
/// Gets or sets whether Discord should send information about offline users, for servers with more than 100 users.
- public bool UseLargeThreshold { get { return _useLargeThreshold; } set { SetValue(ref _useLargeThreshold, value); } }
- private bool _useLargeThreshold = false;
+ public bool UseLargeThreshold { get; set; } = false;
/// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely.
- public int MessageCacheSize { get { return _messageCacheSize; } set { SetValue(ref _messageCacheSize, value); } }
- private int _messageCacheSize = 100;
+ public int MessageCacheSize { get; set; } = 100;
/// Gets or sets whether the permissions cache should be used. This makes operations such as User.GetPermissions(Channel), User.ServerPermissions and Channel.Members
- public bool UsePermissionsCache { get { return _usePermissionsCache; } set { SetValue(ref _usePermissionsCache, value); } }
- private bool _usePermissionsCache = true;
+ public bool UsePermissionsCache { get; set; } = true;
/// Gets or sets whether the a copy of a model is generated on an update event to allow a user to check which properties changed.
- public bool EnablePreUpdateEvents { get { return _enablePreUpdateEvents; } set { SetValue(ref _enablePreUpdateEvents, value); } }
- private bool _enablePreUpdateEvents = true;
+ public bool EnablePreUpdateEvents { get; set; } = true;
+
+ //Events
- public DiscordConfig()
+ /// Gets or sets a handler for all log messages.
+ public EventHandler LogHandler { get; set; }
+
+ public DiscordConfig Build() => new DiscordConfig(this);
+ }
+
+ public class DiscordConfig
+ {
+ public const int MaxMessageSize = 2000;
+ internal const int RestTimeout = 10000;
+ internal const int MessageQueueInterval = 100;
+ internal const int WebSocketQueueInterval = 100;
+
+ public const string LibName = "Discord.Net";
+ public static string LibVersion => typeof(DiscordConfigBuilder).GetTypeInfo().Assembly.GetName().Version.ToString(3);
+ public const string LibUrl = "https://github.com/RogueException/Discord.Net";
+
+ public const string ClientAPIUrl = "https://discordapp.com/api/";
+ public const string StatusAPIUrl = "https://srhpyqt94yxb.statuspage.io/api/v2/"; //"https://status.discordapp.com/api/v2/";
+ public const string CDNUrl = "https://cdn.discordapp.com/";
+ public const string InviteUrl = "https://discord.gg/";
+
+ public LogSeverity LogLevel { get; }
+ public bool LogEvents { get; }
+
+ public int ConnectionTimeout { get; }
+ public int ReconnectDelay { get; }
+ public int FailedReconnectDelay { get; }
+
+ public bool UseLargeThreshold { get; }
+ public int MessageCacheSize { get; }
+ public bool UsePermissionsCache { get; }
+ public bool EnablePreUpdateEvents { get; }
+
+ public string UserAgent { get; }
+ public string CacheDir { get; }
+
+ internal DiscordConfig(DiscordConfigBuilder builder)
{
- UpdateUserAgent();
+ LogLevel = builder.LogLevel;
+ LogEvents = builder.LogEvents;
+
+ ConnectionTimeout = builder.ConnectionTimeout;
+ ReconnectDelay = builder.ReconnectDelay;
+ FailedReconnectDelay = builder.FailedReconnectDelay;
+
+ UseLargeThreshold = builder.UseLargeThreshold;
+ MessageCacheSize = builder.MessageCacheSize;
+ UsePermissionsCache = builder.UsePermissionsCache;
+ EnablePreUpdateEvents = builder.EnablePreUpdateEvents;
+
+ UserAgent = GetUserAgent(builder);
+ CacheDir = GetCacheDir(builder);
}
-
- private void UpdateUserAgent()
+
+ private string GetUserAgent(DiscordConfigBuilder builder)
{
- StringBuilder builder = new StringBuilder();
- if (!string.IsNullOrEmpty(_appName))
+ StringBuilder sb = new StringBuilder();
+ if (!string.IsNullOrEmpty(builder.AppName))
{
- builder.Append(_appName);
- if (!string.IsNullOrEmpty(_appVersion))
+ sb.Append(builder.AppName);
+ if (!string.IsNullOrEmpty(builder.AppVersion))
{
- builder.Append('/');
- builder.Append(_appVersion);
+ sb.Append('/');
+ sb.Append(builder.AppVersion);
}
- if (!string.IsNullOrEmpty(_appUrl))
+ if (!string.IsNullOrEmpty(builder.AppUrl))
{
- builder.Append(" (");
- builder.Append(_appUrl);
- builder.Append(')');
+ sb.Append(" (");
+ sb.Append(builder.AppUrl);
+ sb.Append(')');
}
- builder.Append(' ');
+ sb.Append(' ');
}
- builder.Append($"DiscordBot ({LibUrl}, v{LibVersion})");
- UserAgent = builder.ToString();
+ sb.Append($"DiscordBot ({LibUrl}, v{LibVersion})");
+ return sb.ToString();
+ }
+ private string GetCacheDir(DiscordConfigBuilder builder)
+ {
+ if (builder.CacheToken)
+ return Path.Combine(Path.GetTempPath(), builder.AppName ?? "Discord.Net");
+ else
+ return null;
}
}
}
diff --git a/src/Discord.Net/Enums/LogSeverity.cs b/src/Discord.Net/Enums/LogSeverity.cs
new file mode 100644
index 000000000..c62d8c250
--- /dev/null
+++ b/src/Discord.Net/Enums/LogSeverity.cs
@@ -0,0 +1,11 @@
+namespace Discord
+{
+ public enum LogSeverity : byte
+ {
+ Error = 1,
+ Warning = 2,
+ Info = 3,
+ Verbose = 4,
+ Debug = 5
+ }
+}
diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs
index a75963935..6860a1b59 100644
--- a/src/Discord.Net/MessageQueue.cs
+++ b/src/Discord.Net/MessageQueue.cs
@@ -99,10 +99,10 @@ namespace Discord.Net
_pendingActions.Enqueue(new DeleteAction(msg));
}
- internal Task Run(CancellationToken cancelToken, int interval)
+ internal Task Run(CancellationToken cancelToken)
{
_nextWarning = WarningStart;
- return Task.Run(async () =>
+ return Task.Run((Func)(async () =>
{
try
{
@@ -121,11 +121,11 @@ namespace Discord.Net
while (_pendingActions.TryDequeue(out queuedAction))
await queuedAction.Do(this).ConfigureAwait(false);
- await Task.Delay(interval).ConfigureAwait(false);
+ await Task.Delay((int)Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false);
}
}
catch (OperationCanceledException) { }
- });
+ }));
}
internal async Task Send(Message msg)
diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs
index 447baefe0..73b11441f 100644
--- a/src/Discord.Net/Models/Channel.cs
+++ b/src/Discord.Net/Models/Channel.cs
@@ -343,26 +343,12 @@ namespace Discord
if (text == "") throw new ArgumentException("Value cannot be blank", nameof(text));
return SendMessageInternal(text, true);
}
- private async Task SendMessageInternal(string text, bool isTTS)
+ private Task SendMessageInternal(string text, bool isTTS)
{
if (text.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less.");
-
- if (Client.Config.UseMessageQueue)
- return Client.MessageQueue.QueueSend(this, text, isTTS);
- else
- {
- var request = new SendMessageRequest(Id)
- {
- Content = text,
- Nonce = null,
- IsTTS = isTTS
- };
- var model = await Client.ClientAPI.Send(request).ConfigureAwait(false);
- var msg = AddMessage(model.Id, IsPrivate ? Client.PrivateUser : Server.CurrentUser, model.Timestamp.Value);
- msg.Update(model);
- return msg;
- }
+
+ return Task.FromResult(Client.MessageQueue.QueueSend(this, text, isTTS));
}
public async Task SendFile(string filePath)
diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs
index 77cad6e2a..ed2c634e9 100644
--- a/src/Discord.Net/Models/Message.cs
+++ b/src/Discord.Net/Models/Message.cs
@@ -310,7 +310,7 @@ namespace Discord
}
}
- public async Task Edit(string text)
+ public Task Edit(string text)
{
if (text == null) throw new ArgumentNullException(nameof(text));
@@ -318,28 +318,14 @@ namespace Discord
if (text.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less.");
-
- if (Client.Config.UseMessageQueue)
- Client.MessageQueue.QueueEdit(this, text);
- else
- {
- var request = new UpdateMessageRequest(Channel.Id, Id)
- {
- Content = text
- };
- await Client.ClientAPI.Send(request).ConfigureAwait(false);
- }
+
+ Client.MessageQueue.QueueEdit(this, text);
+ return TaskHelper.CompletedTask;
}
- public async Task Delete()
+ public Task Delete()
{
- if (Client.Config.UseMessageQueue)
- Client.MessageQueue.QueueDelete(this);
- else
- {
- var request = new DeleteMessageRequest(Channel.Id, Id);
- try { await Client.ClientAPI.Send(request).ConfigureAwait(false); }
- catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
- }
+ Client.MessageQueue.QueueDelete(this);
+ return TaskHelper.CompletedTask;
}
/// Returns true if the logged-in user was mentioned.
diff --git a/src/Discord.Net/Net/Rest/SharpRestEngine.cs b/src/Discord.Net/Net/Rest/SharpRestEngine.cs
index a4ae3d392..f9c54064b 100644
--- a/src/Discord.Net/Net/Rest/SharpRestEngine.cs
+++ b/src/Discord.Net/Net/Rest/SharpRestEngine.cs
@@ -31,7 +31,7 @@ namespace Discord.Net.Rest
_client = new RestSharpClient(baseUrl)
{
PreAuthenticate = false,
- ReadWriteTimeout = _config.RestTimeout,
+ ReadWriteTimeout = DiscordConfig.RestTimeout,
UserAgent = config.UserAgent
};
_client.Proxy = null;
diff --git a/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs b/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs
index 34224720f..bda5ccb15 100644
--- a/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs
+++ b/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs
@@ -65,9 +65,7 @@ namespace Discord.Net.WebSockets
{
return Task.Run(async () =>
{
- var sendInterval = _config.WebSocketInterval;
- //var buffer = new ArraySegment(new byte[ReceiveChunkSize]);
- var buffer = new byte[ReceiveChunkSize];
+ var buffer = new ArraySegment(new byte[ReceiveChunkSize]);
var stream = new MemoryStream();
try
@@ -81,7 +79,7 @@ namespace Discord.Net.WebSockets
try
{
- result = await _webSocket.ReceiveAsync(new ArraySegment(buffer), cancelToken).ConfigureAwait(false);
+ result = await _webSocket.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false);
}
catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT)
{
@@ -91,7 +89,7 @@ namespace Discord.Net.WebSockets
if (result.MessageType == WebSocketMessageType.Close)
throw new WebSocketException((int)result.CloseStatus.Value, result.CloseStatusDescription);
else
- stream.Write(buffer, 0, result.Count);
+ stream.Write(buffer.Array, buffer.Offset, buffer.Count);
}
while (result == null || !result.EndOfMessage);
@@ -114,7 +112,6 @@ namespace Discord.Net.WebSockets
return Task.Run(async () =>
{
byte[] bytes = new byte[SendChunkSize];
- var sendInterval = _config.WebSocketInterval;
try
{
@@ -147,7 +144,7 @@ namespace Discord.Net.WebSockets
}
}
}
- await Task.Delay(sendInterval, cancelToken).ConfigureAwait(false);
+ await Task.Delay(DiscordConfig.WebSocketQueueInterval, cancelToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException) { }
diff --git a/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs b/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs
index 98eb7db02..420299d6b 100644
--- a/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs
+++ b/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs
@@ -118,7 +118,6 @@ namespace Discord.Net.WebSockets
private Task SendAsync(CancellationToken cancelToken)
{
- var sendInterval = _config.WebSocketInterval;
return Task.Run(async () =>
{
try
@@ -128,7 +127,7 @@ namespace Discord.Net.WebSockets
string json;
while (_sendQueue.TryDequeue(out json))
_webSocket.Send(json);
- await Task.Delay(sendInterval, cancelToken).ConfigureAwait(false);
+ await Task.Delay(DiscordConfig.WebSocketQueueInterval, cancelToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException) { }