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 } }