diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
index c05c96867..e6f99d7c7 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
@@ -1,40 +1,44 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
namespace DisCatSharp.CommandsNext
{
///
/// Represents arguments for event.
///
public class CommandErrorEventArgs : CommandEventArgs
{
///
/// Gets the exception.
///
public Exception Exception { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public CommandErrorEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
index 528678582..13b1150a5 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
@@ -1,48 +1,52 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using DisCatSharp.Common.Utilities;
using DisCatSharp.EventArgs;
namespace DisCatSharp.CommandsNext
{
///
/// Base class for all CNext-related events.
///
public class CommandEventArgs : DiscordEventArgs
{
///
/// Gets the context in which the command was executed.
///
public CommandContext Context { get; internal set; }
///
/// Gets the command that was executed.
///
public Command Command
=> this.Context.Command;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public CommandEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
index ebd2246a3..bd6ed3455 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
@@ -1,35 +1,39 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
namespace DisCatSharp.CommandsNext
{
///
/// Represents arguments for event.
///
public class CommandExecutionEventArgs : CommandEventArgs
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public CommandExecutionEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs b/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs
index 25642d608..472c007d3 100644
--- a/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs
+++ b/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs
@@ -1,26 +1,29 @@
-// This file is used by Code Analysis to maintain SuppressMessage
+// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "")]
[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "")]
[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._discordConfig")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._discordConfig")]
[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._interactivityConfig")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._interactivityConfig")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._lavalinkConfig")]
[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._lavalinkConfig")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.Bot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.DiscordHostedService},System.IServiceProvider)")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostExtensionTests.DefaultDiscord~System.Collections.Generic.Dictionary{System.String,System.String}")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostExtensionTests.DiscordInteractivityAndLavaLinkConfiguration~Microsoft.Extensions.Configuration.IConfiguration")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.Create(System.Collections.Generic.Dictionary{System.String,System.String})~Microsoft.Extensions.Hosting.IHostBuilder")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.DefaultDiscord~System.Collections.Generic.Dictionary{System.String,System.String}")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.Bot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.Bot},System.IServiceProvider)")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.BotTwoService.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.BotTwoService},System.IServiceProvider)")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.MyCustomBot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.MyCustomBot},System.IServiceProvider)")]
[assembly: SuppressMessage("DocumentationHeader", "InterfaceDocumentationHeader:The interface must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Hosting.Tests.IBotTwoService")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.Create(System.String)~Microsoft.Extensions.Hosting.IHostBuilder")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.Create``2(System.String)~Microsoft.Extensions.Hosting.IHostBuilder")]
+[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.Bot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.Bot},System.IServiceProvider,Microsoft.Extensions.Hosting.IHostApplicationLifetime)")]
+[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.BotTwoService.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.BotTwoService},System.IServiceProvider,Microsoft.Extensions.Hosting.IHostApplicationLifetime)")]
+[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.MyCustomBot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.MyCustomBot},System.IServiceProvider,Microsoft.Extensions.Hosting.IHostApplicationLifetime)")]
diff --git a/DisCatSharp.Hosting/DiscordHostedService.cs b/DisCatSharp.Hosting/DiscordHostedService.cs
index 7d1758b3b..5bc438ff9 100644
--- a/DisCatSharp.Hosting/DiscordHostedService.cs
+++ b/DisCatSharp.Hosting/DiscordHostedService.cs
@@ -1,206 +1,202 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace DisCatSharp.Hosting
{
///
/// Simple implementation for to work as a
///
public abstract class DiscordHostedService : BackgroundService, IDiscordHostedService
{
///
public DiscordClient Client { get; private set; }
protected readonly ILogger Logger;
protected readonly IHostApplicationLifetime ApplicationLifetime;
protected readonly IConfiguration Configuration;
protected readonly IServiceProvider ServiceProvider;
private readonly string _botSection;
#pragma warning disable 8618
///
/// Initializes a new instance of the class.
///
/// The config.
/// The logger.
/// The provider.
/// Current hosting environment. This will be used for shutting down the application on error
/// Name within the configuration which contains the config info for our bot. Default is DisCatSharp
protected DiscordHostedService(IConfiguration config, ILogger logger, IServiceProvider provider, IHostApplicationLifetime applicationLifetime, string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DefaultRootLib)
{
this.Logger = logger;
this.ApplicationLifetime = applicationLifetime;
this.Configuration = config;
this._botSection = configBotSection;
this.ServiceProvider = provider;
this.Initialize();
}
-
#pragma warning restore 8618
///
/// When the bot fails to start, this method will be invoked. (Default behavior is to shutdown)
///
/// The exception/reason the bot couldn't start
- protected virtual void OnInitializationError(Exception ex)
- {
- this.ApplicationLifetime.StopApplication();
- }
+ protected virtual void OnInitializationError(Exception ex) => this.ApplicationLifetime.StopApplication();
///
/// Dynamically loads extensions by using , and
///
///
protected virtual void InitializeExtensions()
{
var typeMap = this.Configuration.FindImplementedExtensions(this._botSection);
this.Logger.LogDebug($"Found the following config types: {string.Join("\n\t", typeMap.Keys)}");
foreach (var typePair in typeMap)
try
{
/*
If section is null --> utilize the default constructor
This means the extension was explicitly added in the 'Using' array,
but user did not wish to override any value(s) in the extension's config
*/
var configInstance = typePair.Value.Section.HasValue
? typePair.Value.Section.Value.ExtractConfig(() =>
ActivatorUtilities.CreateInstance(this.ServiceProvider, typePair.Value.ConfigType))
: ActivatorUtilities.CreateInstance(this.ServiceProvider, typePair.Value.ConfigType);
/*
Explanation for bindings
Internal Constructors --> NonPublic
Public Constructors --> Public
Constructors --> Instance
*/
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
var ctors = typePair.Value.ImplementationType.GetConstructors(flags);
var instance = ctors.Any(x => x.GetParameters().Length == 1 && x.GetParameters().First().ParameterType == typePair.Value.ConfigType)
? Activator.CreateInstance(typePair.Value.ImplementationType, flags, null,
new[] { configInstance }, null)
: Activator.CreateInstance(typePair.Value.ImplementationType, true);
/*
Certain extensions do not require a configuration argument
Those who do -- pass config instance in,
Those who don't -- simply instantiate
ActivatorUtilities requires a public constructor, anything with internal breaks
*/
if (instance == null)
{
this.Logger.LogError($"Unable to instantiate '{typePair.Value.ImplementationType.Name}'");
continue;
}
// Add an easy reference to our extensions for later use
this.Client.AddExtension((BaseExtension)instance);
}
catch (Exception ex)
{
this.Logger.LogError($"Unable to register '{typePair.Value.ImplementationType.Name}': \n\t{ex.Message}");
this.OnInitializationError(ex);
}
}
///
/// Automatically search for and configure
///
private void Initialize()
{
try
{
this.Client = this.Configuration.BuildClient(this._botSection);
this.Client.ServiceProvider = this.ServiceProvider;
}
catch (Exception ex)
{
this.Logger.LogError($"Was unable to build {nameof(DiscordClient)} for {this.GetType().Name}");
this.OnInitializationError(ex);
}
}
///
/// Executes the bot.
///
/// The stopping token.
/// A Task.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
if (this.Client == null)
throw new NullReferenceException("Discord Client cannot be null");
await this.PreConnect();
await this.Client.ConnectAsync();
this.InitializeExtensions();
await this.PostConnect();
}
catch (Exception ex)
{
/*
* Anything before DOTNET 6 will
* fail silently despite throwing an exception in this method
* So to overcome this obstacle we need to log what happened and manually exit
*/
this.Logger.LogError($"Was unable to start {this.GetType().Name} Bot as a hosted service.");
this.OnInitializationError(ex);
}
// Wait indefinitely -- but use stopping token so we can properly cancel if needed
await Task.Delay(-1, stoppingToken);
}
///
/// Runs just prior to the bot connecting
///
protected virtual Task PreConnect() => Task.CompletedTask;
///
/// Runs immediately after the bot connects
///
protected virtual Task PostConnect() => Task.CompletedTask;
}
}
diff --git a/DisCatSharp/Enums/Permission.cs b/DisCatSharp/Enums/Permission.cs
index 2f08a6f99..97c4a90d8 100644
--- a/DisCatSharp/Enums/Permission.cs
+++ b/DisCatSharp/Enums/Permission.cs
@@ -1,363 +1,363 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
namespace DisCatSharp
{
///
/// Represents permission methods.
///
public static class PermissionMethods
{
///
/// Gets the full permissions enum (long).
///
internal static Permissions FULL_PERMS { get; } = (Permissions)1099511627775L; // 2199023255551L
///
/// Calculates whether this permission set contains the given permission.
///
/// The permissions to calculate from
/// permission you want to check
///
public static bool HasPermission(this Permissions p, Permissions permission)
=> p.HasFlag(Permissions.Administrator) || (p & permission) == permission;
///
/// Grants permissions.
///
/// The permissions to add to.
/// Permission to add.
///
public static Permissions Grant(this Permissions p, Permissions grant) => p | grant;
///
/// Revokes permissions.
///
/// The permissions to take from.
/// Permission to take.
///
public static Permissions Revoke(this Permissions p, Permissions revoke) => p & ~revoke;
}
///
/// Whether a permission is allowed, denied or unset
///
public enum PermissionLevel
{
///
/// Said permission is Allowed
///
Allowed,
///
/// Said permission is Denied
///
Denied,
///
/// Said permission is Unset
///
Unset
}
///
/// Bitwise permission flags.
///
[Flags]
public enum Permissions : long
{
///
/// Indicates no permissions given.
///
[PermissionString("No permissions")]
None = 0x0000000000000000,
///
/// Indicates all permissions are granted
///
[PermissionString("All permissions")]
All = 1099511627775, // 2199023255551
///
/// Allows creation of instant channel invites.
///
[PermissionString("Create instant invites")]
CreateInstantInvite = 0x0000000000000001,
///
/// Allows kicking members.
///
[PermissionString("Kick members")]
KickMembers = 0x0000000000000002,
///
/// Allows banning and unbanning members.
///
[PermissionString("Ban members")]
BanMembers = 0x0000000000000004,
///
/// Enables full access on a given guild. This also overrides other permissions.
///
[PermissionString("Administrator")]
Administrator = 0x0000000000000008,
///
/// Allows managing channels.
///
[PermissionString("Manage channels")]
ManageChannels = 0x0000000000000010,
///
/// Allows managing the guild.
///
[PermissionString("Manage guild")]
ManageGuild = 0x0000000000000020,
///
/// Allows adding reactions to messages.
///
[PermissionString("Add reactions")]
AddReactions = 0x0000000000000040,
///
/// Allows viewing audit log entries.
///
[PermissionString("View audit log")]
ViewAuditLog = 0x0000000000000080,
///
/// Allows the use of priority speaker.
///
[PermissionString("Use priority speaker")]
PrioritySpeaker = 0x0000000000000100,
///
/// Allows accessing text and voice channels. Disabling this permission hides channels.
///
[PermissionString("Read messages")]
AccessChannels = 0x0000000000000400,
///
/// Allows sending messages (does not allow sending messages in threads).
///
[PermissionString("Send messages")]
SendMessages = 0x0000000000000800,
///
/// Allows sending text-to-speech messages.
///
[PermissionString("Send TTS messages")]
SendTtsMessages = 0x0000000000001000,
///
/// Allows managing messages of other users.
///
[PermissionString("Manage messages")]
ManageMessages = 0x0000000000002000,
///
/// Allows embedding content in messages.
///
[PermissionString("Use embeds")]
EmbedLinks = 0x0000000000004000,
///
/// Allows uploading files.
///
[PermissionString("Attach files")]
AttachFiles = 0x0000000000008000,
///
/// Allows reading message history.
///
[PermissionString("Read message history")]
ReadMessageHistory = 0x0000000000010000,
///
/// Allows using @everyone and @here mentions.
///
[PermissionString("Mention everyone")]
MentionEveryone = 0x0000000000020000,
///
/// Allows using emojis from external servers, such as twitch or nitro emojis.
///
[PermissionString("Use external emojis")]
UseExternalEmojis = 0x0000000000040000,
///
/// Allows connecting to voice chat.
///
[PermissionString("Use voice chat")]
UseVoice = 0x0000000000100000,
///
/// Allows speaking in voice chat.
///
[PermissionString("Speak")]
Speak = 0x0000000000200000,
///
/// Allows muting other members in voice chat.
///
[PermissionString("Mute voice chat members")]
MuteMembers = 0x0000000000400000,
///
/// Allows deafening other members in voice chat.
///
[PermissionString("Deafen voice chat members")]
DeafenMembers = 0x0000000000800000,
///
/// Allows moving voice chat members.
///
[PermissionString("Move voice chat members")]
MoveMembers = 0x0000000001000000,
///
/// Allows using voice activation in voice chat. Revoking this will usage of push-to-talk.
///
[PermissionString("Use voice activity detection")]
UseVoiceDetection = 0x0000000002000000,
///
/// Allows changing of own nickname.
///
[PermissionString("Change own nickname")]
ChangeNickname = 0x0000000004000000,
///
/// Allows managing nicknames of other members.
///
[PermissionString("Manage nicknames")]
ManageNicknames = 0x0000000008000000,
///
/// Allows managing roles in a guild.
///
[PermissionString("Manage roles")]
ManageRoles = 0x0000000010000000,
///
/// Allows managing webhooks in a guild.
///
[PermissionString("Manage webhooks")]
ManageWebhooks = 0x0000000020000000,
///
/// Allows managing guild emojis and stickers.
///
[PermissionString("Manage emojis & stickers")]
ManageEmojisAndStickers = 0x0000000040000000,
///
/// Allows the user to go live.
///
[PermissionString("Allow stream")]
Stream = 0x0000000000000200,
///
/// Allows the user to use slash commands.
///
[PermissionString("Use application commands")]
UseApplicationCommands = 0x0000000080000000,
///
/// Allows for requesting to speak in stage channels.
///
[PermissionString("Request to speak")]
RequestToSpeak = 0x0000000100000000,
///
/// Allows managing guild events.
///
[PermissionString("Manage Events")]
ManageEvents = 0x0000000200000000,
///
/// Allows for deleting and archiving threads, and viewing all private threads.
///
[PermissionString("Manage Threads")]
ManageThreads = 0x0000000400000000,
///
/// Allows for creating threads.
///
[PermissionString("Create Public Threads")]
CreatePublicThreads = 0x0000000800000000,
///
/// Allows for creating private threads.
///
[PermissionString("Create Private Threads")]
CreatePrivateThreads = 0x0000001000000000,
///
/// Allows the usage of custom stickers from other servers.
///
[PermissionString("Use external Stickers")]
UseExternalStickers = 0x0000002000000000,
///
/// Allows for sending messages in threads.
///
[PermissionString("Send messages in Threads")]
SendMessagesInThreads = 0x0000004000000000,
///
/// Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel.
///
[PermissionString("Start Embedded Activities")]
StartEmbeddedActivities = 0x0000008000000000//,
- ///
+ /*///
/// Allows to time-out a member.
///
- //[PermissionString("Time-out Members")]
- //TimeOutMembers = 0x0000010000000000
+ [PermissionString("Time-out Members")]
+ TimeOutMembers = 0x0000010000000000*/
}
///
/// Defines a readable name for this permission.
///
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class PermissionStringAttribute : Attribute
{
///
/// Gets the readable name for this permission.
///
public string String { get; }
///
/// Defines a readable name for this permission.
///
/// Readable name for this permission.
public PermissionStringAttribute(string str)
{
this.String = str;
}
}
}
diff --git a/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs b/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
index 1f4de0005..2cd209b9a 100644
--- a/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
@@ -1,46 +1,50 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using DisCatSharp.Entities;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for application command events.
///
public sealed class ApplicationCommandEventArgs : DiscordEventArgs
{
///
/// Gets the command that was modified.
///
public DiscordApplicationCommand Command { get; internal set; }
///
/// Gets the optional guild of the command.
///
public DiscordGuild Guild { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ApplicationCommandEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs b/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
index 53eaa4481..58643e17b 100644
--- a/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
@@ -1,57 +1,61 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using DisCatSharp.Entities;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for application command permissions update events.
///
public sealed class ApplicationCommandPermissionsUpdateEventArgs : DiscordEventArgs
{
///
/// Gets the application command permissions.
///
public List Permissions { get; internal set; }
///
/// Gets the application command.
///
public DiscordApplicationCommand Command { get; internal set; }
///
/// Gets the application id.
///
public ulong ApplicationId { get; internal set; }
///
/// Gets the guild.
///
public DiscordGuild Guild { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ApplicationCommandPermissionsUpdateEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs b/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
index 6f5212e69..753f9a143 100644
--- a/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
@@ -1,56 +1,60 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using DisCatSharp.Entities;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for application command events.
///
public sealed class GuildApplicationCommandCountEventArgs : DiscordEventArgs
{
///
/// Gets the count of slash commands.
///
public int SlashCommands { get; internal set; }
///
/// Gets the count of user context menu commands.
///
public int UserContextMenuCommands { get; internal set; }
///
/// Gets the count of message context menu commands.
///
public int MessageContextMenuCommands { get; internal set; }
///
/// Gets the guild.
///
public DiscordGuild Guild { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public GuildApplicationCommandCountEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs b/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
index 5738be1e7..c50242a4b 100644
--- a/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
@@ -1,57 +1,61 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
namespace DisCatSharp.EventArgs
{
///
/// The context menu interaction create event args.
///
public sealed class ContextMenuInteractionCreateEventArgs : InteractionCreateEventArgs
{
///
/// The type of context menu that was used. This is never .
///
public ApplicationCommandType Type { get; internal set; }
///
/// The user that invoked this interaction. Can be casted to a member if this was on a guild.
///
public DiscordUser User => this.Interaction.User;
///
/// The user this interaction targets, if applicable.
///
public DiscordUser TargetUser { get; internal set; }
///
/// The message this interaction targets, if applicable.
///
public DiscordMessage TargetMessage { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ContextMenuInteractionCreateEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs b/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
index 56526c674..40638d5c7 100644
--- a/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
@@ -1,41 +1,45 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using DisCatSharp.Entities;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for
///
public class InteractionCreateEventArgs : DiscordEventArgs
{
///
/// Gets the interaction data that was invoked.
///
public DiscordInteraction Interaction { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public InteractionCreateEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/Net/WebSocket/WebSocketClient.cs b/DisCatSharp/Net/WebSocket/WebSocketClient.cs
index 54916243e..cc679399a 100644
--- a/DisCatSharp/Net/WebSocket/WebSocketClient.cs
+++ b/DisCatSharp/Net/WebSocket/WebSocketClient.cs
@@ -1,413 +1,416 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.EventArgs;
using DisCatSharp.Common.Utilities;
using Microsoft.Extensions.DependencyInjection;
namespace DisCatSharp.Net.WebSocket
{
// weebsocket
// not even sure whether emzi or I posted this. much love, naam.
///
/// The default, native-based WebSocket client implementation.
///
public class WebSocketClient : IWebSocketClient
{
///
/// The outgoing chunk size.
///
private const int OutgoingChunkSize = 8192; // 8 KiB
///
/// The incoming chunk size.
///
private const int IncomingChunkSize = 32768; // 32 KiB
///
/// Gets the proxy settings for this client.
///
public IWebProxy Proxy { get; }
///
/// Gets the collection of default headers to send when connecting to the remote endpoint.
///
public IReadOnlyDictionary DefaultHeaders { get; }
+ ///
+ /// Gets or sets the service provider.
+ ///
IServiceProvider IWebSocketClient.ServiceProvider
{
get => this._serviceProvider;
set => this._serviceProvider = value;
}
private readonly Dictionary _defaultHeaders;
private Task _receiverTask;
private CancellationTokenSource _receiverTokenSource;
private CancellationToken _receiverToken;
private readonly SemaphoreSlim _senderLock;
private CancellationTokenSource _socketTokenSource;
private CancellationToken _socketToken;
private ClientWebSocket _ws;
private volatile bool _isClientClose = false;
private volatile bool _isConnected = false;
private bool _isDisposed = false;
///
/// Instantiates a new WebSocket client with specified proxy settings.
///
/// Proxy settings for the client.
/// Service provider.
private WebSocketClient(IWebProxy proxy, IServiceProvider provider)
{
this._connected = new AsyncEvent("WS_CONNECT", TimeSpan.Zero, this.EventErrorHandler);
this._disconnected = new AsyncEvent("WS_DISCONNECT", TimeSpan.Zero, this.EventErrorHandler);
this._messageReceived = new AsyncEvent("WS_MESSAGE", TimeSpan.Zero, this.EventErrorHandler);
this._exceptionThrown = new AsyncEvent("WS_ERROR", TimeSpan.Zero, null);
this.Proxy = proxy;
this._defaultHeaders = new Dictionary();
this.DefaultHeaders = new ReadOnlyDictionary(this._defaultHeaders);
this._receiverTokenSource = null;
this._receiverToken = CancellationToken.None;
this._senderLock = new SemaphoreSlim(1);
this._socketTokenSource = null;
this._socketToken = CancellationToken.None;
this._serviceProvider = provider;
}
///
/// Connects to a specified remote WebSocket endpoint.
///
/// The URI of the WebSocket endpoint.
public async Task ConnectAsync(Uri uri)
{
// Disconnect first
try { await this.DisconnectAsync().ConfigureAwait(false); } catch { }
// Disallow sending messages
await this._senderLock.WaitAsync().ConfigureAwait(false);
try
{
// This can be null at this point
this._receiverTokenSource?.Dispose();
this._socketTokenSource?.Dispose();
this._ws?.Dispose();
this._ws = new ClientWebSocket();
this._ws.Options.Proxy = this.Proxy;
this._ws.Options.KeepAliveInterval = TimeSpan.Zero;
if (this._defaultHeaders != null)
foreach (var (k, v) in this._defaultHeaders)
this._ws.Options.SetRequestHeader(k, v);
this._receiverTokenSource = new CancellationTokenSource();
this._receiverToken = this._receiverTokenSource.Token;
this._socketTokenSource = new CancellationTokenSource();
this._socketToken = this._socketTokenSource.Token;
this._isClientClose = false;
this._isDisposed = false;
await this._ws.ConnectAsync(uri, this._socketToken).ConfigureAwait(false);
this._receiverTask = Task.Run(this.ReceiverLoopAsync, this._receiverToken);
}
finally
{
this._senderLock.Release();
}
}
///
/// Disconnects the WebSocket connection.
///
/// The code
/// The message
/// Lala Sabathil,06.07.2021
/// Lala Sabathil,06.07.2021
public async Task DisconnectAsync(int code = 1000, string message = "")
{
// Ensure that messages cannot be sent
await this._senderLock.WaitAsync().ConfigureAwait(false);
try
{
this._isClientClose = true;
if (this._ws != null && (this._ws.State == WebSocketState.Open || this._ws.State == WebSocketState.CloseReceived))
await this._ws.CloseOutputAsync((WebSocketCloseStatus)code, message, CancellationToken.None).ConfigureAwait(false);
if (this._receiverTask != null)
await this._receiverTask.ConfigureAwait(false); // Ensure that receiving completed
if (this._isConnected)
this._isConnected = false;
if (!this._isDisposed)
{
// Cancel all running tasks
if (this._socketToken.CanBeCanceled)
this._socketTokenSource?.Cancel();
this._socketTokenSource?.Dispose();
if (this._receiverToken.CanBeCanceled)
this._receiverTokenSource?.Cancel();
this._receiverTokenSource?.Dispose();
this._isDisposed = true;
}
}
catch { }
finally
{
this._senderLock.Release();
}
}
///
/// Send a message to the WebSocket server.
///
/// The message to send.
public async Task SendMessageAsync(string message)
{
if (this._ws == null)
return;
if (this._ws.State != WebSocketState.Open && this._ws.State != WebSocketState.CloseReceived)
return;
var bytes = Utilities.UTF8.GetBytes(message);
await this._senderLock.WaitAsync().ConfigureAwait(false);
try
{
var len = bytes.Length;
var segCount = len / OutgoingChunkSize;
if (len % OutgoingChunkSize != 0)
segCount++;
for (var i = 0; i < segCount; i++)
{
var segStart = OutgoingChunkSize * i;
var segLen = Math.Min(OutgoingChunkSize, len - segStart);
await this._ws.SendAsync(new ArraySegment(bytes, segStart, segLen), WebSocketMessageType.Text, i == segCount - 1, CancellationToken.None).ConfigureAwait(false);
}
}
finally
{
this._senderLock.Release();
}
}
///
/// Adds a header to the default header collection.
///
/// Name of the header to add.
/// Value of the header to add.
/// Whether the operation succeeded.
public bool AddDefaultHeader(string name, string value)
{
this._defaultHeaders[name] = value;
return true;
}
///
/// Removes a header from the default header collection.
///
/// Name of the header to remove.
/// Whether the operation succeeded.
public bool RemoveDefaultHeader(string name)
=> this._defaultHeaders.Remove(name);
///
/// Disposes of resources used by this WebSocket client instance.
///
public void Dispose()
{
if (this._isDisposed)
return;
this._isDisposed = true;
this.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
this._receiverTokenSource?.Dispose();
this._socketTokenSource?.Dispose();
}
///
/// Receivers the loop.
///
internal async Task ReceiverLoopAsync()
{
await Task.Yield();
var token = this._receiverToken;
var buffer = new ArraySegment(new byte[IncomingChunkSize]);
try
{
using var bs = new MemoryStream();
while (!token.IsCancellationRequested)
{
// See https://github.com/RogueException/Discord.Net/commit/ac389f5f6823e3a720aedd81b7805adbdd78b66d
// for explanation on the cancellation token
WebSocketReceiveResult result;
byte[] resultBytes;
do
{
result = await this._ws.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false);
if (result.MessageType == WebSocketMessageType.Close)
break;
bs.Write(buffer.Array, 0, result.Count);
}
while (!result.EndOfMessage);
resultBytes = new byte[bs.Length];
bs.Position = 0;
bs.Read(resultBytes, 0, resultBytes.Length);
bs.Position = 0;
bs.SetLength(0);
if (!this._isConnected && result.MessageType != WebSocketMessageType.Close)
{
this._isConnected = true;
await this._connected.InvokeAsync(this, new SocketEventArgs(this._serviceProvider)).ConfigureAwait(false);
}
if (result.MessageType == WebSocketMessageType.Binary)
{
await this._messageReceived.InvokeAsync(this, new SocketBinaryMessageEventArgs(resultBytes)).ConfigureAwait(false);
}
else if (result.MessageType == WebSocketMessageType.Text)
{
await this._messageReceived.InvokeAsync(this, new SocketTextMessageEventArgs(Utilities.UTF8.GetString(resultBytes))).ConfigureAwait(false);
}
else // close
{
if (!this._isClientClose)
{
var code = result.CloseStatus.Value;
code = code == WebSocketCloseStatus.NormalClosure || code == WebSocketCloseStatus.EndpointUnavailable
? (WebSocketCloseStatus)4000
: code;
await this._ws.CloseOutputAsync(code, result.CloseStatusDescription, CancellationToken.None).ConfigureAwait(false);
}
await this._disconnected.InvokeAsync(this, new SocketCloseEventArgs(this._serviceProvider) { CloseCode = (int)result.CloseStatus, CloseMessage = result.CloseStatusDescription }).ConfigureAwait(false);
break;
}
}
}
catch (Exception ex)
{
await this._exceptionThrown.InvokeAsync(this, new SocketErrorEventArgs(this._serviceProvider) { Exception = ex }).ConfigureAwait(false);
await this._disconnected.InvokeAsync(this, new SocketCloseEventArgs(this._serviceProvider) { CloseCode = -1, CloseMessage = "" }).ConfigureAwait(false);
}
// Don't await or you deadlock
// DisconnectAsync waits for this method
_ = this.DisconnectAsync().ConfigureAwait(false);
}
///
/// Creates a new instance of .
///
/// Proxy to use for this client instance.
/// Service provider.
/// An instance of .
public static IWebSocketClient CreateNew(IWebProxy proxy, IServiceProvider provider)
=> new WebSocketClient(proxy, provider);
#region Events
///
/// Triggered when the client connects successfully.
///
public event AsyncEventHandler Connected
{
add => this._connected.Register(value);
remove => this._connected.Unregister(value);
}
private readonly AsyncEvent _connected;
///
/// Triggered when the client is disconnected.
///
public event AsyncEventHandler Disconnected
{
add => this._disconnected.Register(value);
remove => this._disconnected.Unregister(value);
}
private readonly AsyncEvent _disconnected;
///
/// Triggered when the client receives a message from the remote party.
///
public event AsyncEventHandler MessageReceived
{
add => this._messageReceived.Register(value);
remove => this._messageReceived.Unregister(value);
}
private readonly AsyncEvent _messageReceived;
///
/// Triggered when an error occurs in the client.
///
public event AsyncEventHandler ExceptionThrown
{
add => this._exceptionThrown.Register(value);
remove => this._exceptionThrown.Unregister(value);
}
private readonly AsyncEvent _exceptionThrown;
private IServiceProvider _serviceProvider;
///
/// Events the error handler.
///
/// The event.
/// The exeption.
/// The handler.
/// The sender.
/// The event args.
private void EventErrorHandler(AsyncEvent asyncEvent, Exception ex, AsyncEventHandler handler, WebSocketClient sender, TArgs eventArgs)
where TArgs : AsyncEventArgs
=> this._exceptionThrown.InvokeAsync(this, new SocketErrorEventArgs(this._serviceProvider) { Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
#endregion
}
}